//   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.*;

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

import org.junit.Test;

import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.IEventType;
import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
import de.ugoe.cs.autoquest.eventcore.gui.TextInputDetector;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
import de.ugoe.cs.autoquest.test.DummyGUIElement;
import de.ugoe.cs.autoquest.test.DummyTextField;

/**
 * @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, 0, 0), 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, 0, 0), element2);
        simulateEvent(new MouseClick(MouseClick.Button.LEFT, 0, 0), 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, 0, 0), element3);
        assertTextInput("", "abc", "", "", "abc", "");

        simulateEvent(new MouseClick(MouseClick.Button.LEFT, 0, 0), element2);
        simulateEvent(new MouseClick(MouseClick.Button.LEFT, 0, 0), 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, 0, 0), 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);
            }
        }
    }
}
