package de.ugoe.cs.quest.tasktrees.temporalrelation;

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

import de.ugoe.cs.quest.eventcore.IEventTarget;
import de.ugoe.cs.quest.eventcore.gui.KeyInteraction;
import de.ugoe.cs.quest.eventcore.gui.KeyPressed;
import de.ugoe.cs.quest.eventcore.gui.KeyReleased;
import de.ugoe.cs.quest.eventcore.guimodel.ITextArea;
import de.ugoe.cs.quest.eventcore.guimodel.ITextField;
import de.ugoe.cs.quest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.quest.tasktrees.treeifc.ISequence;
import de.ugoe.cs.quest.tasktrees.treeifc.ITaskTreeBuilder;
import de.ugoe.cs.quest.tasktrees.treeifc.ITaskTreeNode;
import de.ugoe.cs.quest.tasktrees.treeifc.ITaskTreeNodeFactory;
import de.ugoe.cs.quest.tasktrees.treeifc.ITextInputEventTask;
import de.ugoe.cs.tasktree.keyboardmaps.KeyboardMap;
import de.ugoe.cs.tasktree.keyboardmaps.KeyboardMapFactory;
import de.ugoe.cs.tasktree.keyboardmaps.VirtualKey;

/**
 * TODO comment
 * 
 * @version $Revision: $ $Date: 18.03.2012$
 * @author 2012, last modified by $Author: patrick$
 */
public class DefaultTextInputReductionRule implements TemporalRelationshipRule {
    
    /** */
    private KeyboardMap keyboardMap = KeyboardMapFactory.createKeyboardMap(Locale.GERMAN);

    /*
     * (non-Javadoc)
     * 
     * @see TemporalRelationshipRule#apply(TaskTreeNode, TaskTreeBuilder, TaskTreeNodeFactory)
     */
    @Override
    public RuleApplicationResult apply(ITaskTreeNode        parent,
                                       ITaskTreeBuilder     builder,
                                       ITaskTreeNodeFactory nodeFactory,
                                       boolean              finalize)
    {
        if ((!(parent instanceof ISequence)) || (parent instanceof ITextInputEventTask)) {
            return null;
        }

        RuleApplicationResult result = new RuleApplicationResult();
        int textEntryStartIndex = -1;
        IEventTarget currentEventTarget = null;

        int index = 0;
        ITaskTreeNode task = null;
        while (index < parent.getChildren().size()) {
            task = parent.getChildren().get(index);
            if (isKeyInteraction(task) &&
                isDataInputEventTarget(((IEventTask) task).getEventTarget()))
            {
                if (textEntryStartIndex < 0) {
                    textEntryStartIndex = index;
                    currentEventTarget = ((IEventTask) task).getEventTarget();
                }
                else if (!currentEventTarget.equals(((IEventTask) task).getEventTarget())) {
                    handleTextEntrySequence(parent, textEntryStartIndex, index - 1,
                                            currentEventTarget, builder, nodeFactory, result);
                    return result;
                }
            }
            else if (textEntryStartIndex >= 0) {
                handleTextEntrySequence(parent, textEntryStartIndex, index - 1, currentEventTarget,
                                        builder, nodeFactory, result);
                return result;
            }

            index++;
        }

        if (textEntryStartIndex >= 0) {
            if (finalize) {
                handleTextEntrySequence(parent, textEntryStartIndex,
                                        parent.getChildren().size() - 1, currentEventTarget,
                                        builder, nodeFactory, result);
            }
            else {
                result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
            }
        }

        return result;
    }

    /**
     * TODO: comment
     * 
     * @param taskTreeNode
     * @return
     */
    private boolean isKeyInteraction(ITaskTreeNode taskTreeNode) {
        if ((taskTreeNode instanceof IEventTask)) {
            return (((IEventTask) taskTreeNode).getEventType() instanceof KeyInteraction);
        }
        else {
            return false;
        }
    }

    /**
     * TODO: comment
     * 
     * @param textEntryStartIndex
     * @param i
     * @param result
     * @return
     */
    private void handleTextEntrySequence(ITaskTreeNode         parent,
                                         int                   startIndex,
                                         int                   endIndex,
                                         IEventTarget          eventTarget,
                                         ITaskTreeBuilder      builder,
                                         ITaskTreeNodeFactory  nodeFactory,
                                         RuleApplicationResult result)
    {
        ITextInputEventTask textInput = nodeFactory.createNewTextInputEventTask(eventTarget);

        for (int i = startIndex; i <= endIndex; i++) {
            builder.addChild(textInput, parent.getChildren().get(startIndex));
            builder.removeChild((ISequence) parent, startIndex);
        }

        builder.addChild((ISequence) parent, startIndex, textInput);

        StringBuffer enteredText = new StringBuffer();
        determineEnteredText(textInput, new ArrayList<VirtualKey>(), enteredText);
        textInput.setEnteredText(enteredText.toString());

        result.addNewlyCreatedParentNode(textInput);
        result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED);
    }

    /**
     * TODO: comment
     * 
     * @param eventTarget
     * @return
     */
    private boolean isDataInputEventTarget(IEventTarget eventTarget) {
        return ((eventTarget instanceof ITextField) || (eventTarget instanceof ITextArea));
    }

    /**
     * TODO: comment
     * 
     * @param sequence
     * @param enteredText
     */
    private void determineEnteredText(ITaskTreeNode    node,
                                      List<VirtualKey> pressedKeys,
                                      StringBuffer     enteredText)
    {
        if ((node instanceof ISequence) || (node instanceof ITextInputEventTask)) {
            for (ITaskTreeNode child : node.getChildren()) {
                if (child instanceof IEventTask) {
                    if (((IEventTask) child).getEventType() instanceof KeyPressed) {
                        VirtualKey key =
                            ((KeyPressed) ((IEventTask) child).getEventType()).getKey();

                        pressedKeys.add(key);

                        if (key == VirtualKey.BACK_SPACE) {
                            if (enteredText.length() > 0) {
                                enteredText.deleteCharAt(enteredText.length() - 1);
                            }
                        }
                        else if (key == VirtualKey.ENTER) {
                            // text fields only contain one line of code. Therefore the return is
                            // ignored.
                            if (!(((IEventTask) child).getEventTarget() instanceof ITextField)) {
                                enteredText.append(getCharacter(key, pressedKeys));
                            }
                        }
                        else {
                            char theChar = getCharacter(key, pressedKeys);
                            if (theChar != Character.UNASSIGNED) {
                                enteredText.append(theChar);
                            }
                        }
                    }
                    else if (((IEventTask) child).getEventType() instanceof KeyReleased) {
                        pressedKeys.remove
                            (((KeyReleased) ((IEventTask) child).getEventType()).getKey());
                    }
                }
                else {
                    determineEnteredText(child, pressedKeys, enteredText);
                }
            }
        }
    }

    /**
     * TODO: comment
     * 
     * @param key
     * @param pressedKeys
     * @return
     */
    private char getCharacter(VirtualKey key, List<VirtualKey> pressedKeys) {
        boolean numlock = false;
        boolean shift = false;
        boolean altgr = false;

        for (VirtualKey pressedKey : pressedKeys) {
            if (pressedKey.isShiftKey()) {
                shift = !shift;
            }
            else if (pressedKey == VirtualKey.ALT_GRAPH) {
                altgr = !altgr;
            }
            else if (pressedKey == VirtualKey.NUM_LOCK) {
                numlock = !numlock;
            }
        }

        return keyboardMap.getCharacterFor(key, numlock, shift, altgr, false);
    }

}
