//-------------------------------------------------------------------------------------------------
// Module    : $RCSfile: DefaultSequenceDetectionRule.java,v $
// Version   : $Revision: 0.0 $  $Author: patrick $  $Date: 18.03.2012 $
// Project   : TaskTreeCreator
// Creation  : 2012 by patrick
// Copyright : Patrick Harms, 2012
//-------------------------------------------------------------------------------------------------
package de.ugoe.cs.quest.tasktrees.temporalrelation;

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

import de.ugoe.cs.quest.tasktrees.treeifc.InteractionTask;
import de.ugoe.cs.quest.tasktrees.treeifc.Sequence;
import de.ugoe.cs.quest.tasktrees.treeifc.TaskTreeBuilder;
import de.ugoe.cs.quest.tasktrees.treeifc.TaskTreeNode;
import de.ugoe.cs.quest.tasktrees.treeifc.TaskTreeNodeFactory;
import de.ugoe.cs.quest.tasktrees.treeifc.TextInputInteractionTask;
import de.ugoe.cs.tasktree.guimodel.GUIElement;
import de.ugoe.cs.tasktree.guimodel.TextArea;
import de.ugoe.cs.tasktree.guimodel.TextField;
import de.ugoe.cs.tasktree.keyboardmaps.KeyboardMap;
import de.ugoe.cs.tasktree.keyboardmaps.KeyboardMapFactory;
import de.ugoe.cs.tasktree.keyboardmaps.VirtualKey;
import de.ugoe.cs.tasktree.userinteraction.KeyInteraction;
import de.ugoe.cs.tasktree.userinteraction.KeyPressed;
import de.ugoe.cs.tasktree.userinteraction.KeyReleased;

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

  //-----------------------------------------------------------------------------------------------
  /* (non-Javadoc)
   * @see TemporalRelationshipRule#apply(TaskTreeNode, TaskTreeBuilder, TaskTreeNodeFactory)
   */
  //-----------------------------------------------------------------------------------------------
  @Override
  public RuleApplicationResult apply(TaskTreeNode        parent,
                                     TaskTreeBuilder     builder,
                                     TaskTreeNodeFactory nodeFactory,
                                     boolean             finalize)
  {
    if ((!(parent instanceof Sequence)) ||
        (parent instanceof TextInputInteractionTask))
    {
      return null;
    }
    
    RuleApplicationResult result = new RuleApplicationResult();
    int textEntryStartIndex = -1;
    GUIElement currentGUIElement = null;
    
    int index = 0;
    TaskTreeNode task = null;
    while (index < parent.getChildren().size())
    {
      task = parent.getChildren().get(index);
      if (isKeyInteraction(task) && isDataInputGUIElement(((InteractionTask) task).getGUIElement()))
      {
        if (textEntryStartIndex < 0)
        {
          textEntryStartIndex = index;
          currentGUIElement = ((InteractionTask) task).getGUIElement();
        }
        else if (!currentGUIElement.equals(((InteractionTask) task).getGUIElement()))
        {
           handleTextEntrySequence(parent, textEntryStartIndex, index - 1, currentGUIElement,
                                   builder, nodeFactory, result);
           return result;
        }
      }
      else if (textEntryStartIndex >= 0)
      {
        handleTextEntrySequence(parent, textEntryStartIndex, index - 1, currentGUIElement,
                                builder, nodeFactory, result);
        return result;
      }
      
      index++;
    }
    
    if (textEntryStartIndex >= 0)
    {
      if (finalize)
      {
        handleTextEntrySequence(parent, textEntryStartIndex, parent.getChildren().size() - 1,
                                currentGUIElement, builder, nodeFactory, result);
      }
      else
      {
        result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
      }
    }
    
    return result;
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param taskTreeNode
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private boolean isKeyInteraction(TaskTreeNode taskTreeNode)
  {
    if ((taskTreeNode instanceof InteractionTask))
    {
      return (((InteractionTask) taskTreeNode).getInteraction() instanceof KeyInteraction);
    }
    else
    {
      return false;
    }
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param textEntryStartIndex
   * @param i
   * @param result
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private void handleTextEntrySequence(TaskTreeNode          parent,
                                       int                   startIndex,
                                       int                   endIndex,
                                       GUIElement            guiElement,
                                       TaskTreeBuilder       builder,
                                       TaskTreeNodeFactory   nodeFactory,
                                       RuleApplicationResult result)
  {
    TextInputInteractionTask textInput = nodeFactory.createNewTextInputInteractionTask(guiElement);
    
    for (int i = startIndex; i <= endIndex; i++)
    {
      builder.addChild(textInput, parent.getChildren().get(startIndex));
      builder.removeChild((Sequence) parent, startIndex);
    }
    
    builder.addChild((Sequence) 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 guiElement
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private boolean isDataInputGUIElement(GUIElement guiElement)
  {
    return ((guiElement instanceof TextField) || (guiElement instanceof TextArea));
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param sequence
   * @param enteredText
   */
  //-----------------------------------------------------------------------------------------------
  private void determineEnteredText(TaskTreeNode     node,
                                    List<VirtualKey> pressedKeys,
                                    StringBuffer     enteredText)
  {
    if ((node instanceof Sequence) || (node instanceof TextInputInteractionTask))
    {
      for (TaskTreeNode child : node.getChildren())
      {
        if (child instanceof InteractionTask)
        {
          if (((InteractionTask) child).getInteraction() instanceof KeyPressed)
          {
            VirtualKey key = ((KeyPressed) ((InteractionTask) child).getInteraction()).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 (!(((InteractionTask) child).getGUIElement() instanceof TextField))
              {
                enteredText.append(getCharacter(key, pressedKeys));
              }
            }
            else
            {
              char theChar = getCharacter(key, pressedKeys);
              if (theChar != Character.UNASSIGNED)
              {
                enteredText.append(theChar);
              }
            }
          }
          else if (((InteractionTask) child).getInteraction() instanceof KeyReleased)
          {
            pressedKeys.remove(((KeyReleased) ((InteractionTask) child).getInteraction()).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 mKeyboardMap.getCharacterFor(key, numlock, shift, altgr, false);
  }

}
