/*******************************************************************************
* Copyright (c)                                                                *
*           2005   Tristan Allwood,                                            *
*           2005   Daniel Burke,                                               *
*           2005   Marc Hull,                                                  *
*           2005   Ekaterina Itskova,                                          *
*           2005   Steve Zymler                                                *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a      *
* copy of this software and associated documentation files (the                *
* "Software"), to deal in the Software without restriction, including          *
* without limitation the rights to use, copy, modify, merge, publish,          *
* distribute, sublicense, and/or sell copies of the Software, and to           *
* permit persons to whom the Software is furnished to do so, subject to        *
* the following conditions:                                                    *
*                                                                              *
* The above copyright notice and this permission notice shall be included      *
* in all copies or substantial portions of the Software.                       *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS      *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF                   *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.       *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY         *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,         *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE            *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                       *
*******************************************************************************/
/*
 * Created on 20-Oct-2004
 *
 */
package uk.ac.ic.doc.kevlar.gui.internal.pipeline;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;

import uk.ac.ic.doc.kevlar.Kevlar;
import uk.ac.ic.doc.kevlar.core.framework.IPipe;
import uk.ac.ic.doc.kevlar.core.framework.IProgramIDDescription;
import uk.ac.ic.doc.kevlar.gui.GUIManager;
import uk.ac.ic.doc.kevlar.gui.internal.ADrawableObject;
import uk.ac.ic.doc.kevlar.gui.internal.CanvasManager;
import uk.ac.ic.doc.kevlar.gui.internal.animation.AnimationManager;
import uk.ac.ic.doc.kevlar.gui.internal.animation.ScrollAnimation;
import uk.ac.ic.doc.kevlar.gui.internal.caches.ColorCache;
import uk.ac.ic.doc.kevlar.gui.internal.caches.ImageCache;
import uk.ac.ic.doc.kevlar.gui.internal.dragdrop.ADraggableObject;
import uk.ac.ic.doc.kevlar.gui.internal.dragdrop.IDropTarget;
import uk.ac.ic.doc.kevlar.gui.internal.keyboard.KeyboardManager;
import uk.ac.ic.doc.kevlar.gui.internal.keyboard.TabManager;
import uk.ac.ic.doc.kevlar.gui.internal.keyboard.states.FindAsYouTypeState;
import uk.ac.ic.doc.kevlar.gui.internal.keyboard.states.ProgramMultiSelectState;
import uk.ac.ic.doc.kevlar.gui.internal.keyboard.states.ProgramSelectedState;
import uk.ac.ic.doc.kevlar.gui.internal.keyboard.states.State;
import uk.ac.ic.doc.kevlar.gui.internal.layout.ILayout;
import uk.ac.ic.doc.kevlar.gui.internal.layout.ILayoutData;
import uk.ac.ic.doc.kevlar.gui.internal.layout.LayoutManager;
import uk.ac.ic.doc.kevlar.gui.internal.pipeline.comparators.BottomToTopProgramComparator;
import uk.ac.ic.doc.kevlar.gui.internal.pipeline.comparators.LeftToRightProgramComparator;
import uk.ac.ic.doc.kevlar.gui.internal.pipeline.comparators.RightToLeftProgramComparator;
import uk.ac.ic.doc.kevlar.gui.internal.pipeline.comparators.TopToBottomProgramComparator;
import uk.ac.ic.doc.kevlar.gui.internal.pipeline.executionbox.ExecutionBox;
import uk.ac.ic.doc.kevlar.gui.internal.popup.PopUpBox;
import uk.ac.ic.doc.kevlar.gui.internal.taskpane.HistoryPane;
import uk.ac.ic.doc.kevlar.gui.internal.taskpane.TaskPaneManager;
import uk.ac.ic.doc.kevlar.gui.internal.taskpane.Thumbnail;
import uk.ac.ic.doc.kevlar.gui.internal.taskpane.loadpane.MacroLoadStructure;
import uk.ac.ic.doc.kevlar.gui.internal.toolbar.ToolBarManager;
import uk.ac.ic.doc.kevlar.gui.internal.widgets.ScrollingWindow;
import uk.ac.ic.doc.kevlar.hial.IHialContext;
import uk.ac.ic.doc.kevlar.hial.IHialContextListener;
import uk.ac.ic.doc.kevlar.hial.IRefObject;
import uk.ac.ic.doc.kevlar.publicInterfaces.INode;


/**
 * @author Kate
 **/

public class PipeLineManager extends ScrollingWindow implements IDropTarget, IHialContextListener, ILoad {
	
    private static final String BACKGROUND_IMAGE = "background.png";
    
