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

import org.junit.Before;

import de.ugoe.cs.autoquest.tasktrees.TaskTreeChecker;
import de.ugoe.cs.autoquest.tasktrees.TaskTreeDecoder;
import de.ugoe.cs.autoquest.tasktrees.TaskTreeEncoder;
import de.ugoe.cs.autoquest.tasktrees.TaskTreeValidator;
import de.ugoe.cs.autoquest.tasktrees.taskequality.TaskEquality;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskFactory;
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 ITaskBuilder taskBuilder = new TaskBuilder();

    /** */
    private ITaskFactory taskFactory = new TaskFactory();
    
    /** */
    private TaskTreeDecoder decoder = null;

    /** */
    private TaskTreeEncoder encoder = new TaskTreeEncoder();
    
    /**
     *
     */
    @Before
    public void setUp() {
        Console.reset();
        new TextConsole(Level.FINEST);
        
        decoder = new TaskTreeDecoder(taskFactory, taskBuilder);
    }

    /**
     *
     */
    protected void applyRule(Class<? extends ITaskInstanceScopeRule> ruleClass,
                             String                                  inputSpec,
                             String                                  expectedOutputSpec)
    {
        ITaskInstanceScopeRule rule = null;
        
        CONSTRUCTOR_ITERATION:
        for (Constructor<?> constructor : ruleClass.getDeclaredConstructors()) {
            List<Object> parameters = new LinkedList<Object>();
            
            for (Class<?> type : constructor.getParameterTypes()) {
                if (ITaskFactory.class.equals(type)) {
                    parameters.add(taskFactory);
                }
                else if (ITaskBuilder.class.equals(type)) {
                    parameters.add(taskBuilder);
                }
                else if (TaskEquality.class.equals(type)) {
                    parameters.add(TaskEquality.LEXICALLY_EQUAL);
                }
                else {
                    continue CONSTRUCTOR_ITERATION;
                }
            }
            
            try {
                rule = (ITaskInstanceScopeRule) 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;
        
        ITaskInstance inputList = (ITaskInstance) decoder.decode(inputSpec);
        
        Stack<ITaskInstance> toBeAppliedOn = new Stack<ITaskInstance>();
        toBeAppliedOn.push(inputList);
        
        do {
            result = rule.apply(toBeAppliedOn.peek());
            
            if (result != null) {
                status = result.getRuleApplicationStatus();
                assertNotNull(status);
            }
            else {
                status = RuleApplicationStatus.NOT_APPLIED;
            }
            
            if ((result != null) && (result.getNewlyCreatedTaskInstances() != null)) {
                for (int i = result.getNewlyCreatedTaskInstances().size() - 1; i >= 0; i--) {
                    toBeAppliedOn.push(result.getNewlyCreatedTaskInstances().get(i));
                }
            }
            
            if (status == RuleApplicationStatus.NOT_APPLIED) {
                toBeAppliedOn.pop();
            }
            
        }
        while ((!toBeAppliedOn.isEmpty()) || (status == RuleApplicationStatus.FINISHED));

        ITaskInstance expectedList = (ITaskInstance) decoder.decode(expectedOutputSpec);
        
        new TaskTreeChecker().assertTaskInstancesEqual(expectedList, inputList);
    }

    /**
     *
     */
    protected void applySessionScopeRule(Class<? extends ISessionScopeRule> ruleClass,
                                         String                             inputSpec,
                                         String                             expectedOutputSpec)
    {
        ISessionScopeRule rule = null;
        
        CONSTRUCTOR_ITERATION:
        for (Constructor<?> constructor : ruleClass.getDeclaredConstructors()) {
            List<Object> parameters = new LinkedList<Object>();
            
            for (Class<?> type : constructor.getParameterTypes()) {
                if (ITaskFactory.class.equals(type)) {
                    parameters.add(taskFactory);
                }
                else if (ITaskBuilder.class.equals(type)) {
                    parameters.add(taskBuilder);
                }
                else if (TaskEquality.class.equals(type)) {
                    parameters.add(TaskEquality.LEXICALLY_EQUAL);
                }
                else {
                    continue CONSTRUCTOR_ITERATION;
                }
            }
            
            try {
                rule = (ISessionScopeRule) 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);
        }
        
        ITaskInstanceList inputList = decoder.decode(inputSpec);
        
        assertTrue(inputList instanceof IUserSession);
        
        List<IUserSession> sessionList = new LinkedList<IUserSession>();
        sessionList.add((IUserSession) inputList);
        
        System.out.println("Input:");
        encoder.encode(inputList, System.out);
        
        RuleApplicationResult result = rule.apply(sessionList);
        
        assertNotNull(result);
        assertNotNull(result.getRuleApplicationStatus());
            
        ITaskInstanceList expectedList = decoder.decode(expectedOutputSpec);
        
        System.out.println("\nExpected Result:");
        encoder.encode(expectedList, System.out);
        System.out.println("\nResult:");
        encoder.encode(inputList, System.out);

        new TaskTreeChecker().assertTaskInstanceListsEqual(expectedList, inputList);
        new TaskTreeValidator().validate(inputList);
    }

}
