//   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 static org.junit.Assert.fail;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.logging.Level;

import org.junit.Before;

import de.ugoe.cs.autoquest.eventcore.IEventTarget;
import de.ugoe.cs.autoquest.eventcore.IEventType;
import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
import de.ugoe.cs.autoquest.tasktrees.testutils.Utilities;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
import de.ugoe.cs.util.console.Console;
import de.ugoe.cs.util.console.TextConsole;

/**
 * @version $Revision: $ $Date: 28.04.2012$
 * @author 2012, last modified by $Author: patrick$
 */
public class AbstractTemporalRelationshipTC {

    /** */
    private List<IEventTask> events;

    /** */
    private ITaskTreeBuilder taskTreeBuilder = new TaskTreeBuilder();

    /** */
    private ITaskTreeNodeFactory taskTreeNodeFactory = new TaskTreeNodeFactory();

    /** */
    private NodeEqualityRuleManager nodeEqualityRuleManager =
        Utilities.getNodeEqualityRuleManagerForTests();

    /**
     *
     */
    @Before
    public void setUp() {
        new TextConsole(Level.FINEST);
        events = new ArrayList<IEventTask>();
    }

    /**
     *
     */
    protected void simulateEvent(IEventType eventType, IEventTarget eventTarget) {
        events.add(taskTreeNodeFactory.createNewEventTask(eventType, eventTarget));
        Console.reset();
    }

    /**
     *
     */
    protected ITaskTree getTaskTree() {
        ISequence sequence = taskTreeNodeFactory.createNewSequence();

        for (IEventTask task : events) {
            taskTreeBuilder.addChild(sequence, task);
        }

        TemporalRelationshipRuleManager ruleManager = new TemporalRelationshipRuleManager
            (nodeEqualityRuleManager, taskTreeNodeFactory, taskTreeBuilder);

        ruleManager.init();
        ruleManager.applyRules(sequence);

        return taskTreeNodeFactory.createTaskTree(sequence);
    }

    /**
     *
     */
    protected ITaskTree getTaskTree(Class<? extends TemporalRelationshipRule> ruleClass,
                                    NodeEquality                              nodeEquality)
    {
        return getTaskTree(ruleClass, nodeEquality, false);
    }
    
    /**
     *
     */
    protected ITaskTree getTaskTree(Class<? extends TemporalRelationshipRule> ruleClass,
                                    NodeEquality                              nodeEquality,
                                    boolean                                   explicitSessionSeq)
    {
        ISequence sequence = taskTreeNodeFactory.createNewSequence();

        for (IEventTask task : events) {
            taskTreeBuilder.addChild(sequence, task);
        }

        if (explicitSessionSeq) {
            ISequence root = taskTreeNodeFactory.createNewSequence();
            taskTreeBuilder.addChild(root, sequence);
            sequence = root;
        }
        
        TemporalRelationshipRule rule = null;
        
        CONSTRUCTOR_ITERATION:
        for (Constructor<?> constructor : ruleClass.getDeclaredConstructors()) {
            List<Object> parameters = new LinkedList<Object>();
            
            for (Class<?> type : constructor.getParameterTypes()) {
                if (ITaskTreeNodeFactory.class.equals(type)) {
                    parameters.add(taskTreeNodeFactory);
                }
                else if (ITaskTreeBuilder.class.equals(type)) {
                    parameters.add(taskTreeBuilder);
                }
                else if (NodeEqualityRuleManager.class.equals(type)) {
                    parameters.add(nodeEqualityRuleManager);
                }
                else if (NodeEquality.class.equals(type)) {
                    if (nodeEquality != null) {
                        parameters.add(nodeEquality);
                    }
                    else {
                        parameters.add(NodeEquality.LEXICALLY_EQUAL);
                    }
                }
                else {
                    continue CONSTRUCTOR_ITERATION;
                }
            }
            
            try {
                rule = (TemporalRelationshipRule) constructor.newInstance(parameters.toArray());
            }
            catch (IllegalArgumentException e) {
                e.printStackTrace();
                fail("could not invoke the constructor " + constructor);
            }
            catch (InstantiationException e) {
                e.printStackTrace();
                fail("could not invoke the constructor " + constructor);
            }
            catch (IllegalAccessException e) {
                e.printStackTrace();
                fail("could not invoke the constructor " + constructor);
            }
            catch (InvocationTargetException e) {
                e.printStackTrace();
                fail("could not invoke the constructor " + constructor);
            }
        }
        
        if (rule == null) {
            fail("no matching constructor found to instantiate rule " + ruleClass);
        }
        
        RuleApplicationResult result;
        RuleApplicationStatus status;
        
        Stack<ITaskTreeNode> toBeAppliedOn = new Stack<ITaskTreeNode>();
        toBeAppliedOn.push(sequence);
        
        do {
            result = rule.apply(toBeAppliedOn.peek(), true);
            
            if (result != null) {
                status = result.getRuleApplicationStatus();
                assertNotNull(status);
            }
            else {
                status = RuleApplicationStatus.NOT_APPLIED;
            }
            
            assertTrue(status != RuleApplicationStatus.FEASIBLE);
            
            if ((result != null) && (result.getNewlyCreatedParentNodes() != null)) {
                for (int i = result.getNewlyCreatedParentNodes().size() - 1; i >= 0; i--) {
                    toBeAppliedOn.push(result.getNewlyCreatedParentNodes().get(i));
                }
            }
            
            if (status == RuleApplicationStatus.NOT_APPLIED) {
                toBeAppliedOn.pop();
            }
            
        }
        while ((!toBeAppliedOn.isEmpty()) || (status == RuleApplicationStatus.FINISHED));

        return taskTreeNodeFactory.createTaskTree(sequence);
    }

}