	private static PipeLineManager singleton;
	
	private Image background;
	private AIONodeEntry selectedNode;
	private Program selectedProgram;
	private Program lastSelectedProgram;
	
	private SelectionBox selectedArea;
	private Rectangle selectionRect;
	private Point mouseDownPoint;
	private ArrayList<Program> multiSelect;
	private boolean selecting;
	private boolean enabled;
	
	private PopUpBox executionPopUp;
	
	public PipeLineManager() {
	    
	    super();
	    
	    try {
	    
	        background = ImageCache.get(BACKGROUND_IMAGE);
	        
	    } catch (FileNotFoundException e) {
	        
	        System.err.println("Could not find image: "+BACKGROUND_IMAGE);
	        background = new Image(Display.getDefault(), 1, 1);
	        
	    }
	    
	    setSizeFillBoth(new Point(10, 0));
	    setPadding(new Point(300, 300));
	    
        horizScrollbar.setGapLeft();
        horizScrollbar.setIncrement(25);
        vertScrollbar.setGapBottom();
        vertScrollbar.setAlignmentLeft();
        vertScrollbar.setIncrement(25);
        
        selectedNode = null;
        selectedProgram = null;
        lastSelectedProgram = null;

		executionPopUp = new PopUpBox();
		executionPopUp.setSize(new Point(150, 45));
		executionPopUp.setName("Executing Pipeline");
		executionPopUp.setContents(new ExecutionBox());
        
        multiSelect = new ArrayList<Program>();
        selecting = false;
        selectionRect = null;
        mouseDownPoint = null;
        selectedArea = new SelectionBox();
        addChild(selectedArea);
        
        enabled = true;
        
	}
	
	public static PipeLineManager getSingleton()
	{
		return singleton;
	}
	
	public static void createSingleton()
	{
		if (singleton==null)
		{
			singleton = new PipeLineManager();
		}
	}
	
	public boolean isEnabled() {
	    
	    return enabled;
	    
	}
	
	public void enable() {
	    
	    if (!enabled) {
	        
	        enabled = true;
	        
	        for (ADrawableObject child: contents.getChildren()) {
	            
	            if (child instanceof Program) {
	                
	                ((Program)child).enable();
	                
	            }
	            
	        }
	        
	    }
	    
	}
	
	public void disable() {
	    
	    if (enabled) {
	        
	        enabled = false;
	        
	        deselectProgram();
	        clearMultiSelect();
	        
	        for (ADrawableObject child: contents.getChildren()) {
	            
	            if (child instanceof Program) {
	                
	                ((Program)child).disable();
	                
	            }
	            
	        }
	        	        
	    }
	    
	}
	
	public boolean isExecutable() {
	    
	    for (ADrawableObject child: contents.getChildren()) {
	        
	        if (child instanceof Program) {
	            
	            if (!((Program)child).isExecutable()) {
	                
	                return false;
	                
	            }
	            
	        }
	        
	    }
	    
	    return true;
	    
	}
	
	public void updateExecutable() {
	    
	    if (enabled && isExecutable()) {
	        
	        ToolBarManager.getSingleton().enableExecute();
	        
	    } else {
	        
	        ToolBarManager.getSingleton().disableExecute();
	        
	    }
	    
	}
	
	public boolean acceptsDragObject(ADraggableObject object) {
	    
	    if (enabled) {
	    
	        return (object instanceof Program);
	        
	    } else {
	        
	        return false;
	        
	    }
	    
	}
	
	public void addChild(ADrawableObject child) {
	    
	    super.addChild(child);
	    
	    /*
	     * Ensure that programs are always drawn before pipes, so that
	     * pipes can overlap programs
	     */
	    
	    List<ADrawableObject> pipelineObjects = contents.getChildren();
	    
	    for (int i=pipelineObjects.size()-1; i>=0; i--) {
	        
	        ADrawableObject c = pipelineObjects.get(i);
	        
	        if (c instanceof Pipe) {
	            
	            contents.bringToFront(c);
	            c.redraw();
	            
	        }
	        
	    }
	    
	    contents.bringToFront(selectedArea);
	    
	}
	
	public void dropDragObject(ADraggableObject object, Point position) {
	    
	    if (object instanceof Program) {
	        
	        Point contentsPos = contents.getPosition();
	        Point scrollingPos = new Point(position.x-contentsPos.x, position.y-contentsPos.y);
	        
	        Program program = (Program)object;
	        ILayout layout = LayoutManager.getLayout();
	        
	        if (!program.isRegistered()) {
	            
		        layout.addProgram(program, scrollingPos);
//		        layout.suggestProgramPosition(program, scrollingPos);
		        layout.removeProgram(program);
		        
		        GUIManager.getHialContext().addProgram(program.getProgramIDDescription(), program);
	            
	        } else {
	            
	            layout.suggestProgramPosition(program, scrollingPos);
	            addChild(program);
	            
	        }
	        
            PipeLineManager.getSingleton().scrollTo(program);
	        
	    }
	    
	}
	
