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

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import de.ugoe.cs.quest.eventcore.Event;
import de.ugoe.cs.quest.eventcore.IEventType;
import de.ugoe.cs.quest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.quest.test.DummyGUIElement;
import de.ugoe.cs.quest.test.DummyTextField;
import de.ugoe.cs.tasktree.keyboardmaps.VirtualKey;

/**
 * @author Patrick Harms
 */
public class TextInputDetectorTest {

    private List<Event> events = new ArrayList<Event>();

    /**
     *
     */
    @Test
    public void testSimpleTextEntry() {
        IGUIElement element1 = new DummyTextField("elem1");
        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
        assertTextInput("a");

        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
        assertTextInput("ab");

        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
        assertTextInput("abC");

        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_D), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_D), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_E), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_E), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_F), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_F), element1);
        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
        assertTextInput("abCDEF");

        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_G), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_G), element1);
        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
        assertTextInput("abCDEFg");

    }

    /**
     *
     */
    @Test
    public void testTextEntryOnDifferentGuiElements() {
        IGUIElement element1 = new DummyTextField("elem1");
        IGUIElement element2 = new DummyTextField("elem2");
        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
        assertTextInput("a");

        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element2);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element2);
        assertTextInput("a", "b");

        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
        assertTextInput("a", "b", "C");

        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element2);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_D), element2);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_D), element2);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_E), element2);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_E), element2);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_F), element2);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_F), element2);
        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element2);
        assertTextInput("a", "b", "C", "DEF");

        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_G), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_G), element1);
        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
        assertTextInput("a", "b", "C", "DEF", "g");

    }

    /**
     *
     */
    @Test
    public void testUsageOfBackspace() {
        IGUIElement element1 = new DummyTextField("elem1");
        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_D), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_D), element1);
        simulateEvent(new KeyPressed(VirtualKey.BACK_SPACE), element1);
        simulateEvent(new KeyReleased(VirtualKey.BACK_SPACE), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_E), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_E), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_F), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_F), element1);
        assertTextInput("abcef");

        simulateEvent(new KeyPressed(VirtualKey.BACK_SPACE), element1);
        simulateEvent(new KeyReleased(VirtualKey.BACK_SPACE), element1);
        simulateEvent(new KeyPressed(VirtualKey.BACK_SPACE), element1);
        simulateEvent(new KeyReleased(VirtualKey.BACK_SPACE), element1);
        simulateEvent(new KeyPressed(VirtualKey.BACK_SPACE), element1);
        simulateEvent(new KeyReleased(VirtualKey.BACK_SPACE), element1);
        assertTextInput("ab");

    }

    /**
     *
     */
    @Test
    public void testOtherInteractionsInBetween() {
        IGUIElement element1 = new DummyTextField("elem1");
        IGUIElement element2 = new DummyGUIElement("elem2");
        IGUIElement element3 = new DummyGUIElement("elem3");
        
        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element2);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
        assertTextInput("", "abc");

        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element2);
        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element3);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element3);
        assertTextInput("", "abc", "", "", "abc", "");

        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element2);
        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element3);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element3);
        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
        
        // the pressing of the c is not completed. So it must be included in the resulting
        // text input event but it may only be followed by the mouse click. The subsequent release
        // of the c key is ignored and results in a removal of the event.
        assertTextInput("", "abc", "", "", "abc", "", "", "", "abc", "");
    }

    /**
     *
     */
    private void simulateEvent(IEventType eventType, IGUIElement guiElement) {
        events.add(new Event(eventType, guiElement));
    }

    /**
     *
     */
    private void assertTextInput(String... enteredTexts) {
        TextInputDetector detector = new TextInputDetector();
        List<Event> result = detector.detectTextInputs(events);
        
        assertEquals(enteredTexts.length, result.size());
        
        for (int i = 0; i < enteredTexts.length; i++) {
            String enteredText = enteredTexts[i];
            
            if ((enteredText != null) && (!"".equals(enteredText))) {
                assertTrue(result.get(i).getType() instanceof TextInput);
                assertNotNull(((TextInput) result.get(i).getType()).getEnteredText());
                assertEquals(enteredText, ((TextInput) result.get(i).getType()).getEnteredText());
            
                assertNotNull(((TextInput) result.get(i).getType()).getTextInputEvents());
                assertTrue(((TextInput) result.get(i).getType()).getTextInputEvents().size() > 0);
                //assertTrue
                //    ((((TextInput) result.get(i).getType()).getTextInputEvents().size() % 2) == 0);
            }
            else {
                assertFalse(result.get(i).getType() instanceof TextInput);
            }
        }
    }
}
