""" Test serving counter images. """

import os
import json
import re
import shutil
import urllib.request

import pytest

from vasl_templates.webapp.vassal import SUPPORTED_VASSAL_VERSIONS
from vasl_templates.webapp.vasl_mod import get_vo_gpids, SUPPORTED_VASL_MOD_VERSIONS
from vasl_templates.webapp.utils import compare_version_strings
from vasl_templates.webapp.tests import pytest_options
from vasl_templates.webapp.tests.utils import init_webapp, select_tab, find_child, find_children
from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario

# ---------------------------------------------------------------------

@pytest.mark.skipif( pytest_options.short_tests, reason="--short-tests specified" )
def test_counter_images( webapp, webdriver ): #pylint: disable=too-many-locals
    """Test that counter images are served correctly."""

    # NOTE: This is ridiculously slow on Windows :-/

    def check_images( vasl_version, gpids, check_front, check_back ): #pylint: disable=unused-argument
        """Check getting the front and back images for each counter."""
        is_pre_vasl664 = vasl_version and compare_version_strings( vasl_version, "6.6.4" ) < 0
        for gpid in gpids:

            # FUDGE! These Swedish ordnance were only added in VASL 6.6.4.
            if is_pre_vasl664 and gpid in [ "13832", "13835", "13836" ]:
                continue

            for side in ("front","back"):
                url = webapp.url_for( "get_counter_image", gpid=gpid, side=side )
                try:
                    with urllib.request.urlopen( url ) as resp:
                        resp_code = resp.code
                        resp_data = resp.read()
                except urllib.error.HTTPError as ex:
                    resp_code = ex.code
                    resp_data = None
                assert locals()["check_"+side]( resp_code, resp_data )

    # test counter images when no VASL module has been configured
    webapp.control_tests.set_vasl_version( None, None )
    init_webapp( webapp, webdriver )
    # NOTE: It doesn't really matter which set of GPID's we use, since we're expecting
    # a missing image for everything anyway. We just use the most recent supported version.
    gpids = get_vo_gpids( None )
    fname = os.path.join( os.path.split(__file__)[0], "../static/images/missing-image.png" )
    with open( fname, "rb" ) as fp:
        missing_image_data = fp.read()
    check_images( None, gpids,
        check_front = lambda code, data: code == 200 and data == missing_image_data,
        check_back = lambda code, data: code == 200 and data == missing_image_data
    )

    def _do_check_front( code, data ):
        return code == 200 and data
    def _do_check_back( code, data ):
        return (code == 200 and data) or (code == 404 and not data)

    # initialize
    check_dir = os.path.join( os.path.split(__file__)[0], "fixtures" )
    save_dir = os.environ.get( "COUNTERS_SAVEDIR" ) # nb: define this to save the generated reports
    if save_dir:
        if os.path.isdir( save_dir ):
            shutil.rmtree( save_dir )
        os.makedirs( save_dir )

    # load the VASL version aliases
    fname = os.path.join( os.path.dirname( __file__ ), "../data/vasl-version-aliases.json" )
    with open( fname, "r", encoding="utf-8" ) as fp:
        aliases = json.load( fp )

    # test each VASL version
    failed = False
    vasl_versions = webapp.control_tests.get_vasl_versions()
    for vasl_version in vasl_versions:

        # initialize
        webapp.control_tests \
            .set_data_dir( "{REAL}" ) \
            .set_vasl_version( vasl_version, None )
        init_webapp( webapp, webdriver )

        # figure out what we're expecting to see
        fname = os.path.join( check_dir, "vasl-pieces-{}.txt".format(
            aliases.get( vasl_version, vasl_version )
        ) )
        with open( fname, "r", encoding="utf-8" ) as fp:
            expected_vasl_pieces = fp.read()

        # generate a report for the pieces loaded
        report, gpids = webapp.control_tests.get_vasl_pieces( vasl_version )
        if save_dir:
            fname2 = os.path.join( save_dir, vasl_version+".txt" )
            with open( fname2, "w", encoding="utf-8" ) as fp:
                fp.write( report )

        # check the report
        if report != expected_vasl_pieces:
            if save_dir:
                print( "FAILED:", vasl_version )
                failed = True
            else:
                assert False, "Report mismatch: {}".format( vasl_version )

        # check each counter
        check_images( vasl_version, gpids, check_front=_do_check_front, check_back=_do_check_back )

    assert not failed

