/*
   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/.

   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.image.*;
import java.awt.geom.*;
import java.awt.font.*;
import javax.swing.ImageIcon;
import java.io.*;
import java.lang.reflect.*;

public class Link extends GenericComponent {

    
    private static final Color BACKGROUND_ICON_COLOR=new Color(200,200,200);
    protected final static Color COLOR=new Color(128,128,128);
    protected final static Color SELECTED_COLOR=new Color(250,200,200);//new Color(150,50,50);
    protected final static Color BACKGROUND_SELECTED_COLOR=SELECTED_COLOR;
    protected final static double HALF_WIDTH_RATIO=0.25;
    protected final static double CONDITION_SIZE_RATIO=0.4;
    protected final static double CONDITION_MARGIN_RATIO=0.2;
    protected final static double CONDITION_OFFSET_RATIO=CONDITION_SIZE_RATIO*0.3;    
    protected final static double CONDITION_GAP_X_RATIO=0.4;
    protected final static double CONDITION_GAP_Y_RATIO=0.1;
    protected final static String REF_STRING="Link";
    
    private static final float STROKE_SIZE=1f;

    
    protected static Object [][] attributesToDisplay={{"name"},{"condition"}};
    
    protected static boolean [] userAttributes={true,	true};
    
    protected static Object [] dialogsFields={"condition"};
    
    private static Object [] cloneFields={};
    
    //shijian 2001-3-21
    // for storing action-instance ID for precondition 
    private String _preconDM="";

    //Link's attributes    
    
    public String condition="";
    
    //public static int linkCount=0;
    protected LinkableComponent lc1=null;
    protected LinkableComponent lc2=null;
    
    //Used when externalized
    protected boolean partlyConstructed=false;
    protected String name1;
    protected String name2;
    protected String parentName;
    
    protected int id1_XML = 0;    // ID of lc1 read from XML
    protected int id2_XML = 0;    // ID of lc2 read from XML

    protected int x1,x2,y1,y2;
    //private double tx,ty,th,tw;
    
    private TextLayout layout = null;
    private Shape text = null;
    private Shape line1 = null;
    private Shape line2 = null;
    //private Rectangle2D.Double textRectangle=null;
    private boolean cutArrow = false;
    
    
    public Link(LinkableComponent lc1,LinkableComponent lc2,String parentName) {
    	super(REF_STRING);
        
    	this.parentName = parentName;
        
    	createLink(lc1,lc2);
    	updateLink();
    }
    

    //copy constructor
    public Link(Link lc,int index,LinkableComponent lc1,LinkableComponent lc2,String parentName) {
    	super(REF_STRING,index);
    	this.parentName=parentName;
        String tmpName = name;
    	//name=lc.name; Not the name
        
    	Class cl=lc.getClass();
    	Field field;
    	String type;
    	for (int i=0;i<cloneFields.length;i++) {
    	   try {
    	       field=(cl.getField((String)cloneFields[i]));
    	       type=field.getType().getName();
    	       if (type.equals("java.lang.String"))
    	           field.set(this,field.get(lc));
    	       else if (type.equals("int"))
    	           field.setInt(this,field.getInt(lc));
    	       else if (type.equals("boolean")) {
    	           field.setBoolean(this,field.getBoolean(lc));
    	           System.out.println("Fields: original: "+field.getBoolean(lc)+"   new: "+field.getBoolean(this));
               }
           }
           catch (Exception except) {
               System.err.println(except.toString());
           }
        }
        
        //restore name
        name = tmpName;
        
        createLink(lc1,lc2);
        updateLink();
    }

    //--------------------------- IO ------------------------------/
    //for externalization purposes    
    public Link() {
    	super();
    	partlyConstructed=true;
    }

    public Link(int index) {
    	super(REF_STRING,index);
    	partlyConstructed=true;
    }

    
    public void writeExternal(ObjectOutput out) throws IOException{
    	super.writeExternal(out);
        out.writeObject(lc1);
        out.writeObject(lc2);
    	out.writeObject(lc1.getName());
    	out.writeObject(lc2.getName());
    	//out.writeObject(name1);
    	//out.writeObject(name2);
    	out.writeObject(parentName);
    	out.writeObject(condition);
    }
    
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{
    	super.readExternal(in);
    	
    	
        lc1= (LinkableComponent) in.readObject();
     	lc2=(LinkableComponent) in.readObject();
        name1=(String)in.readObject();
        name2=(String)in.readObject();
     	lc1.setName(name1);
     	lc2.setName(name2);
  
    	parentName=(String)in.readObject();
    	condition=(String)in.readObject();
       // this.parentName=parentName;
       // createLink(lc1,lc2);
    	//updateLink();
    }
 
    public void writeXML(PrintStream o,int tabLevel) {
        o.print(getTABs(tabLevel)+"<Link ");
        o.print("id=\"_"+ID+"\" ");
        o.print("Parent_Task_Name=\""+parentName+"\" ");
        o.print("id1=\"_"+lc1.ID+"\" ");
        o.println("id2=\"_"+lc2.ID+"\">");
        o.println(getTABs(tabLevel+1)+"<Precondition>"+
            XMLUtility.convertForEntities(condition)+"</Precondition>");
        o.println(getTABs(tabLevel)+"</Link>");
    }
 
    public void writeASCII(PrintStream o){
    	o.println(FileManager.LINK);
    	o.println(parentName);
    	o.println(lc1.getName());
    	o.println(lc2.getName());
    	//o.println(name1);
    	//o.println(name2);
    	o.println(condition);
    	o.println();
    }
    
    public void readASCII(BufferedReader i){
    	try {
    	    parentName=i.readLine();
        } catch (Exception excep){}
    	try {
    	    name1=i.readLine();
        } catch (Exception excep){}
    	try {
    	    name2=i.readLine();
        } catch (Exception excep){}
    	try {
    	    condition=i.readLine();
        } catch (Exception excep){}
    }
    
    /*public static LinkedList readASCIIToList(BufferedReader i){
    	LinkedList list=new LinkedList();
    	try {
    	    list.add(i.readLine());
    	    list(i.readLine());
    	    list(i.readLine());
    	    list(i.readLine());
        } catch (Exception excep){}
        return list;
    }

    public Link(LinkedList list){
    	super(REF_STRING);
    	String name1=(String)list.removeFirst();
    	String lc2=(String)list.removeFirst();
    	String parentName=(String)list.removeFirst();
    	String lc1=(String)list.removeFirst();
    	createLink(lc1,lc2);
    	updateLink();
    }*/
    
    private void completeConstructionIfNecessary() {
    	//Experimental, no protection
    	if (partlyConstructed) {
    	    LinkableComponent llc1;
            LinkableComponent llc2;
            
            Integer id2;

            //TMUtility.display("completeConstructionIfNecessary : name1",name1);
            //TMUtility.display("completeConstructionIfNecessary : parentName",parentName);
            
            //TMUtility.display("completeConstructionIfNecessary : id1_XML",id1_XML);
            //TMUtility.display("completeConstructionIfNecessary : id2_XML",id2_XML);
        
            if (id1_XML > 0) {
                id2 = new Integer(id1_XML);
                llc1 = (LinkableComponent)Manager.skeleton.stringToGenericComponent(name1,parentName,id2);
            } else
                llc1 = (LinkableComponent)Manager.skeleton.stringToGenericComponent(name1,parentName);
            
            if (id2_XML > 0) {
                id2 = new Integer(id2_XML);
                llc2 = (LinkableComponent)Manager.skeleton.stringToGenericComponent(name2,parentName,id2);
            } else
                llc2 = (LinkableComponent)Manager.skeleton.stringToGenericComponent(name2,parentName);
            
            //TMUtility.displayLinkableComponent("completeConstructionIfNecessary : llc1",llc1);
            //TMUtility.displayLinkableComponent("completeConstructionIfNecessary : llc2",llc2);
            
            if (llc1 == null) {
                TMUtility.displayLine(2);
                TMUtility.display("*** completeConstructionIfNecessary : llc1 = null",
                    "name = "+name+", llc1 name = "+name1+", llc2 name = "+name2+", parentName = "+parentName);
                //Manager.skeleton.printRegister();
            }
            
    	    createLink(llc1,llc2);
            
    	    partlyConstructed = false;
    	    //updateLink();
    	}
    }

    public Object [][] getAttributesToDisplay(){
    	return attributesToDisplay;
    }
    
    public void setAttributesToDisplay(Object [][] o){
    	attributesToDisplay=o;
    }
    
    public boolean [] getUserAttributes(){
    	return userAttributes;
    }
    
    public void setUserAttributes(boolean[] o){
    	userAttributes=o;
    }
    
    public Object [] getDialogsFields(){
    	return dialogsFields;
    }
    
    public void setDialogsFields(Object [] o){
    	dialogsFields=o;
    }
    
    public Object [] getCloneFields(){
    	return cloneFields;
    }
    
    public void setCloneFields(Object [] o){
    	cloneFields=o;
    }
    



    private void createLink(LinkableComponent lc1,LinkableComponent lc2) {
    	this.lc1 = lc1;
    	this.lc2 = lc2;
        
    	name1 = lc1.getName();
        name2 = lc2.getName();
        
    	lc1.addLink(this);
    	lc2.addLink(this);
    }
    
    public void updateLink() {
    	completeConstructionIfNecessary();
        
    	int x10=lc1.getX();
    	int y10=lc1.getY();
    	int x20=lc2.getX();
    	int y20=lc2.getY();
    	double x1m=x10+((double)(lc1.getWidth()))/2;
    	double y1m=y10+((double)(lc1.getHeight()))/2;
    	double x2m=x20+((double)(lc2.getWidth()))/2;
    	double y2m=y20+((double)(lc2.getHeight()))/2;
    	double x1d,x2d,y1d,y2d;
    	x1d=200;y1d=200;x2d=250;y2d=250;
    	//now calculate x1,y1,x2,y2
    	
    	double v=x2m-x1m;
    	double v0=abs(v);
    	double w=y2m-y1m;    	
    	double w0=abs(w);
    	double tempv=(v>=0)?v:(-v);
    	double tempw=(w>=0)?w:(-w);
    	double tempx,tempy,tempx0,tempy0,tempx00,tempy00;
    	boolean inside,b;
    	//faster than Math.abs
    	
    	// lc1 side
    	if (!((tempv==0)&&(tempw==0))) {
    	    if (tempv>=tempw){
    	        v=(v>0)?1:(-1);
    	        w=w/tempv;
    	    }
    	    else {
    	        v=v/tempw;
    	        w=(w>0)?1:(-1);
    	    }
    	    
    	    tempx=x1m-x10;
    	    tempy=y1m-y10;
    	    //local coordinates
    	    
    	    tempx00=tempx;
    	    tempy00=tempy;
    	    tempx0=tempx;
    	    tempy0=tempy;
    	    inside=true;
    	    while (abs(tempx-tempx00)<v0){
    	    	b=lc1.area.contains(tempx,tempy);
    	    	if (inside&&(!b)){
    	    	    //quit shape
    	    	    inside=false;
    	    	    tempx0=tempx;
    	    	    tempy0=tempy;
    	    	}
    	    	else if (!(inside)&&b){
    	    	    //enter shape
    	    	    inside=true;
    	    	    tempx0=tempx;
    	    	    tempy0=tempy;
    	    	}
    	    	tempx+=v;
    	    	tempy+=w;
    	    }

    	    
    	    x1d=x10+tempx0;
    	    y1d=y10+tempy0;
    	}
    	    	    
        //lc2 side
        v=x1m-x2m;
        w=y1m-y2m;

    	if (!((tempv==0)&&(tempw==0))){
    	    if (tempv>=tempw){
    	        v=(v>0)?1:(-1);
    	        w=w/tempv;
    	    }
    	    else {
    	        v=v/tempw;
    	        w=(w>0)?1:(-1);
    	    }
    	    
    	    tempx=x2m-x20;
    	    tempy=y2m-y20;
    	    
    	    tempx00=tempx;
    	    tempy00=tempy;
    	    tempx0=tempx;
    	    tempy0=tempy;
    	    inside=true;
    	    while (abs(tempx-tempx00)<v0){
    	    	b=lc2.area.contains(tempx,tempy);
    	    	if (inside&&(!b)){
    	    	    //we are now outside
    	    	    inside=false;
    	    	    tempx0=tempx;
    	    	    tempy0=tempy;
    	    	}
    	    	else if (!(inside)&&b){
    	    	    //enter shape
    	    	    inside=true;
    	    	    tempx0=tempx;
    	    	    tempy0=tempy;
    	    	}
    	    	tempx+=v;
    	    	tempy+=w;
    	    }
    	    
    	    x2d=x20+tempx0;
    	    y2d=y20+tempy0;
    	}
    	    	
    	if (coordinatesOk(x1m,y1m,x2m,y2m,x1d,y1d,x2d,y2d)){
    	    setVisible(true);
    	    setAreaAndBounds((int)Math.round(x1d),(int)Math.round(y1d),(int)Math.round(x2d),(int)Math.round(y2d));
    	} 
    	else {
    	    setVisible(false);
    	}
    }
    
    private double abs(double d){
    	return (d>=0)?d:(-d);
    }
    
    //check int coordinates
    private boolean coordinatesOk(double x1m, double y1m, double x2m, double y2m, double x1, double y1, double x2, double y2){
    	boolean cOk;
    	double k,k1,k2,k12,v,w,v1,w1,v2,w2;
    	v=x2m-x1m;
    	w=y2m-y1m;
    	v1=x1-x1m;
    	w1=y1-y1m;
    	v2=x2m-x2;
    	w2=y2m-y2;
    	//no null vectors
    	cOk=((!((v==0)&&(w==0)))/*&&(!((v1==0)&&(w1==0)))&&(!((v2==0)&&(w2==0)))*/);
    	//in the good way
    	cOk=cOk&&((v1*v>=0)&&(w1*w>=0)&&(v2*v>=0)&&(w2*w>=0));
    	//good ratio
    	if (v!=0){
    	    k=(v>=0)?v:(-v);
    	    k1=(v1>=0)?v1:(-v1);
    	    k2=(v2>=0)?v2:(-v2);
    	    k12=v1+v2;
    	    k12=(k12>=0)?k12:(-k12);
    	    cOk=cOk&&(/*(k1>0)&&(k2>0)&&(k12>0)&&*/(k1<k)&&(k2<k)&&(k12<k));
    	} else if (w!=0){
    	    k=(w>=0)?w:(-w);
    	    k1=(w1>=0)?w1:(-w1);
    	    k2=(w2>=0)?w2:(-w2);
    	    k12=w1+w2;
    	    k12=(k12>=0)?k12:(-k12);
    	    cOk=cOk&&(/*(k1>0)&&(k2>0)&&(k12>0)&&*/(k1<k)&&(k2<k)&&(k12<k));

    	}
    	
    	return cOk;    
    }	

    private boolean checkIfEnoughSpaceToDisplayCondition(int x1, int y1, int x2, int y2) {
        boolean result = true;
        
        TextLayout tmpLayout = new TextLayout(condition,new Font("Helvetica",Font.PLAIN,r(CONDITION_SIZE_RATIO*u)),new FontRenderContext(null,true,false)); //transform, antialiased, fractional metrics
        Rectangle2D r = tmpLayout.getBounds();
        double thh = r.getHeight();
        double tww = r.getWidth();
        
        double centerX = ((double)(x1+x2))/2;
        double centerY = ((double)(y1+y2))/2;
        
        double twww = tww+CONDITION_GAP_X_RATIO*2*u;
        double thhh = thh+CONDITION_GAP_Y_RATIO*2*u;
        
        if (lc1.area.intersects(centerX-twww/2-lc1.getX(),centerY-thhh/2-lc1.getY(),twww,thhh)) result = false;
        if (lc2.area.intersects(centerX-twww/2-lc2.getX(),centerY-thhh/2-lc2.getY(),twww,thhh)) result = false;
        
        return result;
    }

    private void setAreaAndBounds(int x1, int y1, int x2, int y2) {
    	int tempx = x2-x1;
    	int tempy = y2-y1;
    	double length = Math.sqrt((double)(tempx*tempx+tempy*tempy));
    	int ecos = (int)Math.round(HALF_WIDTH_RATIO*u*tempx/length);
    	int esin = (int)Math.round(HALF_WIDTH_RATIO*u*tempy/length);

        int [] xpoints={x1-esin,x2-esin,x2+esin,x1+esin};
        int [] ypoints={y1+ecos,y2+ecos,y2-ecos,y1-ecos};
	
        //set the rectangular bounds
        int i;
        int minx = xpoints[0];
        int maxx = xpoints[0];
        for (i=1; i<xpoints.length; i++) {
            if (xpoints[i]<minx) minx = xpoints[i];
            else if (xpoints[i]>maxx) maxx = xpoints[i];
        }
    
        int miny = ypoints[0];
        int maxy = ypoints[0];
        for (i=1; i<ypoints.length; i++) {
            if (ypoints[i]<miny) miny = ypoints[i];
            else if (ypoints[i]>maxy) maxy = ypoints[i];
        }
        
        int lx = minx;    
        int ly = miny;
        int lwidth = maxx-minx;
        int lheight = maxy-miny;
	
        double tx,ty,th,tw;
        
        if (condition.equals(""))
            cutArrow = false;
        else {
            if (checkIfEnoughSpaceToDisplayCondition(x1,y1,x2,y2))
    	        layout = new TextLayout(condition,new Font("Helvetica",Font.PLAIN,r(CONDITION_SIZE_RATIO*u)),new FontRenderContext(null,true,false)); //transform, antialiased, fractional metrics
            else
                layout = new TextLayout("...",new Font("Helvetica",Font.PLAIN,r(CONDITION_SIZE_RATIO*u)),new FontRenderContext(null,true,false)); //transform, antialiased, fractional metrics
    	    Rectangle2D r = layout.getBounds();
            double thh = r.getHeight();
            double tww = r.getWidth();

            double centerX = ((double)(x1+x2))/2;
            double centerY = ((double)(y1+y2))/2;
            double txx = centerX-tww/2;
            double tyy = centerY+CONDITION_OFFSET_RATIO*u;
                        
            double twww = tww+CONDITION_GAP_X_RATIO*2*u;
            double thhh = thh+CONDITION_GAP_Y_RATIO*2*u;
            if ((!lc1.area.intersects(centerX-twww/2-lc1.getX(),centerY-thhh/2-lc1.getY(),twww,thhh)) && 
                (!lc2.area.intersects(centerX-twww/2-lc2.getX(),centerY-thhh/2-lc2.getY(),twww,thhh))) {
                cutArrow = true;
                
                th = thh+CONDITION_MARGIN_RATIO*2*u;
                tw = tww+CONDITION_MARGIN_RATIO*2*u;
                tx = txx-CONDITION_MARGIN_RATIO*u;
                ty = tyy-CONDITION_MARGIN_RATIO*u;
            
                int temp = r(tx);
                if (temp<lx) {
                    lwidth = lwidth+lx-temp;
                    lx = temp;
                }
                temp = r(ty);
                if (temp<ly) {
                    lheight = lheight+ly-temp;
                    ly = temp;
                }
                
                temp = r(tx+tw);
                if (temp>(lx+lwidth)) lwidth = Math.max(r(tw),temp-lx);
                temp = r(ty+th);
                if (temp>(ly+lheight)) lheight = Math.max(r(th),temp-ly);

                AffineTransform textAt = new AffineTransform();
                textAt.translate(txx-lx,tyy-ly);
                text = layout.getOutline(textAt);
            
                double x3,x4,y3,y4;
                double x1d = x1;
                double y1d = y1;
                double x2d = x2;
                double y2d = y2;
                /*double xlim=abs(tw*(length/Math.sqrt(th*th+tw*tw)-1)/2);
                double ylim=abs(th*(length/Math.sqrt(th*th+tw*tw)-1)/2);*/
            
                //assume length!=0
                double alpha0 = Math.atan(th/tw);
                double alpha;
                if (x2d==x1d) alpha = (y2d>y1d)?(Math.PI/2):(-Math.PI/2);
                else alpha = Math.atan((y2d-y1d)/(x2d-x1d));
                if (x2d<x1d) alpha = Math.PI+alpha;
            
                if (((-alpha0)<alpha)&&(alpha<=alpha0)) {
                     x3 = centerX-tw/2;
                     x4 = centerX+tw/2;
                     y3 = y1d+(x3-x1d)*(y2d-y1d)/(x2d-x1d);
                     y4 = y1d+(x4-x1d)*(y2d-y1d)/(x2d-x1d);                    
                } else if ((alpha0<alpha)&&(alpha<=(Math.PI-alpha0))) {
                     y3 = centerY-th/2;
                     y4 = centerY+th/2;
                     x3 = x1d+(y3-y1d)*(x2d-x1d)/(y2d-y1d);
                     x4 = x1d+(y4-y1d)*(x2d-x1d)/(y2d-y1d);
                } else if (((Math.PI-alpha0)<alpha)&&(alpha<=(Math.PI+alpha0))) {
                     x4 = centerX-tw/2;
                     x3 = centerX+tw/2;
                     y4 = y1d+(x4-x1d)*(y2d-y1d)/(x2d-x1d);
                     y3 = y1d+(x3-x1d)*(y2d-y1d)/(x2d-x1d);                    
                } else {
                     y4 = centerY-th/2;
                     y3 = centerY+th/2;
                     x4 = x1d+(y4-y1d)*(x2d-x1d)/(y2d-y1d);
                     x3 = x1d+(y3-y1d)*(x2d-x1d)/(y2d-y1d);                    
                }                
                line1 = new Line2D.Double(x1d-lx,y1d-ly,x3-lx,y3-ly);
                line2 = new Line2D.Double(x4-lx,y4-ly,x2d-lx,y2d-ly);
                //textRectangle=new Rectangle2D.Double(centerX-tw/2-lx,centerY-th/2-ly,tw,th);
            }
            else cutArrow = false;                     
    	}
        
	    setBounds(lx,ly,lwidth,lheight);

        //set the area
        for (i=0; i<xpoints.length; i++)
            xpoints[i] -= lx;
        for (i=0; i<ypoints.length; i++)
            ypoints[i] -= ly;
    	area = new Area(new Polygon(xpoints,ypoints,4));
	
        this.x1 = x1-lx;
        this.y1 = y1-ly;
        this.x2 = x2-lx;
        this.y2 = y2-ly;
    }
        
    private void drawLink(Graphics2D g2){
        if (isShiftSelected()) g2.setColor(BottomUp.BACKGROUND_SHIFT_SELECTED_COLOR);
    	else if (selected) g2.setColor(SELECTED_COLOR);
    	else g2.setColor(COLOR);
        
    	g2.setStroke(new BasicStroke(STROKE_SIZE,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER, 2.0f));
        
    	int tempx = x2 - x1;
    	int tempy = y2 - y1;
    	
        double length = Math.sqrt((double)(tempx*tempx+tempy*tempy));
        
    	int ecos=(int)Math.round(HALF_WIDTH_RATIO*u*tempx/length);
    	int esin=(int)Math.round(HALF_WIDTH_RATIO*u*tempy/length);
    	
    	if ((HALF_WIDTH_RATIO*u)<length){
    	    tempx=ecos;
    	    tempy=esin;
    	}
        int [] xpoints={x2-(esin+tempx),x2,x2+(esin-tempx)};
        int [] ypoints={y2+(ecos-tempy),y2,y2-(ecos+tempy)};
    	Polygon p=new Polygon(xpoints,ypoints,3);
    	
    	if (!cutArrow)
    	    g2.drawLine(x1,y1,x2,y2);
    	else {
            g2.fill(text);
            //g2.draw(textRectangle);                
            g2.draw(line1);
            g2.draw(line2);
                
        }    	
    	g2.fill(p);

    	//System.out.println("drawLink ["+name+"] : ("+x1+","+y1+") --- ("+x2+","+y2+")");
    }
    
    private int abs(int i){
    	return (i>=0)?i:(-i);
    }
    protected void drawComponent(Graphics2D g2/*,int w,int h*/){
    	//updateLink();
        drawLink(g2);
    }
    
    public boolean intersection(GenericComponent gc){
    	return false;
    }
    
    public static ImageIcon getReductedIcon(int w,int h,double f,boolean b){
    	BufferedImage image=new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);
    	Graphics2D g2=image.createGraphics();
    	//AlphaComposite ac=AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f);
    	//g2.setComposite(ac);
    	drawIcon(g2,(int)Math.round(w*(1-f)),(int)Math.round(h*(1-f)),b);
    	return new ImageIcon(image);  	
    }
    
    public static ImageIcon getIcon(int w,int h,boolean b){
    	BufferedImage image=new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);
    	Graphics2D g2=image.createGraphics();
    	//AlphaComposite ac=AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f);
    	//g2.setComposite(ac);
    	drawIcon(g2,w,h,b);
    	return new ImageIcon(image);  	
    }
    protected static void drawIcon(Graphics2D g2,int w,int h,boolean b){
    	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        Color BACKGROUND_COLOR=COLOR;
    	//arrow
    	double ww=w;
    	double hh=h;
    	double xcenter=ww/3;
    	double ycenter=hh/3;
    	double TAN_DEMI_ANGLE=Math.tan(Math.PI/7);
    	double DTHICKNESS_RATIO=0.20;
    	double DTHICKNESS=TAN_DEMI_ANGLE*ycenter*DTHICKNESS_RATIO;
    	double slope=-Math.PI/4;
    	double xs[]={xcenter-DTHICKNESS,xcenter-DTHICKNESS,xcenter-TAN_DEMI_ANGLE*ycenter,xcenter,xcenter+TAN_DEMI_ANGLE*ycenter,xcenter+DTHICKNESS,xcenter+DTHICKNESS};
    	double ys[]={hh,ycenter,ycenter,0,ycenter,ycenter,hh};
    	g2.setColor((b)?BACKGROUND_COLOR:(BACKGROUND_COLOR).darker());
    	Area area=new Area(new Polygon(round(xs),round(ys),7));
    	AffineTransform t=new AffineTransform();
    	t.rotate(slope,xcenter,ycenter);
    	area.transform(t);
    	g2.fill(area);
    }
    
    public static Image getComponentCursor(int wi,int hi) {
    	//BufferedImage image = new BufferedImage(wi,hi,BufferedImage.TYPE_INT_ARGB);
        // add 1 pixel to both width & height after upgrading from JDK v.1.22 to v.1.31
        BufferedImage image = new BufferedImage(wi+1,hi+1,BufferedImage.TYPE_INT_ARGB);

        Graphics2D g2 = image.createGraphics();
    	
        AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f);
    	g2.setComposite(ac);
    	
        drawCursor(g2,wi,hi);
        
    	return image;  	
    }
    
    protected static void drawCursor(Graphics2D g2,int cursorWidth,int cursorHeight) {
    	g2.setColor(Color.black);
        
        double ww = cursorWidth;
        double hh = cursorHeight;
        
    	double RATIO_FOR_ARROW_LINE = 0.3;
        
        // (xc,yc) = top left-hand corner of the curvilinear shape
        double xcenter = ww * RATIO_FOR_ARROW_LINE;
        double ycenter = hh * RATIO_FOR_ARROW_LINE;
        int xc = (int)Math.round(xcenter);
        int yc = (int)Math.round(ycenter);

        // slope of cursor
        double slope = Math.atan(hh/ww);
        
        double RATIO_FOR_ARROW_HEAD = 0.4;
        double DANGLE = Math.PI/6;     // 30 degree
        
        double xarrowHead = xcenter * RATIO_FOR_ARROW_HEAD;
        double yarrowHead = ycenter * RATIO_FOR_ARROW_HEAD;
        
        double xoffset = yarrowHead*Math.tan(DANGLE);
        double yoffset = xarrowHead*Math.tan(DANGLE);

        double xs[] = {0,xarrowHead-xoffset,xarrowHead+xoffset};
        double ys[] = {0,yarrowHead+yoffset,yarrowHead-yoffset};
        
        // draw the arrow line
        //g2.drawLine(0,0,xc,yc);
        // adjusted after upgrading from JDK v.1.22 to v.1.31
        g2.drawLine(0,0,xc-2,yc-2);

        // draw the arrow head
        Polygon p = new Polygon(round(xs),round(ys),3);
    	g2.fill(p);
    	
    	double deltax = (ww)/4;
    	double deltay = (hh)/4;
    	
        // draw the starting line
        //Line2D.Double ln = new Line2D.Double(0,0,deltax,deltay);
        // adjusted after upgrading from JDK v.1.22 to v.1.31
        Line2D.Double ln = new Line2D.Double(0,0,deltax-2,deltay-2);
        g2.draw(ln);
        
        // draw the left-half curve
    	//QuadCurve2D.Double c = new QuadCurve2D.Double(deltax,deltay,deltax,2*deltay,2*deltax,2*deltay);
        // adjusted after upgrading from JDK v.1.22 to v.1.31
        QuadCurve2D.Double c = new QuadCurve2D.Double(deltax+1,deltay+1,deltax-2,2*deltay+3,2*deltax+1,2*deltay+1);
    	g2.draw(c);
        
        // draw the right-half curve
    	//c = new QuadCurve2D.Double(2*deltax,2*deltay,3*deltax,2*deltay,3*deltax,3*deltay);
        // adjusted after upgrading from JDK v.1.22 to v.1.31
        c = new QuadCurve2D.Double(2*deltax+1,2*deltay+1,3*deltax+2,2*deltay-1,3*deltax+1,3*deltay+1);
    	g2.draw(c);
        
        // draw the finishing line
    	//ln = new Line2D.Double(3*deltax,3*deltay,ww,hh);
        // adjusted after upgrading from JDK v.1.22 to v.1.31
        ln = new Line2D.Double(3*deltax+1,3*deltay+1,ww,hh);
    	g2.draw(ln);
    	
    	/*Area area = new Area(p);
        area.transform(new AffineTransform(-1,0,0,1,ww,ycenter));
    	g2.fill(area);
    	double deltax=(ww-xcenter)/4;
    	double deltay=(hh-ycenter)/4;
    	CubicCurve2D.Double c = new CubicCurve2D.Double(ww,ycenter,ww-deltax,ycenter+deltay,ww-deltax,ycenter+2*deltay,ww-2*deltax,ycenter+2*deltay);
    	g2.draw(c);
    	c=new CubicCurve2D.Double(ww-2*deltax,ycenter+2*deltay,xcenter+deltax,ycenter+2*deltay,xcenter+deltax,hh-deltay,xcenter,hh);
    	g2.draw(c);*/
    }
    
    private static int[] round(double [] t){
    	int [] result=new int[t.length];
    	for (int i=0;i<t.length;i++){
    	    result[i]=(int)Math.round(t[i]);
    	}
    	return result;
    }

    private static int r(double d){
    	return (int)Math.round(d);
    }

    public LinkableComponent getFirstLC(){

        return lc1;

    }
    public LinkableComponent getSecondLC(){

        return lc2;

    }
  
	 //shijian 21-3-2001 condition
	 //set ID for precondition
	 public void setPreconDM(String str){
	      _preconDM =str;
	      if (lc1 instanceof Task)
	      	((Task)lc1).setDirty(true);
	      if (lc2 instanceof Task)
	      	((Task)lc2).setDirty(true);
	 }
	 //shijian 13-2-2001
	 //fet ID for precondition
	 public String getPreconDM(){
	      return _preconDM;
	 } 
	 //shijian 21-3-2001 
	 //set  precondition
	 public void setPrecondition(String str){
	      condition =str;	 	
	      setPreconDM("");
	 }
	 //shijian 13-2-2001
	 //get  precondition
	 public String getPrecondition(){
	      return condition;
	 } 
 
}