""" Test the turn track functionality. """

from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.keys import Keys

from vasl_templates.webapp.tests.utils import \
    init_webapp, get_turn_track_nturns, set_turn_track_nturns, select_droplist_val, get_droplist_vals, \
    SwitchFrame, unload_table, wait_for, wait_for_elem, wait_for_clipboard, find_child
from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario, save_scenario

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

def test_turn_track_basic( webapp, webdriver ):
    """Test basic turn track functionality."""

    # initialize
    webapp.control_tests.set_data_dir( "{REAL}" )
    init_webapp( webapp, webdriver )

    # check the initial state of the UI
    assert get_turn_track_nturns() == ""
    assert not find_child( "button#turn-track-settings" ).is_displayed()
    assert not find_child( ".snippet-control[data-id='turn_track']" ).is_displayed()

    # configure a number of turns
    set_turn_track_nturns( 6 )
    assert find_child( "button#turn-track-settings" ).is_displayed()
    assert find_child( ".snippet-control[data-id='turn_track']" ).is_displayed()

    # generate a snippet
    assert _generate_turn_track_snippet( None ) == [
        [ (1,None,None), (2,None,None), (3,None,None), (4,None,None), (5,None,None), (6,None,None) ]
    ]

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

def test_turn_track_controls( webapp, webdriver ):
    """Test the basic controls for configuring the turn track."""

    # initialize
    webapp.control_tests.set_data_dir( "{REAL}" )
    init_webapp( webapp, webdriver )

    # show the turn track dialog
    dlg = _show_turn_track_dialog( 6 )
    with SwitchFrame( webdriver, "#turn-track-preview" ):
        _click_reinf_flag( 1, 1 )
        _click_reinf_flag( 2, 1 )
        _click_reinf_flag( 2, 2 )
        _click_reinf_flag( 3, 2 )

    # change the width
    _change_turn_track_width( dlg, 3 )
    def check_for_width():
        return _generate_turn_track_snippet( dlg ) == [
            [ (1,"player1",None), (2,"player1","player2"), (3,None,"player2") ],
            [ (4,None,None), (5,None,None), (6,None,None) ]
        ]
    wait_for( 2, check_for_width )

    # swap the players
    _swap_turn_track_players( dlg )
    def check_for_swap_players():
        return _generate_turn_track_snippet( dlg ) == [
            [ (1,None,"player2"), (2,"player1","player2"), (3,"player1",None) ],
            [ (4,None,None), (5,None,None), (6,None,None) ]
        ]
    wait_for( 2, check_for_swap_players )

    # make the turn track vertical
    _change_turn_track_direction( dlg )
    def check_for_vertical():
        return _generate_turn_track_snippet( dlg ) == [
            [ (1,None,"player2"), (3,"player1",None), (5,None,None) ],
            [ (2,"player1","player2"), (4,None,None), (6,None,None) ]
        ]
    wait_for( 2, check_for_vertical )

    # reset the controls
    find_child( "button.reset2" ).click()
    ask = wait_for_elem( 2, ".ui-dialog.ask" )
    find_child( "button.ok", ask ).click()
    def check_for_reset():
        return _generate_turn_track_snippet( dlg ) == [
            [ (1,None,None), (2,None,None), (3,None,None), (4,None,None), (5,None,None), (6,None,None) ]
        ]
    wait_for( 2, check_for_reset )

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

def test_turn_track_reinforcements( webapp, webdriver ):
    """Test configuring reinforcements on the turn track."""

    # initialize
    webapp.control_tests.set_data_dir( "{REAL}" )
    init_webapp( webapp, webdriver )

    # show the turn track dialog
    dlg = _show_turn_track_dialog( 6.5 )

    # turn on some reinforcements, then check the snippet
    with SwitchFrame( webdriver, "#turn-track-preview" ):
        _click_reinf_flag( 2, 1 )
        _click_reinf_flag( 3, 1 )
        _click_reinf_flag( 3, 2 )
        _click_reinf_flag( 7, 1 )
    assert _generate_turn_track_snippet( dlg ) == [
        [ (1,None,None), (2,"player1",None), (3,"player1","player2"),
          (4,None,None), (5,None,None) , (6,None,None), (7,"player1",None)
        ]
    ]

    # turn off some reinforcements, turn some on, then check the snippet
    with SwitchFrame( webdriver, "#turn-track-preview" ):
        _click_reinf_flag( 2, 2 )
        _click_reinf_flag( 3, 1 )
        _click_reinf_flag( 3, 2 )
        _click_reinf_flag( 5, 2 )
        _click_reinf_flag( 7, 1 )
    assert _generate_turn_track_snippet( dlg ) == [
        [ (1,None,None), (2,"player1","player2"), (3,None,None),
          (4,None,None), (5,None,"player2") , (6,None,None), (7,None,None)
        ]
    ]

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