# ---------------------------------------------------------------------

# NOTE: We disabled this test since we no longer support older versions of VASSAL+VASL, and the later versions
# don't require GPID remapping, but we leave the code here in case we need it again in the future.

def _DISABLED_test_gpid_remapping( webapp, webdriver ):
    """Test GPID remapping."""

    # initialize
    init_webapp( webapp, webdriver )

    def check_gpid_image( gpid ):
        """Check if we can get the image for the specified GPID."""
        url = webapp.url_for( "get_counter_image", gpid=gpid, side="front" )
        try:
            with urllib.request.urlopen( url ) as resp:
                return resp.code
        except urllib.error.HTTPError as ex:
            assert ex.code != 200
            return ex.code

    def check_entry( entry, url_stem, valid_image ):
        """Check a vehicle entry in the UI."""
        img = find_child( "img.vasl-image", entry )
        assert img.get_attribute( "src" ).endswith( url_stem )
        mo = re.search( r"^/counter/(\d+)/", url_stem )
        gpid = mo.group(1)
        if valid_image:
            assert check_gpid_image( gpid ) == 200
            assert img.get_attribute( "width" ) == "47" # nb: this value depends on the CSS settings!
        else:
            assert check_gpid_image( gpid ) == 404

    def do_test( vasl_version, valid_images ):
        """Do the test."""
        # initialize (using the specified version of VASL)
        webapp.control_tests \
            .set_data_dir( "{REAL}" ) \
            .set_vasl_version( vasl_version, None )
        init_webapp( webapp, webdriver, scenario_persistence=1 )
        load_scenario( scenario_data )
        # check that the German vehicles loaded correctly
        select_tab( "ob1" )
        vehicles_sortable = find_child( "#ob_vehicles-sortable_1" )
        entries = find_children( "li", vehicles_sortable )
        assert len(entries) == 2
        check_entry( entries[0], "/counter/2542/front", True )
        check_entry( entries[1], "/counter/7124/front/0", valid_images )
        # check that the American ordnance loaded correctly
        select_tab( "ob2" )
        vehicles_sortable = find_child( "#ob_ordnance-sortable_2" )
        entries = find_children( "li", vehicles_sortable )
        assert len(entries) == 1
        check_entry( entries[0], "/counter/879/front", valid_images )

    # load the test scenario
    fname = os.path.join( os.path.split(__file__)[0], "fixtures/gpid-remapping.json" )
    with open( fname, "r", encoding="utf-8" ) as fp:
        scenario_data = json.load( fp )

    # run the tests using VASL 6.4.4 and 6.5.0
    # NOTE: Versions of VASL prior to 6.6.0 are no longer officially supported (since they use Java 8),
    # but we would still like to run these tests. See VassalShim._run_vassal_shim(), where we figure out
    # which version of Java to use, and run_vassal_tests() in test_vassal.py, where we check for invalid
    # combinations of VASSAL and VASL. Sigh...
    do_test( "6.4.4", True )
    do_test( "6.5.0", True )
    do_test( "6.5.1", True )

    # disable GPID remapping and try again
    webapp.control_tests.set_gpid_remappings( {} )
    do_test( "6.4.4", True )
    do_test( "6.5.0", False )
    do_test( "6.5.1", False )

# ---------------------------------------------------------------------

def test_compare_version_strings():
    """Test comparing version strings."""

    # test comparing VASSAL version strings
    vassal_versions = list( SUPPORTED_VASSAL_VERSIONS.keys() )
    for i,vassal_version in enumerate( vassal_versions ):
        if i > 0:
            assert compare_version_strings( vassal_versions[i-1], vassal_version ) < 0
        assert compare_version_strings( vassal_version, vassal_version ) == 0
        if i < len(vassal_versions)-1:
            assert compare_version_strings( vassal_version, vassal_versions[i+1] ) < 0

    # test comparing VASL version strings
    for i,vasl_version in enumerate(SUPPORTED_VASL_MOD_VERSIONS):
        if i > 0:
            assert compare_version_strings( SUPPORTED_VASL_MOD_VERSIONS[i-1], vasl_version ) < 0
        assert compare_version_strings( vasl_version, vasl_version ) == 0
        if i < len(SUPPORTED_VASL_MOD_VERSIONS)-1:
            assert compare_version_strings( vasl_version, SUPPORTED_VASL_MOD_VERSIONS[i+1] ) < 0
