/*******************************************************************************
* 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 19-Oct-2004
 *
 */
package uk.ac.ic.doc.kevlar.gui.internal;

import java.util.LinkedList;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
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.layout.GridData;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import uk.ac.ic.doc.kevlar.gui.internal.animation.AnimationManager;
import uk.ac.ic.doc.kevlar.gui.internal.dragdrop.DragDropManager;
import uk.ac.ic.doc.kevlar.gui.internal.help.HelpManager;
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.pipeline.PipeLineManager;
import uk.ac.ic.doc.kevlar.gui.internal.popup.PopUpManager;
import uk.ac.ic.doc.kevlar.gui.internal.showpane.ShowPaneManager;
import uk.ac.ic.doc.kevlar.gui.internal.taskpane.TaskPaneManager;
import uk.ac.ic.doc.kevlar.gui.internal.toolbar.ToolBarManager;
import uk.ac.ic.doc.kevlar.gui.internal.workbench.WorkBenchManager;

/**
 * @author Kate
 * 
 */

public class CanvasManager extends DragDropManager implements PaintListener,
        MouseListener, MouseMoveListener, ControlListener {

    private static CanvasManager singleton;

    private Canvas canvas;

    private Rectangle redrawArea;

    private LinkedList<AMouseListenerObject> mouseFocusStack;

    public CanvasManager(Composite c) {

        super();

        singleton = this;
        mouseFocusStack = new LinkedList<AMouseListenerObject>();

        canvas = new Canvas(c, SWT.NO_BACKGROUND);
        GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
        canvas.setLayoutData(data);
        canvas.addPaintListener(this);
        canvas.addControlListener(this);
        canvas.addMouseMoveListener(this);
        canvas.addMouseListener(this);

        KeyboardManager.createSingleton();
        TabManager.createSingleton();
        AnimationManager.createSingleton();
        ToolBarManager.createSingleton();
        TaskPaneManager.createSingleton();
        PipeLineManager.createSingleton();
        ShowPaneManager.createSingleton();

        canvas.addKeyListener(KeyboardManager.getSingleton());
        canvas.forceFocus();

        WorkBenchManager.createSingleton(PipeLineManager.getSingleton(),
                TaskPaneManager.getSingleton(), ShowPaneManager.getSingleton());

        HelpManager.createSingleton(canvas);
        PopUpManager.createSingleton();

        // Is now part of workbench
        // addChild(PipeLineManager.getSingleton());
        // addChild(TaskPaneManager.getSingleton());

        addChild(WorkBenchManager.getSingleton());
        addChild(ShowPaneManager.getSingleton());
        addChild(ToolBarManager.getSingleton());

        
/*        TextBox t = new TextBox(); 
        t.setPosition(new Point(20, 200));
        t.setSize(new Point(150, 20)); 
        t.setText("Try me!"); addChild(t); 
        
        MultilineTextBox m = new MultilineTextBox(); 
        m.setPosition(new Point(20, 230)); 
        m.setSize(new Point(150, 100));
        m.getTextArea().setText("Hello, this is a little test of the MultilineTextBox component - is it any good? "+StringUtil.NEWLINE+StringUtil.NEWLINE+"Bug reports to phjk@doc.ic.ac.uk");
        addChild(m);
        
        DropDownBox b = new DropDownBox(); 
        b.setPosition(new Point(20, 170));
        b.setSize(new Point(150, 20)); 
        b.addOption("Option 1");
        b.addOption("Option 2"); 
        b.addOption("Option 3"); 
        b.setCurrent(1);
        addChild(b); */
         

        addChild(PopUpManager.getSingleton());

    }

    public static CanvasManager getSingleton() {

        return singleton;

    }

    public static void createSingleton(Composite c) {

        if (singleton == null) {

            new CanvasManager(c);

        }

    }

    public Composite getCanvas() {
        return canvas;
    }

    public void getFocus() {

        canvas.forceFocus();

    }

    private Object redrawLock = new Object();

    public void scheduleRedraw(Rectangle area) {

        synchronized (redrawLock) {

            if (redrawArea == null) {

                redrawArea = new Rectangle(area.x, area.y, area.width,
                        area.height);

            } else {

                int left = Math.min(redrawArea.x, area.x);
                int top = Math.min(redrawArea.y, area.y);
                int right = Math.max(redrawArea.x + redrawArea.width, area.x
                        + area.width);
                int bottom = Math.max(redrawArea.y + redrawArea.height, area.y
                        + area.height);

                redrawArea = new Rectangle(left, top, right - left, bottom
                        - top);

            }
        }

    }

    Runnable r = new Runnable() {

        public void run() {

            synchronized (redrawLock) {
                if (redrawArea != null && !canvas.isDisposed()) {

                    canvas.redraw(redrawArea.x, redrawArea.y, redrawArea.width,
                            redrawArea.height, true);
                    redrawArea = null;

                }
            }

        }
    };

    Runnable r2 = new Runnable() {

        public void run() {

            synchronized (redrawLock) {
                if (redrawArea != null && !canvas.isDisposed()) {

                    canvas.redraw(redrawArea.x, redrawArea.y, redrawArea.width,
                            redrawArea.height, true);
                    redrawArea = null;
                    canvas.update();

                }
            }

        }
    };

    public void forceRedraw() {
        
        Display.getDefault().syncExec(r2);
        
    }
    
    public void applyRedraw() {
        Display.getDefault().asyncExec(r);
    }

    public void paintControl(PaintEvent e) {

        Rectangle redrawArea = new Rectangle(e.x, e.y, e.width, e.height);

        Image buffer = new Image(Display.getCurrent(), redrawArea.width,
                redrawArea.height);
        GC gc = new GC(buffer);

        paint(gc, redrawArea, buffer);
        // gc.drawRectangle(0, 0, e.width-1, e.height-1);

        e.gc.drawImage(buffer, redrawArea.x, redrawArea.y);
        buffer.dispose();
        gc.dispose();

    }

    public void mouseDoubleClick(MouseEvent event) {

        // implementation of the method defined in the MouseListener interface

    }

    public void mousePress(int x, int y, int mouseButton) {

        // implementation of the method defined in the super class
        // AMouseListenerObject

    }

    public void mouseDown(MouseEvent e) {

        if (HelpManager.getSingleton().isDisplayed()) {
            HelpManager.getSingleton().hideHelp();
        }

        if (!mouseFocusStack.isEmpty()) {

            AMouseListenerObject mouseFocusObject = mouseFocusStack.getLast();

            if (mouseFocusObject != null) {

                Point local = mouseFocusObject.absoluteToLocal(new Point(e.x,
                        e.y));
                mouseFocusObject.mousePress(local, e.button);

            } else {

                mouseFocusStack.remove(null);

            }

        }

        concreteMouseDown(new Point(e.x, e.y), e.button);

    }

    public void mouseUp(MouseEvent e) {

        canvas.forceFocus();

        concreteMouseUp(e.x, e.y, e.button);

        if (!mouseFocusStack.isEmpty()) {

            AMouseListenerObject mouseFocusObject = mouseFocusStack.getLast();

            if (mouseFocusObject != null) {

                Point local = mouseFocusObject.absoluteToLocal(new Point(e.x,
                        e.y));
                mouseFocusObject.mouseRelease(local, e.button);

            } else {

                mouseFocusStack.remove(null);

            }

        }

    }

    public void mouseMove(MouseEvent e) {

        concreteMouseMove(new Point(e.x, e.y));

        if (!mouseFocusStack.isEmpty()) {

            AMouseListenerObject mouseFocusObject = mouseFocusStack.getLast();

            if (mouseFocusObject != null) {

                Point local = mouseFocusObject.absoluteToLocal(new Point(e.x,
                        e.y));
                mouseFocusObject.mouseMoving(local);

            } else {

                mouseFocusStack.remove(null);

            }

        }

    }

    public void controlMoved(ControlEvent event) {

        /* We're not responding to controlMoved events */

    }

    public void controlResized(ControlEvent event) {

        Rectangle canvasBounds = canvas.getBounds();
        setArea(new Rectangle(0, 0, canvasBounds.width, canvasBounds.height));
        if (HelpManager.getSingleton().isDisplayed()) {
            HelpManager.getSingleton().hideHelp();
        }

        PopUpManager.getSingleton().hidePopUp(
                PopUpManager.getSingleton().getCurrentPopUp());

    }

    public void setMouseFocusObject(AMouseListenerObject o) {

        mouseFocusStack.addLast(o);
        // System.out.println("Added "+o.getClass().getName()+" to mouse focus
        // stack, new size "+mouseFocusStack.size());

    }

    public void releaseMouseFocusObject(AMouseListenerObject o) {

        if (mouseFocusStack.contains(o)) {

            mouseFocusStack.remove(o);

            /*
             * System.out.println("Removed "+o.getClass().getName()+" from mouse
             * focus stack, new size "+mouseFocusStack.size()); } else {
             * 
             * System.out.println("Attempted to remove
             * "+o.getClass().getName()+" but it didn't exist, new size
             * "+mouseFocusStack.size());
             */

        }

    }

    public void dispose() {

        AnimationManager.getSingleton().dispose();

    }

}
