//   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.plugin.exports.tasktree;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import de.ugoe.cs.autoquest.exports.tasktrees.Category;
import de.ugoe.cs.autoquest.exports.tasktrees.ObjectFactory;
import de.ugoe.cs.autoquest.exports.tasktrees.Parent;
import de.ugoe.cs.autoquest.exports.tasktrees.SiblingLeft;
import de.ugoe.cs.autoquest.exports.tasktrees.SiblingRight;
import de.ugoe.cs.autoquest.exports.tasktrees.SubTask;
import de.ugoe.cs.autoquest.exports.tasktrees.Task;
import de.ugoe.cs.autoquest.exports.tasktrees.TaskModel;
import de.ugoe.cs.autoquest.exports.tasktrees.TemporalOperator;
import de.ugoe.cs.autoquest.exports.tasktrees.TemporalOperatorName;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
import de.ugoe.cs.util.console.Console;

/**
 * <p>
 * TODO comment
 * </p>
 * 
 * @author Patrick Harms
 */
public class CTTExportUtils {

    /** */
    private static final ObjectFactory objFact = new ObjectFactory();
    
    /**
     * 
     */
    public static void exportToCTT(ITask task, String filename) {
        TaskModel cttModel = objFact.createTaskModel();
        cttModel.setTask(CTTExportUtils.map(task, false, false, null));
        cttModel.setNameTaskModelID(Integer.toString(task.getId()));

        writeToFile(cttModel, new File(filename));
    }
    

    /**
     *
     */
    public static Task map(ITask            task,
                           boolean          isIterative,
                           boolean          isOptional,
                           TemporalOperator operator)
    {
        if (task instanceof ISequence) {
            return map((ISequence) task, isIterative, isOptional, operator);
        }
        else if (task instanceof ISelection) {
            return map((ISelection) task, isIterative, isOptional, operator);
        }
        else if (task instanceof IOptional) {
            return map((IOptional) task, isIterative, operator);
        }
        else if (task instanceof IIteration) {
            return map((IIteration) task, isOptional, operator);
        }
        else if (task instanceof IEventTask) {
            return map((IEventTask) task, isIterative, isOptional, operator);
        }
        else {
            throw new IllegalArgumentException("unknown type of task: " + task);
        }
    }

    /**
     *
     */
    public static void writeToFile(TaskModel model, File file) {
        String taskId = model.getNameTaskModelID();
        
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            
            JAXBContext jaxbContext =
                JAXBContext.newInstance(TaskModel.class.getPackage().getName());
            Marshaller marshaller = jaxbContext.createMarshaller();

            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
            marshaller.marshal(model, out);
            Console.traceln(Level.INFO, "wrote model of " + taskId + " to file");
        }
        catch (JAXBException | IOException e) {
            Console.traceln(Level.WARNING, "could not write model of " + taskId + " to file: " + e);
            Console.logException(e);
        }
        finally {
            if (out != null) {
                try {
                    out.close();
                }
                catch (Exception e) {
                    // ignore
                }
            }
        }
    }

    /**
     *
     */
    private static Task map(IEventTask       task,
                            boolean          isIterative,
                            boolean          isOptional,
                            TemporalOperator operator)
    {
        return createTask(task, Category.INTERACTION, isIterative, isOptional, operator);
    }

    /**
     *
     */
    private static Task map(ISequence        task,
                            boolean          isIterative,
                            boolean          isOptional,
                            TemporalOperator operator)
    {
        Task result = createTask(task, Category.ABSTRACTION, isIterative, isOptional, operator);
        
        // handle the children
        TemporalOperator operatorForChildren = objFact.createTemporalOperator();
        operatorForChildren.setName(TemporalOperatorName.SEQUENTIAL_ENABLING);
        
        SubTask subTask = objFact.createSubTask();
        List<ITask> children = task.getChildren();
        
        Task precedingChild = null;
        for (int i = 0; i < children.size(); i++) {
            Task child = map(children.get(i), false, false,
                             ((i < (children.size() - 1)) ? operatorForChildren : null));
            
            ensureParentAndSibling(result, child, precedingChild);
            
            subTask.getTask().add(child);
            precedingChild = child;
        }
        
        result.setSubTask(subTask);
        
        return result;
    }

    /**
     *
     */
    private static Task map(ISelection       task,
                            boolean          isIterative,
                            boolean          isOptional,
                            TemporalOperator operator)
    {
        Task result = createTask(task, Category.ABSTRACTION, isIterative, isOptional, operator);
        
        // handle the children
        TemporalOperator operatorForChildren = objFact.createTemporalOperator();
        operatorForChildren.setName(TemporalOperatorName.CHOICE);
        
        SubTask subTask = objFact.createSubTask();
        List<ITask> children = task.getChildren();
        
        Task precedingChild = null;
        for (int i = 0; i < children.size(); i++) {
            Task child = map(children.get(i), false, false,
                             ((i < (children.size() - 1)) ? operatorForChildren : null));
            
            ensureParentAndSibling(result, child, precedingChild);
            
            subTask.getTask().add(child);
            precedingChild = child;
        }
        
        result.setSubTask(subTask);
        
        return result;
    }

    /**
     *
     */
    private static Task map(IIteration       task,
                            boolean          isOptional,
                            TemporalOperator operator)
    {
        return map(task.getMarkedTask(), true, isOptional, operator);
    }

    /**
     *
     */
    private static Task map(IOptional        task,
                            boolean          isIterative,
                            TemporalOperator operator)
    {
        return map(task.getMarkedTask(), isIterative, true, operator);
    }
    
    /**
     * 
     */
    private static Task createTask(ITask            task,
                                   Category         category,
                                   boolean          isIterative,
                                   boolean          isOptional,
                                   TemporalOperator operator)
    {
        Task result = objFact.createTask();
        
        // set the attributes
        result.setIdentifier(task.getType() + " " + task.getId());
        result.setCategory(category);
        result.setIterative(isIterative);
        result.setOptional(isOptional);
        result.setPartOfCooperation(false);
        result.setFrequency(null);
        
        // set the child tags
        result.setName(task.getType() + " " + task.getId());
        
        if (operator != null) {
            result.setTemporalOperator(operator);
        }
        
        return result;
    }

    /**
     *
     */
    private static void ensureParentAndSibling(Task result, Task child, Task precedingChild) {
        // set the parent
        Parent parent = objFact.createParent();
        parent.setName(result.getName());
        child.setParent(parent);
        
        // set previous sibling, if any
        if (precedingChild != null) {
            SiblingLeft siblingLeft = objFact.createSiblingLeft();
            siblingLeft.setName(precedingChild.getName());
            child.setSiblingLeft(siblingLeft);
            
            SiblingRight siblingRight = objFact.createSiblingRight();
            siblingRight.setName(child.getName());
            precedingChild.setSiblingRight(siblingRight);
        }
    }
    
    /**
     * 
     */
    private CTTExportUtils() {
        // prevent instantiation
    }
}
