//   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.ui.swt;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
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.ITaskModel;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;

import org.eclipse.swt.widgets.Label;

/**
 * <p>
 * a dialog to inspect the tasks and task instances of a task model
 * </p>
 * 
 * @author Patrick Harms
 */
public class ShowTaskTreeDialog extends Dialog {

    /** the main shell */
    protected Shell shell;
    
    /** the tree of task instances on the left */
    private Tree instanceTree;
    
    /** the tree of tasks (model) on the right*/
    private Tree modelTree;

    /** the displayed task model */
    protected ITaskModel taskModel;

    /**
     * creates the dialog
     */
    public ShowTaskTreeDialog(Shell parent, int style, ITaskModel taskModel, String taskTreeName) {
        super(parent, style);
        setText("Task Model " + taskTreeName);
        this.taskModel = taskModel;
    }

    /**
     * displays the dialog
     */
    public void open() {
        createContents();
        shell.open();
        shell.layout();
        Display display = getParent().getDisplay();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /**
     * creates the two views, one on the task instances on the left, on on the task models on the
     * right. Also adds a selection adapter to the task instances so that for a selected task
     * instance always the respective model is presented.
     */
    private void createContents() {
        shell = new Shell(getParent(), SWT.SHELL_TRIM | SWT.BORDER | SWT.APPLICATION_MODAL);
        shell.setSize(750, 800);
        shell.setText(getText());

        shell.setLayout(new GridLayout(4, false));
        
        SashForm sashForm = new SashForm(shell, SWT.HORIZONTAL);
        sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));
        
