//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.


package de.ugoe.cs.autoquest.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.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.gui.KeyInteractionCorrector;
import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
import de.ugoe.cs.autoquest.eventcore.gui.KeyTyped;
import de.ugoe.cs.autoquest.eventcore.gui.KeyboardFocusChange;
import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
import de.ugoe.cs.autoquest.eventcore.gui.KeyInteractionCorrector.CleanupMode;
import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction.Button;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
import de.ugoe.cs.util.console.Console;
import de.ugoe.cs.util.console.TextConsole;

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

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

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector();

        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, 0, 0), guiElement));
        input.add(new Event(new MouseButtonUp(Button.LEFT, 0, 0), guiElement));
        input.add(new Event(new MouseClick(Button.LEFT, 0, 0), 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, 0, 0), guiElement));
        expected.add(new Event(new MouseButtonUp(Button.LEFT, 0, 0), guiElement));
        expected.add(new Event(new MouseClick(Button.LEFT, 0, 0), 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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector(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>();

        KeyInteractionCorrector sorter = new KeyInteractionCorrector(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));
    }
}
