//   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 static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

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.MouseButtonDown;
import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
import de.ugoe.cs.autoquest.eventcore.gui.MouseClickCondenser;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.test.DummyGUIElement;
import de.ugoe.cs.autoquest.test.DummyInteraction;

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

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

    /**
     *
     */
    @Test
    public void testNormalSequence() {
        MouseButtonInteraction.Button leftBtn = MouseButtonInteraction.Button.LEFT;
        MouseButtonInteraction.Button middleBtn = MouseButtonInteraction.Button.MIDDLE;
        MouseButtonInteraction.Button rightBtn = MouseButtonInteraction.Button.RIGHT;

        IGUIElement element1 = new DummyGUIElement("elem1");

        simulateEvent(new MouseButtonDown(leftBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(leftBtn, 0, 0), element1);
        simulateEvent(new MouseClick(leftBtn, 0, 0), element1);
        assertCondensedMouseClicks("click left elem1");

        simulateEvent(new MouseButtonDown(middleBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(middleBtn, 0, 0), element1);
        simulateEvent(new MouseClick(middleBtn, 0, 0), element1);
        assertCondensedMouseClicks("click left elem1", "click middle elem1");

        simulateEvent(new MouseButtonDown(rightBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(rightBtn, 0, 0), element1);
        simulateEvent(new MouseClick(rightBtn, 0, 0), element1);
        assertCondensedMouseClicks("click left elem1", "click middle elem1", "click right elem1");

        simulateEvent(new MouseButtonDown(leftBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(leftBtn, 0, 0), element1);
        assertCondensedMouseClicks("click left elem1", "click middle elem1", "click right elem1",
                                   "click left elem1");

        simulateEvent(new MouseButtonDown(middleBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(middleBtn, 0, 0), element1);
        assertCondensedMouseClicks("click left elem1", "click middle elem1", "click right elem1",
                                   "click left elem1", "click middle elem1");

        simulateEvent(new MouseButtonDown(rightBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(rightBtn, 0, 0), element1);
        assertCondensedMouseClicks("click left elem1", "click middle elem1", "click right elem1",
                                   "click left elem1", "click middle elem1", "click right elem1");

    }

    /**
     *
     */
    @Test
    public void testMouseClickInSequence() {
        MouseButtonInteraction.Button middleBtn = MouseButtonInteraction.Button.MIDDLE;
        MouseButtonInteraction.Button rightBtn = MouseButtonInteraction.Button.RIGHT;

        IGUIElement element1 = new DummyGUIElement("elem1");
        IGUIElement element2 = new DummyGUIElement("elem2");

        simulateEvent(new DummyInteraction("bla", 1), element1);
        simulateEvent(new DummyInteraction("bli", 1), element1);
        simulateEvent(new MouseButtonDown(middleBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(middleBtn, 0, 0), element1);
        simulateEvent(new MouseClick(middleBtn, 0, 0), element1);
        simulateEvent(new DummyInteraction("blo", 1), element1);
        simulateEvent(new DummyInteraction("blu", 1), element1);
        assertCondensedMouseClicks("", "", "click middle elem1", "", "");

        simulateEvent(new DummyInteraction("bla", 1), element2);
        simulateEvent(new DummyInteraction("bli", 1), element2);
        simulateEvent(new MouseButtonDown(rightBtn, 0, 0), element2);
        simulateEvent(new MouseButtonUp(rightBtn, 0, 0), element2);
        simulateEvent(new MouseClick(rightBtn, 0, 0), element2);
        simulateEvent(new DummyInteraction("blo", 1), element2);
        simulateEvent(new DummyInteraction("blu", 1), element2);
        assertCondensedMouseClicks("", "", "click middle elem1", "", "",
                                   "", "", "click right elem2", "", "");

    }


    /**
     *
     */
    @Test
    public void testNoMouseClick() {
        MouseButtonInteraction.Button leftBtn = MouseButtonInteraction.Button.LEFT;
        MouseButtonInteraction.Button middleBtn = MouseButtonInteraction.Button.MIDDLE;
        MouseButtonInteraction.Button rightBtn = MouseButtonInteraction.Button.RIGHT;

        IGUIElement element1 = new DummyGUIElement("elem1");
        IGUIElement element2 = new DummyGUIElement("elem2");

        simulateEvent(new MouseButtonDown(leftBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(leftBtn, 0, 0), element2);
        simulateEvent(new MouseClick(leftBtn, 0, 0), element1);
        assertCondensedMouseClicks("", "", "click left elem1");

        simulateEvent(new MouseButtonDown(middleBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(rightBtn, 0, 0), element1);
        simulateEvent(new MouseClick(middleBtn, 0, 0), element1);
        assertCondensedMouseClicks("", "", "click left elem1", "", "", "click middle elem1");

        simulateEvent(new MouseButtonDown(leftBtn, 1, 0), element1);
        simulateEvent(new MouseButtonUp(leftBtn, 0, 1), element1);
        simulateEvent(new MouseClick(leftBtn, 1, 1), element1);
        assertCondensedMouseClicks("", "", "click left elem1", "", "", "click middle elem1",
                                   "drag left elem1", "click left elem1");
    }

    /**
     *
     */
    @Test
    public void testDragAndDrop() {
        MouseButtonInteraction.Button leftBtn = MouseButtonInteraction.Button.LEFT;
        MouseButtonInteraction.Button middleBtn = MouseButtonInteraction.Button.MIDDLE;
        MouseButtonInteraction.Button rightBtn = MouseButtonInteraction.Button.RIGHT;

        IGUIElement element1 = new DummyGUIElement("elem1");

        simulateEvent(new MouseButtonDown(leftBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(leftBtn, 5, 0), element1);
        assertCondensedMouseClicks("drag left elem1");

        simulateEvent(new MouseButtonDown(leftBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(leftBtn, 5, 5), element1);
        assertCondensedMouseClicks("drag left elem1", "drag left elem1");

        simulateEvent(new MouseButtonDown(leftBtn, 0, 0), element1);
        simulateEvent(new MouseButtonUp(leftBtn, 0, 5), element1);
        simulateEvent(new MouseClick(leftBtn, 0, 0), element1);
        assertCondensedMouseClicks("drag left elem1", "drag left elem1", "drag left elem1",
                                   "click left elem1");

        simulateEvent(new MouseButtonDown(middleBtn, 5, 0), element1);
        simulateEvent(new MouseButtonUp(middleBtn, 0, 0), element1);
        simulateEvent(new MouseClick(middleBtn, 0, 0), element1);
        assertCondensedMouseClicks("drag left elem1", "drag left elem1", "drag left elem1",
                                   "click left elem1", "", "", "click middle elem1");

        simulateEvent(new MouseButtonDown(rightBtn, 0, 5), element1);
        simulateEvent(new MouseButtonUp(rightBtn, 0, 0), element1);
        simulateEvent(new MouseClick(rightBtn, 0, 0), element1);
        assertCondensedMouseClicks("drag left elem1", "drag left elem1", "drag left elem1",
                                   "click left elem1", "", "", "click middle elem1",
                                   "", "", "click right elem1");
    }

    /**
     *
     */
    @Test
    public void testDoubleClick() {
        MouseButtonInteraction.Button leftBtn = MouseButtonInteraction.Button.LEFT;
        MouseButtonInteraction.Button rightBtn = MouseButtonInteraction.Button.RIGHT;

        IGUIElement element1 = new DummyGUIElement("elem1");

        simulateEventWithTimestamp(new MouseClick(leftBtn, 0, 0), element1);
        simulateEventWithTimestamp(new MouseClick(leftBtn, 0, 0), element1);
        assertCondensedMouseClicks("dblclick left elem1");

        simulateEventWithTimestamp(new MouseButtonDown(leftBtn, 5, 5), element1);
        simulateEventWithTimestamp(new MouseButtonUp(leftBtn, 5, 5), element1);
        simulateEventWithTimestamp(new MouseButtonDown(leftBtn, 5, 5), element1);
        simulateEventWithTimestamp(new MouseButtonUp(leftBtn, 5, 5), element1);
        assertCondensedMouseClicks("dblclick left elem1", "dblclick left elem1");

        simulateEventWithTimestamp(new MouseClick(leftBtn, 0, 0), element1);
        simulateEventWithTimestamp(new MouseClick(leftBtn, 5, 5), element1);
        assertCondensedMouseClicks("dblclick left elem1", "dblclick left elem1",
                                   "click left elem1", "click left elem1");

        simulateEventWithTimestamp(new MouseButtonDown(leftBtn, 0, 0), element1);
        simulateEventWithTimestamp(new MouseButtonUp(leftBtn, 0, 0), element1);
        simulateEventWithTimestamp(new MouseButtonDown(leftBtn, 5, 5), element1);
        simulateEventWithTimestamp(new MouseButtonUp(leftBtn, 5, 5), element1);
        assertCondensedMouseClicks("dblclick left elem1", "dblclick left elem1",
                                   "click left elem1", "click left elem1",
                                   "click left elem1", "click left elem1");

        simulateEventWithTimestamp(new MouseClick(rightBtn, 0, 0), element1);
        simulateEventWithTimestamp(new MouseClick(rightBtn, 5, 5), element1);
        assertCondensedMouseClicks("dblclick left elem1", "dblclick left elem1",
                                   "click left elem1", "click left elem1",
                                   "click left elem1", "click left elem1",
                                   "click right elem1", "click right elem1");

        simulateEventWithTimestamp(new MouseButtonDown(rightBtn, 0, 0), element1);
        simulateEventWithTimestamp(new MouseButtonUp(rightBtn, 0, 0), element1);
        simulateEventWithTimestamp(new MouseButtonDown(rightBtn, 5, 5), element1);
        simulateEventWithTimestamp(new MouseButtonUp(rightBtn, 5, 5), element1);
        assertCondensedMouseClicks("dblclick left elem1", "dblclick left elem1",
                                   "click left elem1", "click left elem1",
                                   "click left elem1", "click left elem1",
                                   "click right elem1", "click right elem1",
                                   "click right elem1", "click right elem1");

    }

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

    /**
     *
     */
    private void simulateEventWithTimestamp(IEventType eventType, IGUIElement guiElement) {
        Event event = new Event(eventType, guiElement);
        event.setTimestamp(System.currentTimeMillis());
        events.add(event);
    }

    /**
     *
     */
    private void assertCondensedMouseClicks(String... clickedTargets) {
        MouseClickCondenser condenser = new MouseClickCondenser();
        List<Event> result = condenser.condenseMouseClicks(events);
       
        assertEquals(clickedTargets.length, result.size());
       
        for (int i = 0; i < clickedTargets.length; i++) {
            String clickedTarget = clickedTargets[i];
           
            if ((clickedTarget != null) && (!"".equals(clickedTarget))) {
                String[] parts = clickedTarget.split(" ");
                assertEquals("invalid test case", 3, parts.length);
               
                if ("click".equals(parts[0])) {
                    assertTrue(result.get(i).getType() instanceof MouseClick);
                }
                else if ("dblclick".equals(parts[0])) {
                    assertTrue(result.get(i).getType() instanceof MouseDoubleClick);
                }
                else if ("drag".equals(parts[0])) {
                    assertTrue(result.get(i).getType() instanceof MouseDragAndDrop);
                }
                else {
                    fail("invalid assertion in test case");
                }

                if ("left".equals(parts[1])) {
                    assertEquals(MouseButtonInteraction.Button.LEFT,
                                 ((MouseButtonInteraction) result.get(i).getType()).getButton());
                }
                else if ("middle".equals(parts[1])) {
                    assertEquals(MouseButtonInteraction.Button.MIDDLE,
                                 ((MouseButtonInteraction) result.get(i).getType()).getButton());
                }
                else if ("right".equals(parts[1])) {
                    assertEquals(MouseButtonInteraction.Button.RIGHT,
                                 ((MouseButtonInteraction) result.get(i).getType()).getButton());
                }
                else {
                    fail("invalid assertion in test case");
                }

                assertTrue(result.get(i).getTarget() instanceof DummyGUIElement);

                assertEquals
                    (parts[2], ((DummyGUIElement) result.get(i).getTarget()).getStringIdentifier());
            }
            else {
                assertFalse(result.get(i).getType() instanceof MouseClick);
            }
        }
    }
}
