//-------------------------------------------------------------------------------------------------
// Module    : $RCSfile: TaskTreeManager.java,v $
// Version   : $Revision: 0.0 $  $Author: Patrick $  $Date: 06.11.2011 10:14:21 $
// Project   : TaskTreePerformanceTest
// Creation  : 2011 by Patrick
// Copyright : Patrick Harms, 2011
//-------------------------------------------------------------------------------------------------

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

import java.util.Observable;
import java.util.Observer;
import java.util.logging.Logger;

import de.ugoe.cs.quest.tasktrees.treeifc.InteractionTask;
import de.ugoe.cs.quest.tasktrees.treeifc.Sequence;
import de.ugoe.cs.quest.tasktrees.treeifc.TaskTree;
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.tasktree.guimodel.GUIElement;
import de.ugoe.cs.tasktree.userinteraction.InteractionEvent;
import de.ugoe.cs.tasktree.userinteraction.KeyInteraction;
import de.ugoe.cs.tasktree.userinteraction.KeyboardFocusChange;
import de.ugoe.cs.tasktree.userinteraction.UserInteractionProvider;

//-------------------------------------------------------------------------------------------------
/**
 * TODO comment
 *
 * @version $Revision: $ $Date: $
 * @author  2011, last modified by $Author: $
 */
//-------------------------------------------------------------------------------------------------
public class TaskTreeManager implements Observer
{
  /** */
  private static final int MAX_INTERACTIONS_TILL_RULE_APPLICATION = 100;

  /** */
  private static Logger LOG = Logger.getLogger(TaskTreeManager.class.getName());
  
  /** */
  private TaskTreeBuilder mTaskTreeBuilder = ComponentManager.getDefaultTaskTreeBuilder();
  
  /** */
  private TaskTreeNodeFactory mTaskTreeNodeFactory =
    ComponentManager.getDefaultTaskTreeNodeFactory();
  
  /** */
  private int mInteractionsTillRuleApplication = MAX_INTERACTIONS_TILL_RULE_APPLICATION;
  
  /** */
  private Sequence mRootSequence;
  
  /** */
  private GUIElement mCurrentKeyboardFocusGUIElement;

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   */
  //-----------------------------------------------------------------------------------------------
  public TaskTreeManager()
  {
    mRootSequence = mTaskTreeNodeFactory.createNewSequence();
  }

  //-----------------------------------------------------------------------------------------------
  /* (non-Javadoc)
   * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
   */
  //-----------------------------------------------------------------------------------------------
  public void update(Observable observable, Object event)
  {
    if ((observable instanceof UserInteractionProvider) &&
        (event instanceof InteractionEvent))
    {
      handleNewInteractionEvent((InteractionEvent) event);
    }
    else
    {
      LOG.warning("could not handle notification of " + observable + " regarding " + event);
    }
  }
  
  //-----------------------------------------------------------------------------------------------
  /**
   *
   */
  //-----------------------------------------------------------------------------------------------
  public void handleNewInteractionEvent(InteractionEvent event)
  {
    if (event.getInteraction() instanceof KeyInteraction)
    {
      if (mCurrentKeyboardFocusGUIElement == null)
      {
        mCurrentKeyboardFocusGUIElement = event.getGUIElement();
      }
      
      addInteractionTask(mTaskTreeNodeFactory.createNewInteractionTask
                           (mCurrentKeyboardFocusGUIElement, event.getInteraction()));
    }
    else
    {
      addInteractionTask(mTaskTreeNodeFactory.createNewInteractionTask
                           (event.getGUIElement(), event.getInteraction()));
    }
  }

  //-----------------------------------------------------------------------------------------------
  /**
   *
   */
  //-----------------------------------------------------------------------------------------------
  public synchronized TaskTree getTaskTree()
  {
    LOG.info("applying rules temporal relationship generation rules");
    
    Sequence rootSequence = mRootSequence.clone();
    ComponentManager.getTemporalRelationshipRuleManager().applyRules
      (rootSequence, mTaskTreeBuilder, mTaskTreeNodeFactory, true);

    return mTaskTreeNodeFactory.createTaskTree(rootSequence);
  }
  