def test_turn_track_persistence( webapp, webdriver ):
    """Test saving and loading turn track settings."""

    # initialize
    webapp.control_tests.set_data_dir( "{REAL}" )
    init_webapp( webapp, webdriver, scenario_persistence=1 )

    # show the turn track dialog
    load_scenario( {
        "PLAYER_1": "japanese", "PLAYER_2": "american",
        "TURN_TRACK": { "NTURNS": 6.5 },
    } )
    dlg = _show_turn_track_dialog( None )

    # configure the turn track
    with SwitchFrame( webdriver, "#turn-track-preview" ):
        _click_reinf_flag( 1, 1 )
        _click_reinf_flag( 2, 2 )
        _click_reinf_flag( 3, 1 )
        _click_reinf_flag( 3, 2 )
    _change_turn_track_width( dlg, 4 )
    _swap_turn_track_players( dlg )
    _change_turn_track_direction( dlg )

    # check the snippet
    expected = [
        [ (1,None,"player2"), (3,"player1","player2"), (5,None,None), (7,None,None) ],
        [ (2,"player1",None) , (4,None,None), (6,None,None) ]
    ]
    wait_for( 2,
        lambda: _generate_turn_track_snippet( dlg ) == expected
    )

    # save the scenario
    dlg.send_keys( Keys.ESCAPE )
    saved_scenario = save_scenario()
    assert saved_scenario["TURN_TRACK"] == {
        "NTURNS": "6.5",
        "WIDTH": "4", "VERTICAL": True, "SWAP_PLAYERS": True,
        "REINFORCEMENTS_1": "2,3", "REINFORCEMENTS_2": "1,3",
    }
    assert _generate_turn_track_snippet( None ) == expected

    # reset the scenario
    webdriver.refresh()
    assert not find_child( "button.generate[data-id='turn_track']" ).is_displayed()

    # load the scenario and generate the snippet
    load_scenario( saved_scenario )
    assert _generate_turn_track_snippet( None ) == expected

    # open the turn track dialog and check that the controls were loaded correctly
    dlg = _show_turn_track_dialog( None )
    assert get_turn_track_nturns() == "6.5"
    sel = Select( find_child( "select[name='width']", dlg ) )
    assert sel.first_selected_option.get_attribute( "value" ) == "4"
    assert find_child( "input[name='vertical']" ).is_selected()
    assert find_child( "input[name='swap-players']" ).is_selected()
    assert _generate_turn_track_snippet( dlg ) == expected

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

def test_turn_track_droplist( webapp, webdriver ):
    """Test updating entries in the turn track droplist (#turns)."""

    # initialize
    webapp.control_tests.set_data_dir( "{REAL}" )
    init_webapp( webapp, webdriver, scenario_persistence=1 )

    # check the initial state of the droplist
    assert _unload_turn_track_droplist() == [
        "", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "(show-dialog)"
    ]

    # configure a number of turns that is not in the default list (small)
    sel = Select( find_child( "select[name='TURN_TRACK_NTURNS']" ) )
    select_droplist_val( sel, "(show-dialog)" )
    sel2 = Select( find_child( "#turn-track select[name='nturns']" ) )
    select_droplist_val( sel2, "2" )
    assert _unload_turn_track_droplist() == [
        "", "2", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10"
    ]

    # configure a number of turns that is not in the default list (large)
    select_droplist_val( sel2, "14.5" )
    expected = [ "", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "14.5" ]
    assert _unload_turn_track_droplist() == expected

    # save and reload the scenario
    find_child( ".ui-dialog.turn-track" ).send_keys( Keys.ESCAPE )
    saved_scenario = save_scenario()
    webdriver.refresh()
    load_scenario( saved_scenario )
    assert _unload_turn_track_droplist() == expected

    # configure a number of turns that is in the default list
    sel = Select( find_child( "select[name='TURN_TRACK_NTURNS']" ) )
    select_droplist_val( sel, "8" )
    assert _unload_turn_track_droplist() == [
        "", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10"
    ]

    # disable the turn track
    select_droplist_val( sel, "" )
    assert _unload_turn_track_droplist() == [
        "", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "(show-dialog)"
    ]

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