	public void drawBefore(GC gc, Rectangle redrawArea) {
	    
        Rectangle bkBounds = background.getBounds();
        Rectangle windowBounds = CanvasManager.getSingleton().getCanvas().getBounds();	        
        gc.drawImage(background, (int)(redrawArea.x*((double)bkBounds.width/windowBounds.width)), (int)(redrawArea.y*((double)bkBounds.height/windowBounds.height)), (int)(redrawArea.width*((double)bkBounds.width/windowBounds.width)), (int)(redrawArea.height*((double)bkBounds.height/windowBounds.height)), 0, 0, redrawArea.width, redrawArea.height);  
	        
	}
	
	public void toggleMultiSelect(Program program) {
	    
	    if (enabled) {
	    
	        if (multiSelect.contains(program)) {
	            
	            removeFromMultiSelect(program);
	            
	        } else {
	            
	            addToMultiSelect(program);
	            
	        }
	        
	    }
	    
	}
	
	public void addToMultiSelect(Program program) {

	    if (enabled) {
	    
	        if (!multiSelect.contains(program)) {
	            
	            program.addDimensionListener(selectedArea);
	            multiSelect.add(program);
	            multiSelectChanged();
	            program.highlight();
	            
	            if (!selecting) {
	                
	                adjustSelectedArea();
	                
	            }
	            
	        }
	        
	    }
	    
	}

	public void removeFromMultiSelect(Program program) {

	    if (multiSelect.contains(program)) {
	    
	        program.removeDimensionListener(selectedArea);
	        multiSelect.remove(program);
	        multiSelectChanged();

	        if (!selecting) {
            
	            adjustSelectedArea();
            
	        }
	        
	    }

        program.unhighlight();
	    
	}
	
	public void clearMultiSelect() {
	    
	    for (Program p: multiSelect) {
	        
	        p.removeDimensionListener(selectedArea);
	        
	    }
	    
	    multiSelect.clear();
	    multiSelectChanged();
	    adjustSelectedArea();
	    updateHighlightedPrograms();
	    
	}
	
	private void multiSelectChanged() {
	    
	    State currentState = KeyboardManager.getSingleton().getState();
	    
	    if (multiSelect.isEmpty()) {
	    
	        if (currentState instanceof ProgramMultiSelectState) {
	            
	            ProgramMultiSelectState state = (ProgramMultiSelectState)currentState;
	            
	            if (state.getSelectedState() != null) {
	                
	                KeyboardManager.getSingleton().setState(state.getSelectedState());
	                
	            }
	            
	        }
	                	        
	    } else {
	        
	        if (!(currentState instanceof ProgramMultiSelectState)) {
            
	            ProgramMultiSelectState s = new ProgramMultiSelectState();
	            
	            if (selectedProgram != null) {
	            
	                s.setSelectedState(new ProgramSelectedState(selectedProgram));
	                
	            }
	            
	            KeyboardManager.getSingleton().setState(s);
            
	        }
	        
	    }
	    
	}
	
	public void deleteMultiSelect() {
	    
	    if (enabled) {
	    
	        while (multiSelect.size() > 0) {
	        
	            multiSelect.get(0).removeProgram();
	            multiSelect.remove(0);
	        
	        }

	        clearMultiSelect();
	        
	    }
	    
	}
	
	public void toggleExpandContractMultiSelect() {
	    
	    selectedArea.toggleExpandContract();
	    
	}
	
	public void expandMultiSelect() {
	    
	    for (Program p: multiSelect) {
	        
	        p.expand();
	        
	    }
	    
	}
	
	public void contractMultiSelect() {
	    
	    for (Program p: multiSelect) {
	        
	        p.contract();
	        
	    }
	    
	}
	
	public void shiftMultiSelectUp() {
	    
	    Collections.sort(multiSelect, new TopToBottomProgramComparator());
	    
	    for (Program p: multiSelect) {
	        
	        p.shiftUp();
	        
	    }
	    
	}
	
	public void shiftMultiSelectDown() {

	    Collections.sort(multiSelect, new BottomToTopProgramComparator());
	    
	    for (Program p: multiSelect) {
	        
	        p.shiftDown();
	        
	    }
	    
	}
	
