package de.ugoe.cs.quest.plugin.mfc;

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

import de.ugoe.cs.quest.eventcore.Event;
import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage;
import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessageType;
import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree;
import de.ugoe.cs.util.console.Console;

/**
 * <p>
 * Responsible to split sequences into subsequences, such that each subsequences contains exactly
 * one event.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class SequenceSplitter {

    /**
     * <p>
     * Contains the current subsequence.
     * </p>
     */
    private List<WindowsMessage> currentSequence;

    /**
     * <p>
     * Number of messages in the current sequences, that signal that a key or mouse button has been
     * pressed down to which not yet a message has been found, that signals that the button has been
     * released.
     * </p>
     */
    private int openDowns;

    /**
     * <p>
     * Internal flag that signals if {@link #currentSequence} needs to be initialized.
     * </p>
     */
    private boolean initMessages;

    /**
     * <p>
     * The {@link EventGenerator} used to convert the subsequences into {@link Event}s
     * </p>
     */
    private EventGenerator tokenGenerator;

    /**
     * <p>
     * The event sequence generated.
     * </p>
     */
    private List<Event> actionSequence;

    /**
     * <p>
     * Type of the previous message.
     * </p>
     */
    private WindowsMessageType prevMsg;

    /**
     * <p>
     * Constructor. Creates a new SequenceSplitter.
     * </p>
     */
    public SequenceSplitter(WindowTree windowTree) {
        currentSequence = new LinkedList<WindowsMessage>();
        openDowns = 0;
        initMessages = true;
        tokenGenerator = new EventGenerator(windowTree);
        actionSequence = new LinkedList<Event>();
        prevMsg = null;
    }

    /**
     * <p>
     * Called by the {@link MFCLogParser} every time a message is parsed.
     * </p>
     * 
     * @param msg
     *            message to be added
     */
    public void addMessage(WindowsMessage msg) {
        if (startOfSequence(msg)) {
            if (!initMessages) {
                Event currentAction = tokenGenerator.generateEvent(currentSequence);
                if (currentAction != null) {
                    actionSequence.add(currentAction);
                }
                if (msg.getType().isKeyMessage() && openDowns > 0) {
                    Console.traceln(Level.SEVERE, "Key message found with open down mouse " +
                                    "messages - will probabably result in a faulty sequence.");
                }
            }
            else {
                initMessages = false;
            }
            currentSequence = new LinkedList<WindowsMessage>();
        }
        if (msg.getType().isUpMessage()) {
            if (openDowns > 0) {
                openDowns--;
            }
        }

        // this fix checks if there are two consecutive mouse-down messages.
        // This sometimes occurs due to incorrect filtering in the monitoring
        // dll.
        if (!(prevMsg == WindowsMessageType.WM_LBUTTONDOWN && prevMsg == msg.getType())) {
            currentSequence.add(msg);
        }
        else {
            openDowns--;
        }
        prevMsg = msg.getType();
    }

    /**
     * <p>
     * Returns the event sequence generated from the message that have been added.
     * </p>
     * 
     * @return generated event sequence
     */
    public List<Event> getSequence() {
        return actionSequence;
    }

    /**
     * <p>
     * Called when a session in the log file is finished, i.e., a closing session-node is found.
     * </p>
     */
    public void endSession() {
        Event currentAction = tokenGenerator.generateEvent(currentSequence);
        if (currentAction != null) {
            actionSequence.add(currentAction);
        }
    }

    /**
     * <p>
     * Checks if the message starts a new subsequence and returns the result.
     * </p>
     * 
     * @param msg
     *            message that is checked
     * @return true, if a new subsequence begins
     */
    private boolean startOfSequence(WindowsMessage msg) {
        boolean isStart = false;
        WindowsMessageType msgType = msg.getType();
        if (msgType.isKeyMessage()) {
            isStart = true;
        }
        if (msgType.isDownMessage()) {
            openDowns++;
            if (openDowns == 1) {
                isStart = true;
            }
        }
        if (msgType.isDblclkMessage()) {
            openDowns++;
        }
        return isStart;
    }

}
