//   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.utils;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITemporalRelationship;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskPath;

/**
 * Convenience class to encode user sessions, task instance lists, task instances or tasks into
 * a human readable textual representation.
 * 
 * @version $Revision: $ $Date: 01.04.2012$
 * @author 2012, last modified by $Author: patrick$
 */
public class TaskTreeEncoder {
    
    /**
     *
     */
    public void encode(List<IUserSession> userSessions)
        throws FileNotFoundException
    {
        PrintStream out = null;
        try {
            out = new PrintStream(new FileOutputStream("userSessions.txt"), true, "UTF-8");
            for (IUserSession session : userSessions) {
                encode(session, out);
            }
        }
        catch (UnsupportedEncodingException e) {
            // must not happen as the encoding should be correct
            throw new RuntimeException("programming error", e);
        }
        finally {
            if (out != null) {
                out.close();
            }
        }
    }
    
    /**
     *
     */
    public void encode(ITaskInstanceList taskInstanceList)
        throws FileNotFoundException
    {
        PrintStream out = null;
        try {
            out = new PrintStream(new FileOutputStream("taskInstanceList.txt"), true, "UTF-8");
            encode(taskInstanceList, out);
        }
        catch (UnsupportedEncodingException e) {
            // must not happen as the encoding should be correct
            throw new RuntimeException("programming error", e);
        }
        finally {
            if (out != null) {
                out.close();
            }
        }
    }
    
