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

import de.ugoe.cs.autoquest.eventcore.IEventTarget;
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 subdivides long sequences into subsequences of tasks on the same event target.
 * Event targets are compared using the equals method. A more complex GUI model is ignored.
 * 
 * @version $Revision: $ $Date: 18.03.2012$
 * @author 2012, last modified by $Author: patrick$
 */
public class DefaultEventTargetSequenceDetectionRule 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();

        IEventTarget currentEventTarget = null;
        int startingIndex = -1;

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

            IEventTarget eventTarget = determineEventTarget(child);

            if (((eventTarget == null) && (currentEventTarget != null)) ||
                ((eventTarget != null) && (!eventTarget.equals(currentEventTarget))))
            {
                if (startingIndex < 0) {
                    startingIndex = index;
                    currentEventTarget = eventTarget;
                }
                else {
                    int endIndex = index - 1;
                    
                    // only reduce to a sequence, if it is not a sequence with only one child
                    // or if this child is not a sequence itself
                    if ((startingIndex != endIndex) ||
                        (!(parent.getChildren().get(startingIndex) instanceof ISequence)))
                    {
                        handleEventTargetSequence
                            (parent, startingIndex, endIndex, builder, nodeFactory, result);

                        result.setRuleApplicationStatus
                            (RuleApplicationStatus.RULE_APPLICATION_FINISHED);
                        return result;
                    }
                    else {
                        // here a new sequence on a new target begins
                        startingIndex = index;
                        currentEventTarget = eventTarget;
                    }
                }
            }

            index++;
        }

        if (startingIndex > -1) {
            int endIndex = parent.getChildren().size() - 1;
            
            if (finalize) {
                // only reduce to a sequence, if it is not a sequence with only one child
                // or if this child is not a sequence itself
                if ((startingIndex > 0) &&
                    ((startingIndex != endIndex) ||
                     (!(parent.getChildren().get(startingIndex) instanceof ISequence))))
                {
                    handleEventTargetSequence
                        (parent, startingIndex, endIndex, builder, nodeFactory, result);
                
                    result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED);
                }
            }
            else {
                result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
            }
        }

        return result;
    }

    /**
     * TODO: comment
     * 
     * @param child
     * @return
     */
    private IEventTarget determineEventTarget(ITaskTreeNode node) {
        if (node instanceof IEventTask) {
            return ((IEventTask) node).getEventTarget();
        }
        else {
            IEventTarget commonTarget = null;
            
            for (ITaskTreeNode child : node.getChildren()) {
                if (commonTarget == null) {
                    commonTarget = determineEventTarget(child);
                }
                else {
                    if (!commonTarget.equals(determineEventTarget(child))) {
                        return null;
                    }
                }
            }
            
            return commonTarget;
        }
    }

    /**
     * TODO: comment
     * 
     */
    private void handleEventTargetSequence(ITaskTreeNode         parent,
                                           int                   startIndex,
                                           int                   endIndex,
                                           ITaskTreeBuilder      builder,
                                           ITaskTreeNodeFactory  nodeFactory,
                                           RuleApplicationResult result)
    {
        ISequence sequence = nodeFactory.createNewSequence();

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

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

        result.addNewlyCreatedParentNode(sequence);
    }

}