	public void shiftMultiSelectLeft() {

	    Collections.sort(multiSelect, new LeftToRightProgramComparator());
	    
	    for (Program p: multiSelect) {
	        
	        p.shiftLeft();
	        
	    }
	    
	}
	
	public void shiftMultiSelectRight() {
	    
	    Collections.sort(multiSelect, new RightToLeftProgramComparator());
	    
	    for (Program p: multiSelect) {
	        
	        p.shiftRight();
	        
	    }
	    
	}
	
	public void selectAll() {
	    
	    if (enabled) {
	    
	        clearMultiSelect();
	    
	        for (ADrawableObject child: contents.getChildren()) {
	        
	            if (child instanceof Program) {
	            
	                addToMultiSelect((Program)child);
	            
	            }
	        
	        }
	    
/*	    adjustSelectedArea();
	    updateHighlightedPrograms(); */
	        
	    }
	    
	}
	
	public void createMacro() {
	    
	    if (enabled) {
	    
	        Macro m = createMacro(multiSelect);
	        addChild(m);
	        TabManager.getSingleton().addToTabList(m);
	        LayoutManager.getLayout().addProgram(m, selectedArea.getPosition());
	        m.updatePipes();
	        clearMultiSelect();
	        selectProgram(m);
	        
	    }
	    
	}
	
	public Macro createMacro(List<Program> programs) {

        Macro macro = new Macro();
        macro.addPrograms(programs);
	        
        for (Program p: programs) {
	            
            if (p == selectedProgram) {
	                
                deselectProgram();
	                
            }
	            
            p.remove();
	            
        }
	        
        return macro;
	    
	}
	
	public void selectProgram(Program program) {
	    
	    if (enabled) {
	    
	        try {
	            
	            if (selectedProgram != null) {
	                
	                selectedProgram.deselect();
	                
	            }
	            
	            selectedProgram = program;
	            selectedProgram.select();
	            scrollTo(selectedProgram);
	            
	            State currentState = KeyboardManager.getSingleton().getState();
	            
	            if (currentState instanceof ProgramMultiSelectState) {
	                
	                ((ProgramMultiSelectState)currentState).setSelectedState(new ProgramSelectedState(selectedProgram));
	                
	            } else if (currentState instanceof FindAsYouTypeState) {
	                
	                ((FindAsYouTypeState)currentState).setHistory(new ProgramSelectedState(selectedProgram));
	                
	            } else {
	                
	                KeyboardManager.getSingleton().setState(new ProgramSelectedState(selectedProgram));
	                
	            }
	            
	        } catch (NullPointerException e) {
	            
	            System.err.println("NPE detected!!! "+program);
	            
	        }
	        
	    }
	    
	}
	
	public void scrollTo(ADrawableObject child) {
	    
	    Rectangle childArea = child.getAreaRelativeTo(this);
	    Point newPosition = contents.getPosition();
	    
	    if ((childArea.x+childArea.width+50 > area.width) || (childArea.x-50 < 0) || (childArea.y+childArea.height+50 > area.height) || (childArea.y-50 < 0)) {
	    
	        if (childArea.x+childArea.width+50 > area.width) {
	            
	            newPosition.x = newPosition.x-((childArea.x+childArea.width+50)-area.width);
	            
	            if (newPosition.x > 0) {
	                
	                newPosition.x = 0;
	                
	            } else if (newPosition.x < -contents.getArea().width) {
	                
	                newPosition.x = -contents.getArea().width;
	                
	            }
	            
	        } else if (childArea.x-50 < 0) {
	            
	            newPosition.x = newPosition.x-childArea.x+50;
	            
	            if (newPosition.x > 0) {
	                
	                newPosition.x = 0;
	                
	            } else if (newPosition.x < -contents.getArea().width) {
	                
	                newPosition.x = -contents.getArea().width;
	                
	            }
	            
	        }
	        
	        if (childArea.y+childArea.height+50 > area.height) {
	            
	            newPosition.y = newPosition.y-((childArea.y+childArea.height+50)-area.height);
	            
	            if (newPosition.y > 0) {
	                
	                newPosition.y = 0;
	                
	            } else if (newPosition.y < -contents.getArea().height) {
	                
	                newPosition.y = -contents.getArea().height;
	                
	            }
	            
	        } else if (childArea.y-50 < 0) {
	            
	            newPosition.y = newPosition.y-childArea.y+50;
	            
	            if (newPosition.y > 0) {
	                
	                newPosition.y = 0;
	                
	            } else if (newPosition.y < -contents.getArea().height) {
	                
	                newPosition.y = -contents.getArea().height;
	                
	            }
	            
	        }
        
	        AnimationManager.getSingleton().addAnimation(new ScrollAnimation(this, newPosition));
//	        contents.setPosition(newPosition);
	        
	    }
                
	}
	