        instanceTree = new Tree(sashForm, SWT.BORDER | SWT.MULTI);
        instanceTree.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                modelTree.removeAll();
                TreeItem[] selectedItems = instanceTree.getSelection();
                if (selectedItems.length == 1) {
                    if (selectedItems[0].getData() instanceof ITaskInstance) {
                        buildModelTree((ITaskInstance) selectedItems[0].getData());
                    }
                    else if (selectedItems[0].getData() instanceof ITaskModel) {
                        buildModelTree((ITaskModel) selectedItems[0].getData());
                    }
                }
            }
        });

        buildInstanceTree();

        modelTree = new Tree(sashForm, SWT.BORDER | SWT.MULTI);
        buildModelTree(taskModel);

        Button btnExpandAll = new Button(shell, SWT.NONE);
        btnExpandAll.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                expandAll(instanceTree, true);
            }
        });
        btnExpandAll.setText("Expand all");

        Button btnCollapseAll = new Button(shell, SWT.NONE);
        btnCollapseAll.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                expandAll(instanceTree, false);
            }
        });
        btnCollapseAll.setText("Collapse all");
        
        //new Label(shell, SWT.NONE);
        new Label(shell, SWT.NONE);
        new Label(shell, SWT.NONE);
        new Label(shell, SWT.NONE);

    }

    /**
     * convenience method for creating the display of the instances
     */
    private void buildInstanceTree() {
        List<IUserSession> sessions = taskModel.getUserSessions();
        
        TreeItem root = new TreeItem(instanceTree, SWT.NULL);
        root.setText(sessions.size() + " sessions");
        root.setData(taskModel);
        
        for (IUserSession session : sessions) {
            buildInstanceTree(root, session);
        }
    }

    /**
     * convenience method for creating the display of the instances
     */
    private void buildInstanceTree(TreeItem currentParent, ITaskInstanceList taskInstanceList) {
        TreeItem child = new TreeItem(currentParent, SWT.NULL);
        child.setText(taskInstanceList.toString());
        child.setData(taskInstanceList);
        
        for (ITaskInstanceList childTask : taskInstanceList) {
            buildInstanceTree(child, childTask);
        }
    }

    /**
     * convenience method for creating the display of the task model
     */
    private void buildModelTree(ITaskModel taskModel) {
        modelTree.removeAll();
        
        // load the correct children on expansion
        if (modelTree.getListeners(SWT.Expand).length == 0) {
           modelTree.addListener(SWT.Expand, new Listener() {
               public void handleEvent(final Event event) {
                   ensureChildren((TreeItem) event.item);
                   ((TreeItem) event.item).setExpanded(true);
               }
           });
        }
        
        Collection<ITask> tasks = taskModel.getTasks();
        
        TreeItem root = new TreeItem(modelTree, SWT.NULL);
        root.setText(tasks.size() + " tasks in model (showing only root tasks)");
        root.setData(taskModel);

        tasks = createSortedTaskList(tasks);
        
        for (ITask task : tasks) {
            createTreeItemFor(task, root);
        }
        
        root.setExpanded(true);
    }

    /**
     * convenience method for creating the display of the task model
     */
    private void buildModelTree(ITaskInstance taskInstance) {
        modelTree.removeAll();
        TreeItem root = new TreeItem(modelTree, SWT.NULL);
        root.setText("model of instance " + taskInstance);
        root.setData(taskInstance);

        createTreeItemFor(taskInstance.getTask(), root);
        
        expandAll(root, true, 20);
    }

    /**
     * reduces the list of tasks in a model so that it only contains root nodes and that it is 
     * sorted starting with the most complex task first 
     */
    private List<ITask> createSortedTaskList(Collection<ITask> tasks) {
        Set<ITask> resultSet = new HashSet<ITask>(tasks);
        
        for (ITask task : tasks) {
            removeChildren(task, resultSet);
        }
        
        List<ITask> result = new LinkedList<ITask>(resultSet);
        
        Collections.sort(result, new Comparator<ITask>() {
            @Override
            public int compare(ITask task1, ITask task2) {
                return depth(task2) - depth(task1);
            }

            private int depth(ITask task) {
                int maxChildDepth = 0;
                if (task instanceof IStructuringTemporalRelationship) {
                    for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) {
                        maxChildDepth = Math.max(maxChildDepth, depth(child));
                    }
                }
                else if (task instanceof IMarkingTemporalRelationship) {
                    maxChildDepth = Math.max
                        (maxChildDepth, depth(((IMarkingTemporalRelationship) task).getMarkedTask()));
                }
                return maxChildDepth + 1;
            }
        });
        
        return result;
    }

    /**
     * convenience method for removed all non root tasks of the provided list
     */
    private void removeChildren(ITask task, Set<ITask> result) {
        if (task instanceof IStructuringTemporalRelationship) {
            for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) {
                result.remove(child);
            }
        }
        else if (task instanceof IMarkingTemporalRelationship) {
            result.remove(((IMarkingTemporalRelationship) task).getMarkedTask());
        }
    }

    /**
     * expands all nodes in the tree
     */
    private void expandAll(Tree tree, boolean expanded) {
        for (TreeItem item : tree.getItems()) {
            expandAll(item, expanded, Integer.MAX_VALUE);
        }
    }

    /**
     * expands all nodes in the tree
     */
    private void expandAll(TreeItem item, boolean expanded, int maxChildrenToExpand) {
        if (expanded) {
            ensureChildren(item);
        }
        
        if (item.getItems().length < maxChildrenToExpand) {
            item.setExpanded(expanded);

            for (TreeItem childItem : item.getItems()) {
                expandAll(childItem, expanded, maxChildrenToExpand);
            }
        }
    }
    
    /**
     * ensures, that the children of a specific node are loaded
     */
    private void ensureChildren(TreeItem parent) {
        if (parent.getData() instanceof ITask) {
            TreeItem[] items = parent.getItems();
            if ((items == null) || (items.length == 0) || (items[0].getData() == null)) {
                if (items != null) {
                    for (int i = 0; i < items.length; i++) {
                        items[i].dispose();
                    }
                }

                ITask task = (ITask) parent.getData();

                if (task instanceof IStructuringTemporalRelationship) {
                    for (ITask subTask : ((IStructuringTemporalRelationship) task).getChildren()) {
                        createTreeItemFor(subTask, parent);
                    }
                }
                else if (task instanceof IMarkingTemporalRelationship) {
                    createTreeItemFor
                        (((IMarkingTemporalRelationship) task).getMarkedTask(), parent);
                }
            }
        }
    }

    /**
     * convenience method to create a tree item for a task
     */
    private void createTreeItemFor(ITask task, TreeItem parent) {
        TreeItem item = new TreeItem(parent, SWT.NULL);
        item.setText(task.toString());
        item.setData(task);
        
        // simulate a child
        if ((task instanceof IStructuringTemporalRelationship) ||
            (task instanceof IMarkingTemporalRelationship))
        {
            new TreeItem(item, SWT.NULL);
        }
    }

}
