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

import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;

/**
 * This rule generates sequences of events depending on the event types, more concrete, the
 * {@link IInteraction}s and the return values of their {@link IInteraction#startsLogicalSequence()}
 * and {@link IInteraction#finishesLogicalSequence()}. If a new logical sequence is started by
 * an interaction, then a real sequence is instantiated. The sequence is finished, if an
 * interaction finishes a logical sequence. Examples include keyboard focus changes.
 * 
 * @version $Revision: $ $Date: 18.03.2012$
 * @author 2012, last modified by $Author: patrick$
 */
public class DefaultGuiEventSequenceDetectionRule implements TemporalRelationshipRule {

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

        RuleApplicationResult result = new RuleApplicationResult();
        int sequenceStartingIndex = -1;

        int index = 0;
        while (index < parent.getChildren().size()) {
            ITaskTreeNode child = parent.getChildren().get(index);

            if ((child instanceof IEventTask) &&
                (((IEventTask) child).getEventType() instanceof IInteraction))
            {
                IInteraction eventType = (IInteraction) ((IEventTask) child).getEventType();
                
                if (eventType.finishesLogicalSequence() && (sequenceStartingIndex > -1))
                {
                    // There are several situations in which this implementation may cause infinite
                    // loops. This is because the rule manager will reapply rules until
                    // no rule is applied anymore. A sequence identified in a first iteration will
                    // be identified as a sequence also in a second iteration. As an example
                    // many sequences start with an interaction starting that sequence and end
                    // with an interaction ending that sequence. This will be reidentified as
                    // further subsequence. It must therefore be assured, that a sequence, that
                    // was once identified is not reidentified in a further application of the rule.
                    // For this, the implementation performs a kind of dry run. It creates a list of
                    // children that would belong to an identified sequence. Only if this list is
                    // not a reidentification then a new sequence is created and added to the
                    // parent. If it is a reidentification can be identified, if the list of
                    // children will contain all children of the parent, or if the list of children
                    // only consists of one sequence. Further, an identified sequence must at least
                    // have one child.
                    if (((sequenceStartingIndex != 0) ||
                         (index != (parent.getChildren().size() - 1))) &&
                        (((index - sequenceStartingIndex) > 0) ||
                          (((index - sequenceStartingIndex) == 0) &&
                           (!eventType.startsLogicalSequence()))))
                    {
                        boolean allNewChildrenAreSequences = true;

                        for (int j = sequenceStartingIndex;
                             ((allNewChildrenAreSequences) && (j < index)); j++)
                        {
                            allNewChildrenAreSequences &=
                                (parent.getChildren().get(j) instanceof ISequence);
                        }

                        if (!allNewChildrenAreSequences) {
                            ISequence sequence = nodeFactory.createNewSequence();

                            for (int j = sequenceStartingIndex; j < index; j++) {
                                builder.addChild
                                    (sequence, parent.getChildren().get(sequenceStartingIndex));
                                builder.removeChild((ISequence) parent, sequenceStartingIndex);
                            }

                            if (!eventType.startsLogicalSequence()) {
                                builder.addChild
                                    (sequence, parent.getChildren().get(sequenceStartingIndex));
                                builder.removeChild((ISequence) parent, sequenceStartingIndex);
                            }

                            builder.addChild((ISequence) parent, sequenceStartingIndex, sequence);

                            result.addNewlyCreatedParentNode(sequence);
                            result.setRuleApplicationStatus
                                (RuleApplicationStatus.RULE_APPLICATION_FINISHED);
                            return result;
                        }
                    }
                }

                if (eventType.startsLogicalSequence()) {
                    sequenceStartingIndex = index;
                }
            }

            index++;
        }

        if (sequenceStartingIndex >= 0) {
            result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
        }

        return result;
    }

}