	public void deselectProgram() {
	    
	    if (selectedProgram != null) {
	        
	        lastSelectedProgram = selectedProgram;
	        selectedProgram.deselect();
	        selectedProgram = null;
	        
	    }

        KeyboardManager.getSingleton().setState(KeyboardManager.ROOT_STATE);
	    
	}
	
	public Program getLastSelectedProgram() {
	    
	    return lastSelectedProgram;
	    
	}
	
	public Program getSelectedProgram() {
	    
	    return selectedProgram;
	    
	}
	
	public AIONodeEntry getSelectedNode() {
	    
	    return selectedNode;
	    
	}
	
	public void selectNode(AIONodeEntry node) {
	    
	    if (enabled) {
	    
	        if (selectedNode instanceof InputNodeEntry && node instanceof OutputNodeEntry) {
	            
	            addPipe((OutputNodeEntry)node, (InputNodeEntry)selectedNode);
	            selectedNode.deselect();
	            node.deselect();
	            deselectNode();
	            
	        } else if (node instanceof InputNodeEntry && selectedNode instanceof OutputNodeEntry) {
	            
	            addPipe((OutputNodeEntry)selectedNode, (InputNodeEntry)node);
	            selectedNode.deselect();
	            node.deselect();
	            deselectNode();
	            
	        } else {
	            
	            if (selectedNode != null) {
	                
	                selectedNode.deselect();
	                
	            }
	            
	            selectedNode = node;
	            selectedNode.select();
	            
	        }
	        
	    }
	    
	}
	
	public void deselectNode() {
	    	    
	    selectedNode = null;
	    
	}
	
	public Program addProgram(IProgramIDDescription programDescription) {

	    if (enabled) {
	    
	        Program program = new Program(programDescription);	    
	        
	        if (selectedNode != null) {
	            
	            if (selectedNode instanceof InputNodeEntry) {
	                
	                LayoutManager.getLayout().addProgramAsOutput(program, selectedNode.getProgram());
	                LayoutManager.getLayout().removeProgram(program);
	                
	            } else if (selectedNode instanceof OutputNodeEntry) {
	                
	                LayoutManager.getLayout().addProgramAsInput(program, selectedNode.getProgram());
	                LayoutManager.getLayout().removeProgram(program);
	                
	            }
	            
	        }
	        
	        GUIManager.getHialContext().addProgram(program.getProgramIDDescription(), program);
	        
	        return program;
	        
	    } else {
	        
	        return null;
	        
	    }
	    
	}
	
	public Pipe addPipe(OutputNodeEntry source, InputNodeEntry target) {
	    
	    if (enabled) {
	    
	        /*toa02: Fixed a bug where if u tried to replace a pipeline
	         with one that was following the same route (i.e. both ends
	         pointed to the same pipe, the HIAL would crash complaining
	         the pipe doesn't exist in the system. */
	        
//	        System.out.println("AddPipe: "+source.getName()+" "+target.getName());
	        
	        if (source.hasPipe() && (!target.hasPipe() || !target.getPipe().equals(source.getPipe()))) {
	            
	            source.getPipe().requestRemove();
	            
	        }
	        
	        if (target.hasPipe() ) {
	            
	            target.getPipe().requestRemove();
	            
	        }
	        
	        Pipe p = new Pipe(source, target);
	        GUIManager.getHialContext().addPipe(source.getProgram(), ((INode)source.getDataNode()).getNodeID(), target.getProgram(), ((INode)target.getDataNode()).getNodeID(), p);
	        
	        return p;
	        
	    } else {
	        
	        return null;
	        
	    }
	    
	}
	
	public void removePipe(Pipe p) {

	    p.requestRemove();
	    
	}
	
	public void clear() {
	
	    if (isEnabled()) {
	        
	        List<ADrawableObject> context = contents.getChildren();
	        
	        if (!context.isEmpty())
	        {
	            //context.clear();
	            //   List programs = PipeLineManager.getSingleton().getContents().getChildren();
	            
	            for (int i = 0; i<context.size(); i++)
	            {
	                ADrawableObject p = context.get(i);
	                if (p instanceof Macro)
	                {
	                    ((Program)p).remove();
	                }
	            }
	            
	            GUIManager.getHialContext().removeAll();
	            /* inform the hial -> framework that programs and pipes have been removed */
	            
	            CanvasManager.getSingleton().redraw();
	            CanvasManager.getSingleton().applyRedraw();
	        }
	        
	    }
	    
	}
	
