/*
   Tamot --- Task Modelling Editor
   Copyright (C) 2000 Commonwealth Scientific and Industrial Research Organisation
   (CSIRO), Australia. All rights reserved. 

   The Software, owned by CSIRO Australia, has been developed by the CSIRO 
   Intelligent Interactive Technology group. 
   For more information, please see http://www.cmis.csiro.au/iit/.

   Redistribution in source and binary forms, with or without modification, are not permitted.

   The Software is a beta-test version for internal research and evaluation purposes 
   by the Licensee. The Software is still at a development stage. 
   It has not undergone complete testing and may have inherent errors,
   bugs or deficiencies that can affect the operation of the Software. 

   We encourage your feedback and suggestions. Any bugs / suggestions found please email to
   Shijian.Lu@cmis.csiro.au and Thomas.Lo@cmis.csiro.au. 

   After you have signed the Beta-Test Software License Agreement with the CSIRO,
   CSIRO grants you a non-exclusive, non-transferable license to use it only for your 
   personal, non-commercial and lawful end use. Implied licenses are negated. 

   Warranty Disclaimer : 

   CSIRO supplies the Software on an "as is" basis and makes no warranties that use of
   the Software by the Licensee will not infringe any third party’s 
   Intellectual Property Rights nor that the Software will be of merchantable quality,
   or suitable for a particular purpose. CSIRO excludes all terms, conditions and
   warranties implied by custom, the general law or statute in relation to the
   Software.
*/

package taskModellingTool;

import java.util.*;
import java.io.*;
import java.awt.*;
import java.awt.geom.*;

/*
mouse release : 

    if (intersection(gcSel)) {
        // Manager.intersection(gcSel)) is possible too,
        //it uses the kernel instead of the JPanel list.
        // Probably slower.
        gcSel.setLocation(initialX,initialY);
        
        
        repaint();
     }
        
     if (gcSel instanceof LinkableComponent){
         LinkableComponent lc=(LinkableComponent)gcSel;
         lc.backupX=lc.getX();
         lc.backupY=lc.getY();
     }
     OkForMoving=false;
*/

// manager of multiple components selection and movement using mouse dragging
public class MultipleComponents {
    private static int beginX, beginY; // (beginX,beginY) beginning coordinate of mouse dragging
    private static int endX, endY;     // (endX,endY) last coordinate of mouse dragging
    private static int cursorx, cursory;     // (cursorx,cursory) last cursor coordinate
    
    private static boolean okForMoving;  // true to allow to move the selected components together
    
    private static boolean okForSelecting;  // true to allow to select components by dragging
    
    private static int lastX = -1;           // last (lastX,lastY) mouse coordinate of moving multiple components
    private static int lastY = -1;

    private static final Color ENCLOSING_RECTANGLE_COLOR = Color.gray;
    
    private static class InitialCoordinate {
        int initialX;
        int initialY;
        
        public InitialCoordinate(int initialX, int initialY) {
            this.initialX = initialX;
            this.initialY = initialY;
        }
    }

    private static TreeMap initialCoordinateTreeMap = new TreeMap();  // id2 (key), initialCoordinate (value)
    
    
    public static void reset() {
        //TMUtility.display("reset","");
        
        // variable for moving
        okForMoving = false;
        
        // variables for selection
        beginX = -1;
        beginY = -1;
        endX = -1;
        endY = -1;
        
        okForSelecting = false;
    }

    public static void mouseDraggedToMove(int x, int y, GraphicPanel gp) {
        //TMUtility.display("mouseDraggedToMove",okForMoving);
        
        if (!okForMoving) return;
        
        LinkableComponent [] lctab = BottomUp.getLinkableComponents();
        
        int tempLastX = lastX;     // backup (lastX,lastY) so that it is updated once only
        int tempLastY = lastY;
        
        for (int i=0; i<lctab.length; i++) {
            LinkableComponent lc = lctab[i];
            
            //TMUtility.displayLinkableComponent("mouseDraggedToMove",lc);
            
            int lcX = lc.getX();
            int lcY = lc.getY();
            
            int deltaX = x-lastX;
            int deltaY = y-lastY;
            
            if ((lcX+deltaX) >= 0) tempLastX = x;
            else {
                tempLastX -= lcX;
                deltaX = -lcX;
            }
            
            if ((lcY+deltaY) >= 0) tempLastY = y;
            else {
                tempLastY -= lcY;
                deltaY = -lcY;
            }
            
            updateLocation(lc,deltaX,deltaY,gp);
        }
        
        lastX = tempLastX;
        lastY = tempLastY;
    }
    
