
package de.ugoe.cs.quest.eventcore.gui;

import static org.junit.Assert.assertEquals;

import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;

import org.junit.BeforeClass;
import org.junit.Test;
import static org.mockito.Mockito.*;

import de.ugoe.cs.quest.eventcore.Event;
import de.ugoe.cs.quest.eventcore.gui.KeyInteractionSorter.CleanupMode;
import de.ugoe.cs.quest.eventcore.gui.MouseButtonInteraction.Button;
import de.ugoe.cs.quest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.tasktree.keyboardmaps.VirtualKey;
import de.ugoe.cs.util.console.Console;
import de.ugoe.cs.util.console.TextConsole;

/**
 * @author Patrick Harms, Steffen Herbold
 */
public class KeyInteractionSorterTest {

    @Test
    public void testSortKeyInteractions_1() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // Normal pressing and releasing of A and B
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));

        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_2() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // A is pressed and not released before B is pressed
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));

        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_3() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // SHIFT is pressed and released after all keys are pressed
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_4() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // SHIFT is released before the last key is released
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));

        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_5() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // SHIFT is released before all other keys are released
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_C), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_C), guiElement));

        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_C), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_6() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // SHIFT, CTRL and ALT are pressed and released after all keys are pressed
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        expected.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
        expected.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_7() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // now SHIFT, CTRL and ALT are released before the last key is released
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));

        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        expected.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
        expected.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_8() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // SHIFT, CTRL and ALT are released in another order and before some other keys are
        // released
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_C), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_C), guiElement));

        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        expected.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
        expected.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_C), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_9() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // holding of A
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));

        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_10() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // holding of shift and meanwhile pressing of A
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_11() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // Check if keyboard focus change is moved in front of a sequence in case it is in between a
        // pressed
        // released pair
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyboardFocusChange(), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));

        expected.add(new Event(new KeyboardFocusChange(), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_12() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter();

        IGUIElement guiElement = mock(IGUIElement.class);

        // Check if mouse events stay where they are
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new MouseButtonDown(Button.LEFT), guiElement));
        input.add(new Event(new MouseButtonUp(Button.LEFT), guiElement));
        input.add(new Event(new MouseClick(Button.LEFT), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));

        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new MouseButtonDown(Button.LEFT), guiElement));
        expected.add(new Event(new MouseButtonUp(Button.LEFT), guiElement));
        expected.add(new Event(new MouseClick(Button.LEFT), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_13() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter(CleanupMode.REMOVAL);

        IGUIElement guiElement = mock(IGUIElement.class);

        // Check if mouse events stay where they are
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));

        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @Test
    public void testSortKeyInteractions_14() {
        List<Event> input = new LinkedList<Event>();
        List<Event> expected = new LinkedList<Event>();

        KeyInteractionSorter sorter = new KeyInteractionSorter(CleanupMode.ADDITION);

        IGUIElement guiElement = mock(IGUIElement.class);

        // Check if mouse events stay where they are
        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));

        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));

        List<Event> output = sorter.sortKeyInteractions(input);

        assertEquals(expected, output);
    }

    @BeforeClass
    public static void setUpBeforeClass() {
        Console.reset();
        Console.getInstance().registerTraceListener(new TextConsole(Level.INFO));
    }
}