	public void mousePress(Point position, int button) {
	    
	    super.mousePress(position, button);
	    deselectProgram();

	    selecting = true;
	    selectedArea.setPosition(new Point(0, 0));
	    selectedArea.setSize(new Point(0, 0));
	    selectionRect = new Rectangle(position.x, position.y, 0, 0);
	    mouseDownPoint = position;
	    updateHighlightedPrograms();
	    
	    getMouseFocus();
	    
	}
	
	public void mouseMoving(Point position) {
	    
	    if (isMouseDown() && mouseDownPoint != null && selectionRect != null) {
	        
/*	        Point currentPosition = selectedArea.getPosition();
	        selectedArea.setSize(new Point(position.x-currentPosition.x, position.y-currentPosition.y));
	        updateHighlightedPrograms();
	        selectedArea.redraw(); */
	        
	        redraw(new Rectangle(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height));
	        selectionRect.x = Math.min(mouseDownPoint.x, position.x);
	        selectionRect.y = Math.min(mouseDownPoint.y, position.y);
	        selectionRect.width = Math.max(mouseDownPoint.x, position.x)-selectionRect.x;
	        selectionRect.height = Math.max(mouseDownPoint.y, position.y)-selectionRect.y;
	        updateHighlightedPrograms();
	        redraw(new Rectangle(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height));
	        
	        
	    }
	    
	}
	
	public void mouseRelease(Point position, int button) {
	    
	    mouseDownPoint = null;
	    
	    if (selectionRect != null) {
	    
	        redraw(new Rectangle(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height));
	        selectionRect = null;
	        selecting = false;
	        releaseMouseFocus();
	        adjustSelectedArea();
	        
	    }
	    
	}
	
	public boolean capturesMouse() {
	    
	    return true;
	    
	}
	
	public void adjustSelectedArea() {
	    
	    if (!selecting) {
	    
	        if (multiSelect.size() == 0) {
	            
	            selectedArea.redraw();
	            selectedArea.setPosition(new Point(0, 0));
	            selectedArea.setSize(new Point(0, 0));
	            selectedArea.redraw();
	            
	        } else {
	            
	            Point topLeft = null;
	            Point bottomRight = null;
	            
	            for (Program p: multiSelect) {
	                
	                Point pos = p.getPosition();
	                Point size = p.getSize();
	                
	                if (topLeft == null) {
	                    
	                    topLeft = new Point(pos.x, pos.y-1);
	                    
	                } else {
	                    
	                    if (pos.x < topLeft.x) {
	                        
	                        topLeft.x = pos.x;
	                        
	                    }
	                    
	                    if (pos.y-1 < topLeft.y) {
	                        
	                        topLeft.y = pos.y-1;
	                        
	                    }
	                    
	                }
	                
	                if (bottomRight == null) {
	                    
	                    bottomRight = new Point(pos.x+size.x, pos.y+size.y);
	                    
	                } else {
	                    
	                    if (pos.x+size.x > bottomRight.x) {
	                        
	                        bottomRight.x = pos.x+size.x;
	                        
	                    }
	                    
	                    if (pos.y+size.y > bottomRight.y) {
	                        
	                        bottomRight.y = pos.y+size.y;
	                        
	                    }
	                    
	                }
	                
	            }
	            
	            selectedArea.redraw();
	            selectedArea.setPosition(topLeft);
	            selectedArea.setSize(new Point(bottomRight.x-topLeft.x, bottomRight.y-topLeft.y));
	            selectedArea.redraw();
	            
	        }
	        
	    }
	    
	}
	
	public void updateHighlightedPrograms() {
	    
	    Rectangle selection;
	    
	    if (selectionRect != null) {
	        
	        Point contentsPos = contents.getPosition();
	        selection = new Rectangle(selectionRect.x-contentsPos.x, selectionRect.y-contentsPos.y, selectionRect.width, selectionRect.height);
	        
	    } else {
	    
	        selection = selectedArea.getArea();
	        
	    }
	    
	    for (ADrawableObject d: contents.getChildren()) {
	        
	        if (d instanceof Program) {
	            
	            Program p = (Program)d;
	            Rectangle area = p.getArea();
	            Point topLeft = new Point(area.x, area.y);
	            Point bottomRight = new Point(area.x+area.width, area.y+area.height);
	            
	            if (selection.contains(topLeft) && selection.contains(bottomRight)) {
	                
	                addToMultiSelect(p);
	                
	            } else {

	                removeFromMultiSelect(p);
	                
	            }
	            
	        }
	        
	    }
	        
	}
	