    public static void mouseDraggedToSelect(int x, int y, GraphicPanel gp) {
        if (!okForSelecting) return;
        
        //TMUtility.display("mouseDraggedToSelect","");
        
        if ((beginX < 0) || (beginY < 0)) {
            //TMUtility.display("mouseDraggedToSelect","resetAllSelection");
            BottomUp.resetAllSelection();
        
            beginX = x;
            beginY = y;
            
            okForSelecting = true;  // restore to true as it is reset by resetAllSelection()

            //TMUtility.display("mouseDragged : beginX",beginX);
            //TMUtility.display("mouseDragged : beginY",beginY);
            
            cursorx = x;
            cursory = y;
        } else {
            // clear previous rectangle only if cursor move a bit
            int distancex = Math.abs(cursorx - x);
            int distancey = Math.abs(cursory - y);
            if ((distancex+distancey) > 3) {
                gp.repaint();

                //TMUtility.display("mouseDragged","repaint");
                
                // remember previous cursor position
                cursorx = x;
                cursory = y;
            }

            // display the rectangle
            displayDraggingWindow(beginX,beginY,x,y,gp);
        }
    }
    
    public static void checkOKForMoving(LinkableComponent lc, int x, int y) {
        if (BottomUp.isComponentExistInShiftRegister(lc)) {
            okForMoving = true;
            
            lastX = x;
            lastY = y;
            
            // register old coordinates of all multiple components selected
            registerOldCoordinates();
        }
    }
    
    public static void setOKForSelecting() {
        //TMUtility.display("setOKForSelecting","");
        okForSelecting = true;
    }
    
    private static void registerOldCoordinates() {
        initialCoordinateTreeMap.clear();
        
        BottomUp.updateLinkableComponentRegister();
        
        LinkableComponent [] lctab = BottomUp.getLinkableComponents();
        
        //TMUtility.display("registerOldCoordinates",lctab.length);
        
        for (int i=0; i<lctab.length; i++) {
            LinkableComponent lc = lctab[i];
            
            Integer id2 = new Integer(lc.ID);
            int initialX = lc.getX();
            int initialY = lc.getY();
            InitialCoordinate initial = new InitialCoordinate(initialX,initialY);
            
            initialCoordinateTreeMap.put(id2,initial);

            //TMUtility.display("registerOldCoordinates "+lc.getName(),initialX);
        }
    }

    private static void restoreInitialCoordinates(LinkableComponent lc) {
        Integer id2 = new Integer(lc.ID);
        
        if (initialCoordinateTreeMap.containsKey(id2)) {
            InitialCoordinate initial = (InitialCoordinate) initialCoordinateTreeMap.get(id2);
            
            lc.setLocation(initial.initialX,initial.initialY);

            //TMUtility.display("restoreInitialCoordinates "+lc.getName(),initial.initialX);
        }
    }

    // Updates the coordinates representing the location of the component.
    private static void updateLocation(LinkableComponent lc, int deltaX, int deltaY, GraphicPanel gp) {
        resizePanel(lc,deltaX,deltaY,gp);
        lc.setLocation(lc.getX()+deltaX, lc.getY()+deltaY); 
        gp.repaint();
    }
     
    private static void resizePanel(LinkableComponent lc, int deltaX, int deltaY, GraphicPanel gp) {
        int x = lc.getX()+deltaX;
        int y = lc.getY()+deltaY;
        int w = lc.getWidth();
        int h = lc.getHeight();
        
        Dimension size = new Dimension();

        boolean changed = false;
        
        if ((x+w) > gp.getWidth()) {
            size.width = x+w;
            size.height = gp.getHeight();
            changed = true;
        }
        
        if ((y+h) > gp.getHeight()) {
            size.width = gp.getWidth();
            size.height = y+h;
            changed = true;
        }
        
        if (changed) {
            //Update client's preferred size
            gp.setPreferredSize(size);
        }
        
        gp.scrollRectToVisible(new Rectangle(x,y,w,h));
        
        //Let the scroll pane know to update itself and its scrollbars.
        if (changed) gp.revalidate();
    }
    
