/*
   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.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.print.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.tree.TreePath;
import java.util.*;
import java.lang.Object;

/**
 * Manages the actions on GenericComponents within a frame.
 * <P><A HREF="../source/GraphicPanel.html#GraphicPanel_beginning">Source</A>
 */
///GraphicPanel_beginning
//Thomas
public class GraphicPanel extends JPanel implements 
	KeyListener,MouseListener,MouseMotionListener,Printable {
    
    //the component currently selected
    protected GenericComponent gcSel=null;
    protected LinkableComponent firstComponentToLink=null;
    
    protected String name;   

    // Holds the coordinates of the user's last mousePressed event.
    int lastX, lastY;
    int initialX, initialY;

    boolean OkForMoving=false;
    
    protected JScrollPane scrollPane;
    
    private long firstClickTime = 0;
    private Component selectedComponent = null;
    
    
    public GraphicPanel(String name) {
    	this.name=name;
    	setBackground(Color.white);
    	setLayout(null);
    	scrollPane=(JScrollPane)getParent();
    	
    	addKeyListener(this);
    	
        addMouseMotionListener(this);
        addMouseListener(this);
        addMouseListener(new PopupListener()); 
    }

    public void keyTyped(KeyEvent e) {
      //System.out.println("KEY TYPED: ");
    }
    
    // allow 'Delete' key to delete currently selected task(s)
    public void keyPressed(KeyEvent e) {
        //System.out.println("KEY PRESSED: ");
        int keyCode = e.getKeyCode();
        
        // need to detect inidividual component and multiple components' deletion
        if (keyCode == e.VK_DELETE) {
      	    //System.out.println("Delete key detected !");
   	        DeleteManager.delete();
        }
    }

    public void keyReleased(KeyEvent e) {
        //System.out.println("KEY RELEASED: ");
    }


    /**
     * Handles the event of the user pressing down the mouse button.
     * <p>External operations:
     * <ol>
     *<li>Simple click on GenericComponent -> Manager.select(<i>this comp</i>);
     *<li>Double click on GenericComponent -> Manager.select(<i>this comp</i>); Manager.openComposite(<i>this comp</i>);
     *<li>Otherwise Manager.deselect(<i> this panel</i>);
     *</ol> 
     * <P><A HREF="../source/GraphicPanel.html#mousePressed">Source</A>
     */
    ///mousePressed
    public void mousePressed(MouseEvent e) {
    	this.requestFocus();
        //System.out.println("mouse pressed in GraphicPanel");

        long clickTime = System.currentTimeMillis();
        long clickInterval = clickTime - firstClickTime;
        boolean doubleClickFlag;

    	int m=e.getModifiers();
     	Component c = getComponentAt(e.getX(),e.getY());
     	
     	if (c.equals(selectedComponent)) {
	        if (clickInterval < 1200) {
    	    	doubleClickFlag = true;   // double click if both clicks within 1.2 sec.
	        }
    	    else {
        		doubleClickFlag = false;
        	}
     	}
     	else {
     		selectedComponent = c;
       		doubleClickFlag = false;
     	}
   	    firstClickTime = clickTime;
   	    
   	    //System.out.println("Double Click = "+doubleClickFlag+", time = "+clickInterval+" msec.");

     	c.setCursor(Manager.waitCursor);
    	        
        
     	if (e.isPopupTrigger()) {
     	}
        
        //CTRL, SHIFT, BUTTON3, BUTTON2, BUTTON1 .. ALT, META
        boolean leftClickFlag = ((m&InputEvent.BUTTON1_MASK) != 0);
        //System.out.println("leftClickFlag = "+leftClickFlag);
        
        boolean rightClickFlag = ((m&InputEvent.BUTTON3_MASK) != 0);
        //System.out.println("rightClickFlag = "+rightClickFlag);
        
        boolean shiftFlag = ((m&InputEvent.SHIFT_MASK) != 0);
        //TMUtility.display("mousePressed : shiftFlag",shiftFlag);
        
        //TMUtility.display("mousePressed --- name",name);
        //TMUtility.display("                 lastParentName",BottomUp.lastParentName);
        
        if (BottomUp.isParentChanged(name)) {
            BottomUp.resetAllSelection();
            BottomUp.setParent(name);
            //TMUtility.display("mousePressed","reset1");
        } else {
            if (!shiftFlag) {
                //  without Shift pressed
                if (BottomUp.isComponentExistInShiftRegister(c)) {
                    // click on a selected component -> multiple components still selected
                } else {
                    // click on an unselected component or a blank area
                    BottomUp.resetAllSelection();
                    BottomUp.setParent(name);
                    //TMUtility.display("mousePressed","reset2");
                }
            }
        }
        
        //TMUtility.display("GraphicPanel : width",getWidth());
        //TMUtility.display("               height",getHeight());
        
        bringFrameToTop(name);
        
     	if (leftClickFlag || rightClickFlag) {
    	    if (c instanceof GenericComponent) {
    	        gcSel = (GenericComponent)c;
    	        
    	        Integer id2 = new Integer(gcSel.ID);
    	        
    	        // Component selected
                //System.out.println("\""+gcSel.getName()+"\" : ID = "+id2);
                TMUtility.displayGenericComponent(gcSel);
    	        
    	        TreePath treePath = Manager.fetchTreePath(gcSel.getName(),name,Manager.tree,id2);
    	        
    	        //if (e.getClickCount()==1) {
    	        if (!doubleClickFlag) {
    	        	//single click
    	        	//System.out.println("1st click");
    	    		//boolean bb1 = gcSel.isSelected();
    	    		//boolean bb2 = (gcSel==Manager.currentComponent);
    	    		Manager.select(gcSel,treePath,id2);
                    
                    if (shiftFlag) {
                        if (rightClickFlag) 
                            BottomUp.select(gcSel,name);
                        else
                            BottomUp.toggleSelect(gcSel,name);
                    }
    	        }
    	        else {
    	        //else if (e.getClickCount()==2) {
    	        	//double-click
					//Toolkit.getDefaultToolkit().beep();
    	        	//System.out.println("2nd click");
                    //System.out.println("Selecting: "+gcSel.getName()+", "+name);
                    
                    Manager.select(gcSel,treePath,id2);
                    if (leftClickFlag) {
                        Manager.openComposite(gcSel,name);
                        
                        //in order to point to the correct node in tree after openComposite for duplicated task in same window
                        Manager.selectNode(treePath);
                    }
    	        }
    	    }
    	    else {
    	        gcSel=null;
    	        Manager.deselect(this);
    	    }
            
            if (leftClickFlag) {
                if (gcSel != null) {
                    if (gcSel instanceof LinkableComponent) goAbove(gcSel);
                    repaint();
                    lastX=e.getX();
                    lastY=e.getY();
                    initialX=gcSel.getX();
                    initialY=gcSel.getY();
                    OkForMoving=true;
                    
                    if (gcSel instanceof LinkableComponent)
                        MultipleComponents.checkOKForMoving((LinkableComponent) gcSel,e.getX(),e.getY());
                } else {
                    MultipleComponents.setOKForSelecting();
                }
            }
		} 
		else if ((m&InputEvent.BUTTON2_MASK)!=0){
	    	if (c instanceof LinkableComponent)
	        	firstComponentToLink=(LinkableComponent)c;
		}

        c.setCursor(Manager.normalCursor);
        
        //shijian 2001-3-21
        Manager.menu.setOptions(gcSel);
        //maybeShowPopup(e);

    }
    
    // to fix changes from JDK v.1.22 to v.1.3
    private void bringFrameToTop(String name) {
        Manager.desktop.selectOnly(name);
        
        JInternalFrame frame = Manager.desktop.fetchFrame(name);
        
        //TMUtility.display("bringFrameToTop : visible : ",frame.isVisible());
        //TMUtility.display("bringFrameToTop : selected : ",frame.isSelected());
        
        frame.moveToFront();
    }

    /** 
     * Handles the event of a user dragging the mouse while holding
     * down the mouse button.
     * <p>Only internal operations.
     * <P><A HREF="../source/GraphicPanel.html#mouseDragged">Source</A>
     */
    ///mouseDragged
    public void mouseDragged(MouseEvent e) {	
        if (OkForMoving) {
            // click on a component
            if (BottomUp.isMultiComponentsSelected()) {
                MultipleComponents.mouseDraggedToMove(e.getX(),e.getY(),this);
            } else {
                if (gcSel == null) {
                    System.out.println("mouseDragged : null pointer");
                    
                    OkForMoving = false;
                    return;
                }
                
                int eX=e.getX();
                int eY=e.getY();
                int gcX=gcSel.getX();
                int gcY=gcSel.getY();
                
                int deltaX=eX-lastX;
                int deltaY=eY-lastY;
                
                if ((gcX+deltaX)>=0) lastX=eX;
                else {
                    lastX-=gcX;
                    deltaX=-gcX;
                }
                
                if ((gcY+deltaY)>=0) lastY=eY;
                else {
                    lastY-=gcY;
                    deltaY=-gcY;
                }
                
                //TMUtility.display("mouseDragged  : eX",eX);
                //TMUtility.display("                eY",eY);
                //TMUtility.display("                gcX",gcX);
                //TMUtility.display("                gcY",gcY);
                //TMUtility.display("                lastX",lastX);
                //TMUtility.display("                lastY",lastY);
                //TMUtility.display("                deltaX",deltaX);
                //TMUtility.display("                deltaY",deltaY);
                
                updateLocation(deltaX,deltaY);
            }
        } else {
            // click on an empty space
            MultipleComponents.mouseDraggedToSelect(e.getX(),e.getY(),this);
        }
    }

    /**
     * Handles the event of a user releasing the mouse button.
     *<p>Only internal operations
     * <P><A HREF="../source/GraphicPanel.html#mouseReleased">Source</A>
     */
    ///mouseReleased
    public void mouseReleased(MouseEvent e) {
        //System.out.println("mouse released");
    	int m = e.getModifiers();
     	Component c = getComponentAt(e.getX(),e.getY());
        
     	if ((m&InputEvent.BUTTON1_MASK) != 0) {
            // Checks whether the mouse pointer is inside of the component
            // when the user releases the mouse button.
            if (OkForMoving) {
                if (!BottomUp.isMultiComponentsSelected()) {
                    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;
            }
        } else if ((m&InputEvent.BUTTON2_MASK)!=0) {
		    if (c instanceof LinkableComponent) {
		        LinkableComponent lc=(LinkableComponent)c;
		        if ((firstComponentToLink!=null)&&(lc!=null)&&(lc!=firstComponentToLink)) {
                    //TMUtility.display("mouseReleased","NewManager.newLink");
                    
		            NewManager.newLink(firstComponentToLink,lc,name);
		            firstComponentToLink=null;
	    	    }
		    }
		}
		
		//shijian 2001-3-21
		maybeShowPopup(e);

        MultipleComponents.mouseReleased(e.getX(),e.getY(),this);
    }
       
    /**
     * This method is required by MouseListener. Not used here.
     * <P><A HREF="../source/GraphicPanel.html#mouseMoved">Source</A>
     */
    ///mouseMoved
     public void mouseMoved(MouseEvent e){}

    /**
     * This method is required by MouseMotionListener. Not used here.
     * <P><A HREF="../source/GraphicPanel.html#mouseClicked">Source</A>
     */
    ///mouseClicked
     public void mouseClicked(MouseEvent e){}
    /**
     * This method is required by MouseMotionListener. Not used here.
     * <P><A HREF="../source/GraphicPanel.html#mouseExited">Source</A>
     */
    ///mouseExited
     public void mouseExited(MouseEvent e){
     }
     
    /**
     * This method ../source/GraphicPanel.html#mouseEntered">Source</A>
     */
    ///mouseEntered
     public void mouseEntered(MouseEvent e){
     }
        
     // Updates the coordinates representing the location of the current component.
     private void updateLocation(int deltaX,int deltaY) {
     	resizePanel(deltaX,deltaY);
        gcSel.setLocation(gcSel.getX()+deltaX,gcSel.getY()+deltaY); 
	    repaint();
     }
     
     private void goAbove(GenericComponent gcSel){
     	remove(gcSel);
     	add(gcSel,0);
     }


    public void paintComponent(Graphics g) {
        super.paintComponent(g);
    	
    	int length=getComponentCount();

    	//System.out.println("GraphicPanel : paintComponent, length = "+length);

        Component comp;
        for (int i=0;i<length;i++) {
            comp=getComponent(i);
            if (comp instanceof Link){
                ((Link)comp).updateLink();
            }
        }
    }
    
    public void updateLinks(){
        int length=getComponentCount();
        Component comp;
        for (int i=0;i<length;i++){
            comp=getComponent(i);
            if (comp instanceof Link) ((Link)comp).updateLink();
        }
    }



    private boolean intersection(GenericComponent gc) {
    	boolean result=false;
        
        if (gc == null) {
            System.out.println("Intersection : null pointer");
        }
        else {
            for (int i=1;i<getComponentCount();i++) {
                GenericComponent gc2 = (GenericComponent) getComponent(i);
            
                if (gc2.intersection(gc)) {
                    result=true;
                    break;
                }

                //TMUtility.display("    intersection : "+gc.getName()+" - "+gc2.getName(),result);
            }
        }
        
    	return result;	
    }

    
    private boolean allowedToMove(int deltaX,int deltaY){
    	return !(((gcSel.getX()+deltaX)<0)||((gcSel.getY()+deltaY)<0));
    }
    
    private void resizePanel(int deltaX,int deltaY){
    	int x=gcSel.getX()+deltaX;
    	int y=gcSel.getY()+deltaY;
    	int w=gcSel.getWidth();
    	int h=gcSel.getHeight();
    	Dimension size=new Dimension();
    	    

    	boolean changed=false;
    	
    	if ((x+w)>getWidth()){

    	    size.width=x+w;
    	    size.height=getHeight();
    	    changed=true;
    	}
    	if ((y+h)>getHeight()){
    	    //System.out.println("Panel height=" + getHeight());
    	    //System.out.println("task height="+ h);	
    	    size.width=getWidth();
    	    size.height=y+h;
    	    changed=true;
    	}
        if (changed) {
            //Update client's preferred size
            setPreferredSize(size);
        }
        
    	scrollRectToVisible(new Rectangle(x,y,w,h));
    	
        //Let the scroll pane know to update itself
        //and its scrollbars.
        //revalidate();
    	if (changed) revalidate();
    	    
    }
    
    public int print(Graphics g, PageFormat pf, int pi) throws PrinterException{
     	if (pi >= 1) {
     		return Printable.NO_SUCH_PAGE;
     	}
	 	drawComponents((Graphics2D)g);

	 	return Printable.PAGE_EXISTS;
    }
     
    private Rectangle getSmallestBounds(){
    	int nb=getComponentCount();
    	int minx=0;
    	int miny=0;
    	int maxx=0;
    	int maxy=0;
    	int tempx,tempy,tempx2,tempy2;
    	Component comp;

    	for (int i=0;i<nb;i++) {
    	    comp=getComponent(i);
    	    tempx=comp.getX();
    	    tempy=comp.getY();
    	    tempx2=comp.getWidth()+tempx;
    	    tempy2=comp.getHeight()+tempy;
    	    if (tempx<minx) minx=tempx;
    	    if (tempy<miny) miny=tempy;
    	    if (tempx2>maxx) maxx=tempx2;
    	    if (tempy2>maxy) maxy=tempy2;
    	}
    	
    	return (new Rectangle(minx,miny,maxx-minx,maxy-miny));
    }
    
    private void drawComponents(Graphics2D g2) {
    	System.out.println("GraphicPanel : drawComponents");
    	
    	Rectangle printBounds=g2.getClipBounds();
    	Rectangle panelBounds=getSmallestBounds();    	
    	AffineTransform at=new AffineTransform(
    		((double)printBounds.width)/((double)panelBounds.width),0,0,
    		((double)printBounds.height)/((double)panelBounds.height),
    		printBounds.x-panelBounds.x,printBounds.y-panelBounds.y);
    	//scaling and translation
    	g2.transform(at);
    	paintChildren(g2);
    }   
    
    //shijian 2001-3-21
    private void maybeShowPopup(MouseEvent e) {
    /*	MainMenu menu = Manager.menu;
    	double xLoc = e.getX();
		double yLoc = e.getY();
		System.out.println("x= " + xLoc+ " y = "+ yLoc);
		
        if (e.isPopupTrigger()) {
        	//13-11-2001
        	
			Dimension popupSize = Manager.popup.getPreferredSize();
			Point topLeft= e.getComponent().getLocationOnScreen();
		
			System.out.println("component name= " + e.getComponent().getName()); 
			System.out.println("component x= " + topLeft.getX()+ 
								" component y = "+ topLeft.getY());
			System.out.println("screen x= " + _screenSize.width+ 
								" screen y = "+ _screenSize.height);
			System.out.println("pop x= " + popupSize.width+ 
								" pop y = "+ popupSize.height);

			if ((topLeft.getX() + xLoc+popupSize.width) >
        		_screenSize.width){
        		xLoc = _screenSize.width - popupSize.width;
        				//topLeft.getX() - popupSize.width;
        	}
        	else
        		xLoc = xLoc+ topLeft.getX();
        	if ((topLeft.getY() + yLoc+popupSize.height) >
        		_screenSize.height){
        		yLoc = _screenSize.height - popupSize.height;
        			//topLeft.getY() - popupSize.height;
        	}
        	else
        		yLoc = yLoc+ topLeft.getY();
        		
			System.out.println("xnew = " + xLoc+ " ynew = "+ yLoc);
			Manager.popup.setLocation(((int)xLoc), ((int)xLoc));
			Manager.popup.setVisible(true);
            //Manager.popup.show(e.getComponent(),((int)xLoc), ((int)xLoc));
            //Manager.popup.show(e.getComponent(),e.getX(), e.getY());
        }
    */
    }
    
}