	public void drawAfter(GC gc, Rectangle redrawArea) {

	    if (selectionRect != null) {
	    
	        gc.setForeground(ColorCache.get(0, 0, 0));
	        gc.setLineStyle(SWT.LINE_DOT);
	        gc.drawRectangle(selectionRect.x-redrawArea.x, selectionRect.y-redrawArea.y, selectionRect.width-1, selectionRect.height-1);
	        
	    } 
	    
		
	}
	
	public Image createSnapshot()
	{
	    
	    vertScrollbar.hide();
	    horizScrollbar.hide();
	    
		Image img = new Image(Display.getCurrent(), area.width, area.height);
		
		GC gc = new GC(img);
		
		super.paint(gc, new Rectangle(0,0, area.width, area.height), img);
		
		(img.getImageData()).scaledTo(Thumbnail.THUMB_WIDTH, Thumbnail.THUMB_HEIGHT);
	                        
		updateScrollbarVisibility();
		
		return img;
	}
	
	public void execute() {
	    
	    if (isEnabled() && isExecutable()) {

	        HistoryPane history = (HistoryPane)TaskPaneManager.getSingleton().getPane(TaskPaneManager.HISTORY);
	        history.addToHistory();
	        	        
	        ToolBarManager.getSingleton().disableExecute();
	        ToolBarManager.getSingleton().enableStop();
	        disable();
	        
//	        executionPopUp.popUpAt(localToAbsolute(new Point(10, 10)));
	        CanvasManager.getSingleton().forceRedraw();
	        
	        IHialContext hialContext = GUIManager.getHialContext();
	        hialContext.executeContext();       
	        
	    }

	}
	
	public void stopExecution() {
	    
	    GUIManager.getHialContext().killAll();
	    
	}
	
	public void toggleExpandContractAll() {
	    
	    int expanded = 0;
	    int total = 0;
	    
	    for (ADrawableObject d : contents.getChildren()) {
	        
	        if (d instanceof Program) {
	            
	            Program p = (Program)d;
	            
	            if (p.isExpanded()) {
	                
	                expanded++;
	                
	            }
	            
	            total++;
	            
	        }
	        
	    }
	    
	    if (expanded > total/2) {
	        
	        contractAll();
	        
	    } else {
	        
	        expandAll();
	        
	    }
	    
	}
	
	public void contractAll() {
	    
	    for (ADrawableObject d : contents.getChildren()) {
	        
	        if (d instanceof Program) {
	            
	            Program p = (Program)d;
	            p.contract();
	            
	        }
	        
	    }	    
	    
	}
	
	public void expandAll() {
	    
	    for (ADrawableObject d : contents.getChildren()) {
	        
	        if (d instanceof Program) {
	            
	            Program p = (Program)d;
	            p.expand();
	            
	        }
	        
	    }	    
	    
	}
		
	public void scrollUp() {

        vertScrollbar.decrement();
	    
	}
	
	public void scrollDown() {
	    
        vertScrollbar.increment();
	    
	}
	
	public void scrollLeft() {
	    
        horizScrollbar.decrement();
	    
	}
	