    private static void displayDraggingWindow(int posx1,int posy1,int posx2,int posy2, GraphicPanel gp) {
        Graphics g = gp.getGraphics();
        Graphics2D g2 = (Graphics2D) g;
        
        g2.setColor(ENCLOSING_RECTANGLE_COLOR);
        
        double width = posx2 - posx1;
        double height = posy2 - posy1;
        
        Rectangle2D.Double rectangle = new Rectangle2D.Double(posx1,posy1,width,height);
        g2.draw(rectangle);
    }
    
    public static void mouseReleased(int x, int y, GraphicPanel gp) {
        if (okForMoving) {
            okForMoving = false;
            
            // for moving multiple components
            LinkableComponent [] lctab = BottomUp.getLinkableComponents();
            
            boolean interactionFlag = intersection(lctab,gp);
            
            for (int i=0; i<lctab.length; i++) {
                LinkableComponent lc = lctab[i];
                
                if (interactionFlag) restoreInitialCoordinates(lc);
                    
                lc.backupX = lc.getX();
                lc.backupY = lc.getY();
            }

            gp.repaint();
        }
        
        if (okForSelecting) {
            okForSelecting = false;
            
            // for dragging multiple components
            if ((beginX < 0) || (beginY < 0)) return;
            
            endX = x;
            endY = y;
    
            //TMUtility.display("mouseReleased : endX",endX);
            //TMUtility.display("mouseReleased : endY",endY);
    
            gp.repaint();  // clear the rectangle
    
            checkComponentsSelectedByMouseDragging(gp);
        }
    }
    
    private static boolean intersection(LinkableComponent [] lctab, GraphicPanel gp) {
        boolean result = false;
        
        for (int i=0; i<lctab.length; i++) {
            LinkableComponent lc = lctab[i];
            
            if (intersection(lc,gp)) {
                result = true;
                break;
            }
        }
        
        //TMUtility.display("intersection",result);
        
        return result;
    }

    public static boolean intersection(TreeMap treeMap, GraphicPanel gp) {
        boolean result = false;
        
        Set keys = treeMap.keySet();
        for (Iterator it = keys.iterator();it.hasNext();) {
            Integer id2 = (Integer) it.next();
            LinkableComponent lc = (LinkableComponent) treeMap.get(id2);
            
            if (intersection(lc,gp)) {
                result = true;
                break;
            }
        }

        return result;
    }

    public static boolean intersection(GenericComponent gc, GraphicPanel gp) {
        boolean result = false;
        
        for (int i=0; i<gp.getComponentCount(); i++) {
            GenericComponent gc2 = (GenericComponent) gp.getComponent(i);
            
            if (gc2 != gc) {
                if (gc2.intersection(gc)) {
                    result = true;
                    break;
                }
            }
        }
        
        return result;    
    }

    private static void checkComponentsSelectedByMouseDragging(GraphicPanel gp) {
        final int OFFSET = 15;   // offset detection point from the top-left corner of a component
        
        String parentName = gp.name;

        int backupBeginX = beginX;
        int backupBeginY = beginY;
        int backupEndX = endX;
        int backupEndY = endY;
        
        int length = gp.getComponentCount();

        for (int i=0; i<length; i++) {
            Component comp = gp.getComponent(i);
            
            if (comp instanceof LinkableComponent) {
                LinkableComponent lc = (LinkableComponent) comp;
                
                int x = lc.getX() + OFFSET;
                int y = lc.getY() + OFFSET;
                
                //TMUtility.display("checkComponentsSelectedByMouseDragging : x",x);
                //TMUtility.display("checkComponentsSelectedByMouseDragging : y",y);

                if ( ((backupBeginX <= x) && (backupBeginY <= y)) && 
                    ((x <= backupEndX) && (y <= backupEndY)) ) {
                    //TMUtility.displayLinkableComponent("checkComponentsSelectedByMouseDragging",lc);
                        
                    BottomUp.select(lc,parentName);
                }
            }
        }
        
        // select and display links selected
        BottomUp.updateRegisters();
        BottomUp.updateLinksDisplay();
    }

}