//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

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

import java.util.List;

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$
 */
class SequenceOnSameTargetDetectionRule implements TemporalRelationshipRule {

    /**
     * <p>
     * the task tree node factory to be used for creating substructures for the temporal
     * relationships identified during rule
     * </p>
     */
    private ITaskTreeNodeFactory taskTreeNodeFactory;
    /**
     * <p>
     * the task tree builder to be used for creating substructures for the temporal relationships
     * identified during rule application
     * </p>
     */
    private ITaskTreeBuilder taskTreeBuilder;
    
    /**
     * <p>
     * instantiates the rule with a task tree node factory and builder to be used during rule
     * application.
     * </p>
     * 
     * @param taskTreeNodeFactory the task tree node factory to be used for creating substructures
     *                            for the temporal relationships identified during rule
     *                            application
     * @param taskTreeBuilder     the task tree builder to be used for creating substructures for
     *                            the temporal relationships identified during rule application
     */
    SequenceOnSameTargetDetectionRule(ITaskTreeNodeFactory taskTreeNodeFactory,
                                      ITaskTreeBuilder     taskTreeBuilder)
    {
        this.taskTreeNodeFactory = taskTreeNodeFactory;
        this.taskTreeBuilder = taskTreeBuilder;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "SequenceOnSameTargetDetectionRule";
    }

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

        RuleApplicationResult result = new RuleApplicationResult();

        IEventTarget currentEventTarget = null;
        int startingIndex = -1;

        int index = 0;
        List<ITaskTreeNode> children = parent.getChildren();
        
        while (index < children.size()) {
            ITaskTreeNode child = children.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) ||
                        (!(children.get(startingIndex) instanceof ISequence)))
                    {
                        handleEventTargetSequence
                            (parent, currentEventTarget, startingIndex, endIndex, result);

                        result.setRuleApplicationStatus(RuleApplicationStatus.FINISHED);
                        return result;
                    }
                    else if (eventTarget != null) {
                        // here a new sequence on a new target begins
                        startingIndex = index;
                        currentEventTarget = eventTarget;
                    }
                    else {
                        startingIndex = -1;
                        currentEventTarget = null;
                    }
                }
            }

            index++;
        }

        if (startingIndex > -1) {
            int endIndex = children.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) ||
                     (!(children.get(startingIndex) instanceof ISequence))))
                {
                    handleEventTargetSequence
                        (parent, currentEventTarget, startingIndex, endIndex, result);
                
                    result.setRuleApplicationStatus(RuleApplicationStatus.FINISHED);
                }
            }
            else {
                result.setRuleApplicationStatus(RuleApplicationStatus.FEASIBLE);
            }
        }

        return result;
    }

    /**
     *
     */
    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;
        }
    }

    /**
     *
     */
    private void handleEventTargetSequence(ITaskTreeNode         parent,
                                           IEventTarget          target,
                                           int                   startIndex,
                                           int                   endIndex,
                                           RuleApplicationResult result)
    {
        String description = "interactions on " + target.getStringIdentifier();
        
        ISequence sequence = RuleUtils.createNewSubSequenceInRange
            (parent, startIndex, endIndex, description, taskTreeNodeFactory, taskTreeBuilder);

        result.addNewlyCreatedParentNode(sequence);
    }

}