  //-----------------------------------------------------------------------------------------------
  /**
   * @param task
   */
  //-----------------------------------------------------------------------------------------------
  private synchronized void addInteractionTask(InteractionTask task)
  {
    handleInteractionTask(task);
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * adds the task to the current or the new sequence. The decision depends on the type
   * of task. If the task finishes the current sequence, the sequence is marked as finished
   * If the task does not start a new sequence, it is added to the current sequence, before it
   * is marked s finished. Otherwise it is added to a new sequence.
   */
  //-----------------------------------------------------------------------------------------------
  private void handleInteractionTask(InteractionTask interactionTask)
  {
    if (interactionTask.getInteraction() instanceof KeyboardFocusChange)
    {
      mCurrentKeyboardFocusGUIElement = interactionTask.getGUIElement();
    }
    else
    {
      LOG.info("handling interaction task \"" + interactionTask + "\"");
      addTaskToSequence(interactionTask, mCurrentKeyboardFocusGUIElement);
    }
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param interactionTask
   */
  //-----------------------------------------------------------------------------------------------
  /*private boolean handleAndCorrectFocusChanges(InteractionTask interactionTask)
  {
    GUIElement newGUIElement = interactionTask.getGUIElement();
    
    // find the identical parent element
    GUIElement identicalParentGUIElement = null;
    
    while ((identicalParentGUIElement == null) && (newGUIElement != null))
    {
      GUIElement currentGUIElement = mCurrentGUIElement;
      
      while ((identicalParentGUIElement == null) && (currentGUIElement != null))
      {
        if (newGUIElement.equals(currentGUIElement))
        {
          identicalParentGUIElement = newGUIElement;
        }
        else
        {
          currentGUIElement = currentGUIElement.getParent();
        }
      }
      
      newGUIElement = newGUIElement.getParent();
    }
    
    // now create focus lost interactions for each GUI element, that is not common with the
    // hierarchy of the new GUI element
    GUIElement currentGUIElement = mCurrentGUIElement;
    
    List<InteractionTask> tasksToBeAdded = new ArrayList<InteractionTask>();
    
    while ((currentGUIElement != null) &&
           ((identicalParentGUIElement == null) ||
            (!currentGUIElement.equals(identicalParentGUIElement))))
    {
      tasksToBeAdded.add
        (mTaskTreeNodeFactory.createNewInteractionTask(currentGUIElement, new FocusLost()));
      currentGUIElement = currentGUIElement.getParent();
    }
    
    // now create focus received interactions for each GUI element, that is not common with
    // with the old one. Ensure, that if the current interaction is a focus reception, that
    // it is used instead of a generated one
    newGUIElement = interactionTask.getGUIElement();
    
    int index = tasksToBeAdded.size();
    while ((newGUIElement != null) &&
           ((identicalParentGUIElement == null) ||
            (!newGUIElement.equals(identicalParentGUIElement))))
    {
      tasksToBeAdded.add(index, mTaskTreeNodeFactory.createNewInteractionTask
                           (newGUIElement, new FocusReceived()));
      newGUIElement = newGUIElement.getParent();
    }
    
    // this part ensures, that the original focus reception, if any, is preserved as is.
    boolean taskAlreadyHandled = false;
    if (interactionTask.getInteraction() instanceof FocusReceived)
    {
      if (tasksToBeAdded.size() > 0)
      {
        tasksToBeAdded.set(tasksToBeAdded.size() - 1, interactionTask);
      }
      //else
      //{
        // in this case, we already have focus on the element to which the focus shall be changed.
        // therefore, we discard the new focus change on the same element.
      //}
      
      taskAlreadyHandled = true;
    }
    
    // now that all tasks are determined, add them to the sequence
    for (InteractionTask task : tasksToBeAdded)
    {
      addTaskToSequence(task);
    }
    
    mCurrentGUIElement = interactionTask.getGUIElement();
    
    return taskAlreadyHandled;
  }*/

  //-----------------------------------------------------------------------------------------------
  /**
   *
   */
  //-----------------------------------------------------------------------------------------------
  private void addTaskToSequence(TaskTreeNode task, GUIElement currentKeyboardFocusGUIElement)
  {
    mTaskTreeBuilder.addChild(mRootSequence, task);
    
    if (--mInteractionsTillRuleApplication == 0)
    {
      mInteractionsTillRuleApplication = MAX_INTERACTIONS_TILL_RULE_APPLICATION;
      
      LOG.info("applying rules temporal relationship generation rules");
      ComponentManager.getTemporalRelationshipRuleManager().applyRules
        (mRootSequence, mTaskTreeBuilder, mTaskTreeNodeFactory, false);
    }
  }

}
