//   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.LinkedList;
import java.util.List;

import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
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;

/**
 * <p>
 * TODO comment
 * </p>
 * 
 * @author Patrick Harms
 */
class SimpleIterationDetectionRule 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>
     * the node comparator used for comparing task tree nodes with each other
     * </p>
     */
    private TaskTreeNodeComparator nodeComparator;

    /**
     * <p>
     * instantiates the rule and initializes it with a node equality rule manager and the minimal
     * node equality identified sublist must have to consider them as iterated.
     * </p>
     */
    SimpleIterationDetectionRule(NodeEqualityRuleManager nodeEqualityRuleManager,
                                 NodeEquality            minimalNodeEquality,
                                 ITaskTreeNodeFactory    taskTreeNodeFactory,
                                 ITaskTreeBuilder        taskTreeBuilder)
    {
        this.taskTreeNodeFactory = taskTreeNodeFactory;
        this.taskTreeBuilder = taskTreeBuilder;
        
        this.nodeComparator =
            new TaskTreeNodeComparator(nodeEqualityRuleManager, minimalNodeEquality);
    }

    /**
     * <p>
     * instantiates the rule and initializes it with a node equality rule manager and the minimal
     * node equality identified sublist must have to consider them as iterated.
     * </p>
     */
    SimpleIterationDetectionRule(TaskTreeNodeComparator nodeComparator,
                                 ITaskTreeNodeFactory   taskTreeNodeFactory,
                                 ITaskTreeBuilder       taskTreeBuilder)
    {
        this.nodeComparator = nodeComparator;
        this.taskTreeNodeFactory = taskTreeNodeFactory;
        this.taskTreeBuilder = taskTreeBuilder;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "SimpleIterationDetectionRule";
    }

    /*
     * (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;
        }
        
        if (!finalize) {
            // this rule can only finalize whole trees
            return null;
        }

        RuleApplicationResult result = new RuleApplicationResult();

        applyOn(parent, result);
        
        if (result.getNewlyCreatedParentNodes().size() > 0) {
            result.setRuleApplicationStatus(RuleApplicationStatus.FINISHED);
        }

        return result;
    }

    /**
     *
     */
    private void applyOn(ITaskTreeNode node, RuleApplicationResult result) {
        int iterationStartIndex = -1;
        ITaskTreeNode iteratedChild = null;

        int index = 0;
        List<ITaskTreeNode> children = node.getChildren();
        
        while (index < children.size()) {
            ITaskTreeNode child = children.get(index);

            if (iteratedChild == null) {
                // new iteration may start
                iterationStartIndex = index;
                iteratedChild = child;
            }
            else {
                if (!nodeComparator.equals(iteratedChild, child)) {
                    // iteration finished
                    handleIteration(node, iterationStartIndex, index - 1, result);
                    
                    // children may have changed
                    children = node.getChildren();

                    // new iteration may start
                    index = iterationStartIndex + 1;
                    iterationStartIndex = index;
                    iteratedChild = child;
                }
            }
            index++;
        }
        
        if (iterationStartIndex > -1) {
            handleIteration(node, iterationStartIndex, children.size() - 1, result);
        }
    }

    /**
     *
     */
    private void handleIteration(ITaskTreeNode         parent,
                                 int                   startIndex,
                                 int                   endIndex,
                                 RuleApplicationResult result)
    {
        if (startIndex == endIndex) {
            // only one child
            return;
        }
        
        IIteration iteration = taskTreeNodeFactory.createNewIteration();
        result.addNewlyCreatedParentNode(iteration);

        List<ITaskTreeNode> children = parent.getChildren();
        
        List<ITaskTreeNode> equalChildren = new LinkedList<ITaskTreeNode>();
            
        for (int i = endIndex - startIndex; i >= 0; i--) {
             equalChildren.add(children.get(startIndex));
             taskTreeBuilder.removeChild((ISequence) parent, startIndex);
        }
            
        // merge the identified variants, but preserve the differences in form of selections
        // by using lexical equality for merge comparisons
        TaskTreeNodeMerger merger = new TaskTreeNodeMerger
            (taskTreeNodeFactory, taskTreeBuilder, nodeComparator);

        merger.mergeTaskNodes(equalChildren);

        if (equalChildren.size() == 1) {
            taskTreeBuilder.setChild(iteration, equalChildren.get(0));
            taskTreeBuilder.setDescription(iteration, "several " + equalChildren.get(0));
        }
        else {
            taskTreeBuilder.setDescription(iteration, "several " + equalChildren.get(0));

            // create a selection of all variants
            ISelection selection = taskTreeNodeFactory.createNewSelection();
            result.addNewlyCreatedParentNode(selection);
            taskTreeBuilder.setChild(iteration, selection);
            taskTreeBuilder.setDescription(selection, "variants");

            for (ITaskTreeNode variant : equalChildren) {
                taskTreeBuilder.addChild(selection, variant);
            }
        }

        taskTreeBuilder.addChild((ISequence) parent, startIndex, iteration);
    }

}