def _show_turn_track_dialog( nturns ):
    """Show the TURN TRACK dialog."""
    if nturns:
        set_turn_track_nturns( nturns )
    btn = wait_for_elem( 2, "button#turn-track-settings" )
    btn.click()
    dlg = wait_for_elem( 2, ".ui-dialog.turn-track" )
    return dlg

def _generate_turn_track_snippet( dlg ):
    """Generate a turn track snippet."""

    # generate the snippet
    btn = find_child( "button.snippet", dlg ) if dlg else find_child( "button.generate[data-id='turn_track']" )
    assert btn.is_displayed()
    btn.click()
    clipboard = wait_for_clipboard( 2, "<!-- vasl-templates:id", contains=True )

    def get_reinforce_class( cell ):
        cell_class = str( cell.xpath( ".//@class" )[0] )
        if cell_class == "reinforce1":
            return "player1"
        elif cell_class == "reinforce2":
            return "player2"
        else:
            assert cell_class == "no-reinforce"
            return None

    def unload_square( square ):
        cells = square.xpath( ".//td" )
        assert len(cells) == 3
        return (
            int( cells[1].text ),
            get_reinforce_class( cells[0] ),
            get_reinforce_class( cells[2] ),
        )

    # unload the snippet contents
    squares = unload_table( "//table[@class='turn-track']", html=clipboard, unload=False )
    for row_no, row in enumerate(squares):
        for col_no, square in enumerate(row):
            squares[row_no][col_no] = unload_square( square )

    return squares

def _unload_turn_track_droplist():
    """Get the available options in the turn track droplist."""
    keys = []
    prev_key = None
    options = get_droplist_vals( Select(
        find_child( "select[name='TURN_TRACK_NTURNS']" )
    ) )
    for key, caption in options:
        if key == "":
            assert caption == "-"
        elif key == "(show-dialog)":
            assert caption == "(more)"
        else:
            assert key == caption
            key2 = float( key )
            if prev_key:
                assert key2 > prev_key
            assert int( 10 * key2 ) % 10 in (0,5)
            prev_key = key2
        keys.append( key )
    return keys

def _click_reinf_flag( turn_no, player_no ):
    """Click on a reinforcement flag."""
    find_child(
        "#flag-{}_{} .click".format( turn_no, player_no )
    ).click()

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

def _change_turn_track_width( dlg, width ):
    """Change the turn track width."""
    sel = Select( find_child( "select[name='width']", dlg ) )
    _wait_for_preview( dlg,
        lambda: select_droplist_val( sel, width )
    )

def _change_turn_track_direction( dlg ):
    """Toggle the direction of the turn track."""
    _wait_for_preview( dlg,
        lambda: find_child( "input[name='vertical']", dlg ).click()
    )

def _swap_turn_track_players( dlg ):
    """Swap the turn track players."""
    _wait_for_preview( dlg,
        lambda: find_child( "input[name='swap-players']", dlg ).click()
    )

def _wait_for_preview( dlg, func ):
    """Make a change to the preview, and wait for the UI to update."""
    # NOTE: The preview <iframe> is replaced with a new one, so we need to be ready for the old one to disappear.
    iframe = find_child( "iframe", dlg )
    seqno = iframe.get_attribute( "data-seqno" )
    func()
    def check_seqno():
        iframe = find_child( "iframe", dlg )
        if not iframe:
            return False
        return iframe.get_attribute( "data-seqno" ) != seqno
    wait_for( 20, check_seqno )