	public void scrollRight() {
	    
        horizScrollbar.increment();
	    
	}
	
	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#pipeAdded(uk.ac.ic.doc.kevlar.hial.IRefObject)
	 */
	public void pipeAdded(IRefObject refID) {
		
	    if (refID instanceof Pipe) {
	    
	    	System.out.println("PLM: adding pipe");
	        Pipe pipe = (Pipe)refID;
	        pipe.setInvalid(false);
	        addChild(pipe);
	        
	    }
		
	}

	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#pipeAddFailed(uk.ac.ic.doc.kevlar.hial.IRefObject, java.util.List)
	 */
	public void pipeAddFailed(IRefObject arg0, List<IPipe> arg1) {
		
	    if (arg0 instanceof Pipe) {
	    
	        Pipe pipe = (Pipe)arg0;
	        pipe.setInvalid(true);
	        addChild(pipe);
	        
	    }
		
	}

	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#pipeFailed(uk.ac.ic.doc.kevlar.hial.IRefObject)
	 */
	public void pipeFailed(IRefObject refID) {
		
	    if (refID instanceof Pipe) {
	    
	        Pipe pipe = (Pipe)refID;
	        pipe.setInvalid(true);
	        
	    }
		
	}

	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#pipeRemoved(uk.ac.ic.doc.kevlar.hial.IRefObject)
	 */
	public void pipeRemoved(IRefObject refID) {
		
	    if (refID instanceof Pipe) {
	    
	        Pipe pipe = (Pipe)refID;
	        pipe.unset();
	        pipe.remove();
	        
	    }
		
	}

	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#programAdded(uk.ac.ic.doc.kevlar.hial.IRefObject)
	 */
	public void programAdded(IRefObject refID) {
		
	    if (refID instanceof Program) {
	    
	        Program program = (Program)refID;
	        addChild(program);
	        program.register(GUIManager.getHialContext().getProgram(program));
	        
	        LayoutManager.getLayout().addProgram(program);
//	        LayoutManager.getLayout().suggestProgramPosition(program, program.getPosition());
	        
	        TabManager.getSingleton().addToTabList(program);
	        
	    }
	    
	}

	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#programAddFailed(uk.ac.ic.doc.kevlar.hial.IRefObject)
	 */
	public void programAddFailed(IRefObject refID) {
		// Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#programFailed(uk.ac.ic.doc.kevlar.hial.IRefObject)
	 */
	public void programFailed(IRefObject refID) {
		// Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#programRemoved(uk.ac.ic.doc.kevlar.hial.IRefObject)
	 */
	public void programRemoved(IRefObject refID) {

	    if (refID instanceof Program) {
	    
	        Program program = (Program)refID;
	        program.remove();
	        
/*	        program.redraw();
	        removeChild(program);
	        
	        LayoutManager.getLayout().removeProgram(program);
	        TabManager.getSingleton().removeFromTabList(program); */
	        
	        if (selectedProgram == program) {

	            deselectProgram();
	            
	        }
	        
	    }
		
	}

	/* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.hial.IHialContextListener#programStateChanged(uk.ac.ic.doc.kevlar.hial.IRefObject)
	 */
	public void programStateChanged(IRefObject refID) {
	    
	    if (refID instanceof Program) {
	        
	        Program program = (Program)refID;
	        program.register(GUIManager.getHialContext().getProgram(program));
	        
	    }
		
	}

    public void pipeLost(IRefObject refID) {
        
        if (refID instanceof Pipe) {
        
            Pipe pipe = (Pipe)refID;
            pipe.unset();
            pipe.remove();
            
        }
        
    }
		
    public void executionCompleted() {
        
        if(Kevlar.DEBUG)
            System.out.println("called: executionCompleted(...)");
        
//        PopUpManager.getSingleton().hidePopUp(executionPopUp);
        
	    ToolBarManager.getSingleton().enableExecute();
	    ToolBarManager.getSingleton().disableStop();   
	    enable();

    }
    
    public void executionCompleted(IRefObject refID) {
        
        String name = GUIManager.getHialContext().getProgram(refID).getDescription().getFriendlyName();
        
        if(Kevlar.DEBUG)
            System.out.println("called: executionCompleted(" + name + ")");
        
    }
    
    /* (non-Javadoc)
	 * @see uk.ac.ic.doc.kevlar.gui.internal.pipeline.ILoad#loadProgram(uk.ac.ic.doc.kevlar.publicInterfaces.IProgramDescription, uk.ac.ic.doc.kevlar.gui.internal.layout.ILayoutData)
	 */
	public Program loadProgram(IProgramIDDescription desc, ILayoutData layout) {
		
		Program program = new Program(desc);
		program.setLayoutData(layout);
	    GUIManager.getHialContext().addProgram(program.getProgramIDDescription(), program);
	    return program;
	}
	
	public void loadMacro(MacroLoadStructure macroStruct, List macros)
	{
		
		List innerMacroIds = macroStruct.getInnerMacroIds();
		List<Program> macroPrograms = macroStruct.getPrograms();
				
		if (innerMacroIds != null)
		{
			for (int i = 1; i<innerMacroIds.size(); i++)
			{
				MacroLoadStructure innerMacro = (MacroLoadStructure)macros.get(i);
				Macro m = createMacro(innerMacro.getPrograms());
				macroPrograms.add(m);
				
				macros.remove(innerMacro);
			}
		}

		macroLoad(macroPrograms, macroStruct.getPosition());
	}
	
	public Macro macroLoad(List<Program> programs, Point position)
	{
		 Macro macro = new Macro();
	     macro.addPrograms(programs);
	      
		 for (Program p: programs) {
		    
		 	if (p == selectedProgram) {
		            
		 		deselectProgram();
		            
		 	}
		        
		 	p.remove();
		        	        
		 }
		
		 addChild(macro);
		 TabManager.getSingleton().addToTabList(macro);
		 LayoutManager.getLayout().addProgram(macro, position);
	     macro.updatePipes();
		 clearMultiSelect();
		 selectProgram(macro);
		 
		 return macro;
	}
}

    