    /**
     *
     */
    public void encode(ITask task)
        throws FileNotFoundException
    {
        PrintStream out = null;
        try {
            out = new PrintStream(new FileOutputStream("task.txt"), true, "UTF-8");
            encode(task, out);
        }
        catch (UnsupportedEncodingException e) {
            // must not happen as the encoding should be correct
            throw new RuntimeException("programming error", e);
        }
        finally {
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     *
     */
    public void encode(ITaskInstanceList taskInstanceList, PrintStream out) {
        encode(taskInstanceList, out, null);
    }

    /**
     *
     */
    public void encode(ITaskInstanceList taskInstanceList,
                       PrintStream       out,
                       List<TaskPath>    excludes)
    {
        TaskPath path = new TaskPath();
        if (taskInstanceList instanceof IUserSession) {
            out.println("UserSession {");
        }
        else {
            out.println("TaskInstances {");
        }
        
        if (taskInstanceList instanceof ITaskInstance) {
            path.add(((ITaskInstance) taskInstanceList).getTask(), 0);
        }
        
        int i = 0;
        for (ITaskInstance taskInstance : taskInstanceList) {
            encode(taskInstance, out, "  ", i++, path, excludes);
        }
        
        out.println('}');
    }

    /**
     *
     */
    public void encode(ITaskInstance taskInstance, PrintStream out) {
        encode(taskInstance, out, null);
    }

    /**
     *
     */
    public void encode(ITaskInstance taskInstance, PrintStream out, List<TaskPath> excludes) {
        TaskPath path = new TaskPath();
        encode(taskInstance, out, "", 0, path, excludes);
    }

    /**
     *
     */
    public void encode(ITask task, PrintStream out) {
        encode(task, out, "", 0);
    }
    
    /**
     *
     */
    private void encode(ITaskInstance  taskInstance,
                        PrintStream    out,
                        String         indent,
                        int            index,
                        TaskPath       parentPath,
                        List<TaskPath> excludes)
    {
        ITask task = taskInstance.getTask();
        TaskPath path = parentPath;
        path.add(task, index);
        
        if (task instanceof ITemporalRelationship) {
            if (index > 0) {
                out.println();
            }
            out.print(indent);

            if (task instanceof ISequence) {
                out.print("Sequence ");
            }
            else if (task instanceof ISelection) {
                out.print("Selection ");
            }            
            else if (task instanceof IIteration) {
                out.print("Iteration ");
            }            
            else if (task instanceof IOptional) {
                out.print("Optional ");
            }
            
            out.print(task.getId());
            
            if (task.getDescription() != null) {
                out.print('(');
                out.print(task.getDescription());
                out.print(") ");
            }
            
            out.print('{');
            
            if ((excludes == null) || (!excludes.contains(path))) {
                out.println();
            }
        }
        else if (task instanceof IEventTask) {
            out.print(indent);
            out.print(task);
        }
        else {
            throw new RuntimeException
                ("unknown type of task referred by task instance " + taskInstance);
        }

        if ((excludes == null) || (!excludes.contains(path))) {
            int i = 0;
        
            if (taskInstance instanceof ITaskInstanceList) {
                for (ITaskInstance child : (ITaskInstanceList) taskInstance) {
                    encode(child, out, indent + "  ", i++, path, excludes);
                }
            }
            else if (taskInstance instanceof ISelectionInstance) {
                encode(((ISelectionInstance) taskInstance).getChild(), out, indent + "  ", i++,
                       path, excludes);
            }
            else if (taskInstance instanceof IOptionalInstance) {
                if (((IOptionalInstance) taskInstance).getChild() != null) {
                    encode(((IOptionalInstance) taskInstance).getChild(), out, indent + "  ", i++,
                           path, excludes);
                }
            }

            if (task instanceof ITemporalRelationship) {
                out.print(indent);
                out.print("}");
            }
        }
        else {
            if (task instanceof ITemporalRelationship) {
                out.print(" ... }");
            }
        }

        out.println();
        
        parentPath.removeLast();
    }
    
    /**
     *
     */
    private void encode(ITask         task,
                        PrintStream   out,
                        String        indent,
                        int           index)
    {
        if (task instanceof ITemporalRelationship) {
            if (index > 0) {
                out.println();
            }
            out.print(indent);

            if (task instanceof ISequence) {
                out.print("Sequence ");
            }
            else if (task instanceof ISelection) {
                out.print("Selection ");
            }            
            else if (task instanceof IIteration) {
                out.print("Iteration ");
            }            
            else if (task instanceof IOptional) {
                out.print("Optional ");
            }
            
            out.print(task.getId());
            out.println(" {");
        }
        else if (task instanceof IEventTask) {
            out.print(indent);
            out.print(task);
        }
        else {
            throw new RuntimeException("unknown type of task " + task);
        }

        int i = 0;
        
        if (task instanceof IStructuringTemporalRelationship) {
            for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) {
                encode(child, out, indent + "  ", i++);
            }
        }
        else if (task instanceof IMarkingTemporalRelationship) {
            encode(((IMarkingTemporalRelationship) task).getMarkedTask(), out, indent + "  ", i++);
        }

        if (task instanceof ITemporalRelationship) {
            out.print(indent);
            out.print("}");
        }

        out.println();
    }
    
    /**
     *
     */
    public void dumpAsCheckString(IUserSession userSession) {
        int[] typeCounters = new int[4];
        Map<ITask, String> taskIds = new HashMap<ITask, String>();
        
        for (ITaskInstance taskInstance : userSession) {
            dumpTaskInstanceAsCheckString(taskInstance, typeCounters, taskIds, "");
        }
    }

    /**
     *
     */
    private void dumpTaskInstanceAsCheckString(ITaskInstance      taskInstance,
                                               int[]              typeCounters,
                                               Map<ITask, String> taskIds,
                                               String             indent)
    {
        ITask task = taskInstance.getTask();
        
        System.out.print("       \"");
        System.out.print(indent);

        String id = taskIds.get(task);
        
        if (task instanceof ISequence) {
            if (id == null) {
                id = "sequence" + typeCounters[0]++;
            }
            
            System.out.print("Sequence ");
            System.out.print(id);
            System.out.println(" {\" +");
        }
        else if (task instanceof IIteration) {
            if (id == null) {
                id = "iteration" + typeCounters[1]++;
            }
            
            System.out.print("Iteration ");
            System.out.print(id);
            System.out.println(" {\" +");
        }
        else if (task instanceof ISelection) {
            if (id == null) {
                id = "selection" + typeCounters[2]++;
            }
            
            System.out.print("Selection ");
            System.out.print(id);
            System.out.println(" {\" +");
        }
        else if (task instanceof IEventTask) {
            Event event = ((IEventTaskInstance) taskInstance).getEvent();
            if (event.getType() instanceof TextInput) {
                if (id == null) {
                    id = "textInput" + typeCounters[3]++;
                }
                
                System.out.print("TextInputEvent ");
                System.out.print(id);
                System.out.print(" \"");
                System.out.print(((TextInput) event.getType()).getEnteredText());
                System.out.print("\"");
            }
            else {
                if (id == null) {
                    id = "event" + typeCounters[3]++;
                }
                
                System.out.print("Event ");
                System.out.print(id);
                System.out.print(' ');
                System.out.print(event.getType().getName());
            }
            System.out.print(" {}\" +");
        }
        else {
            throw new RuntimeException("unknown type of task referred by task instance " + taskInstance);
        }

        taskIds.put(task, id);
        
        if (taskInstance instanceof ITaskInstanceList) {
            for (ITaskInstance child : (ITaskInstanceList) taskInstance) {
                dumpTaskInstanceAsCheckString(child, typeCounters, taskIds, indent + "  ");
            }
        }
        else if (taskInstance instanceof ISelectionInstance) {
            dumpTaskInstanceAsCheckString
                (((ISelectionInstance) taskInstance).getChild(), typeCounters, taskIds, indent + "  ");
        }
        else if (taskInstance instanceof IOptionalInstance) {
            dumpTaskInstanceAsCheckString
                (((IOptionalInstance) taskInstance).getChild(), typeCounters, taskIds, indent + "  ");
        }

        if (!(task instanceof IEventTask)) {
            System.out.print("       \"");
            System.out.print(indent);
            System.out.print("}\" +");
        }

        System.out.println();
    }

}
