package shapeUtil;

import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.event.*;
import util.*;
import DrawTop.*;
import shape.*;
import geomExtension.*;



public class AutoAlign {
    MousePositionInfo mousePositionInfo=null;
    ShapeContainer[] selectedContainers=null;
    int auto_tracking_option;
    MoveResizeShapeLS moveResizeShapeLS=null;
    
    int mode=0;
    ArrayList xAlignList=new ArrayList();
    ArrayList yAlignList=new ArrayList();
    Vector hitAlignedCouples=new Vector();
    Vector selectedHitAlignedCouples=new Vector();
    public static double ErrorMargin=3d;
    Color color=Color.RED;
    //Color color=new Color(0xC0C0C0); //silver
    int debug=0;

    public AutoAlign(){}
    public AutoAlign(MoveResizeShapeLS moveResizeShapeLS){
        this.moveResizeShapeLS=moveResizeShapeLS;
    }

    public AutoAlign(int mode, MousePositionInfo mousePositionInfo,
            ShapeContainer[] selectedContainers){
        this.mode=mode;
        this.mousePositionInfo=mousePositionInfo;
        this.selectedContainers=selectedContainers;
        this.createAlign();
    }

    public void start(int mode, MousePositionInfo mousePositionInfo,
            ShapeContainer[] selectedContainers){
        if(!DrawParameters.AUTO_ALIGN) return;
        this.auto_tracking_option=DrawParameters.AUTO_TRACKING_OPTION;
        this.xAlignList.clear();
        this.yAlignList.clear();
        this.hitAlignedCouples.clear();
        this.selectedHitAlignedCouples.clear();
        this.mode=mode;
        if(mode==Command.MOVE) this.mode=Command.MOVING_MODE;
        if(mode==Command.RESIZE) this.mode=Command.RESIZING_MODE;
        if(mode==Command.MOVE_ENDPT) this.mode=Command.MOVING_ENDPT_MODE;
        this.mousePositionInfo=mousePositionInfo;
        this.selectedContainers=selectedContainers;
        this.createAlign();
        this.drawHitAligns();
        ObjectTable.getDrawPanel().repaint();
    }
  
    public void mouseDragged(MouseEvent e) {
        if(!DrawParameters.AUTO_ALIGN) return;
        this.drawHitAligns();
    }

    public void mouseReleased(MouseEvent e) {
        if(!DrawParameters.AUTO_ALIGN) return;
        int ctrl=0;
        int key=e.getModifiersEx();
        if((key&InputEvent.SHIFT_DOWN_MASK)!=0) ctrl=1;
        if((key&InputEvent.CTRL_DOWN_MASK)!=0) ctrl=2;
        if((key&InputEvent.ALT_DOWN_MASK)!=0) ctrl=3;
        if(this.mode==Command.MOVING_ENDPT_MODE){
            boolean connected=this.moveResizeShapeLS.connectionUtil.connected;
            if(!connected) this.ajustAlignment(ctrl);
			if(debug>=0&&connected) System.out.println("**** AutoAlign mouseReleased connected=true");
           
        } else {
            boolean guided=this.moveResizeShapeLS.connectionUtil.guided;
            if(!guided) this.ajustAlignment(ctrl);
			if(debug>=0&&guided) System.out.println("** AutoAlign mouseReleased guided=true");
        }
		if(debug>=0) System.out.println("** AutoAlign.mouseReleased called");
    } //mouseReleased

    private void createAlign(){
        ContainerManager containerManager=ObjectTable.getContainerManager();
        ShapeContainer[] containers=containerManager.getContainers();
        for(int i=0;i<containers.length;i++){
            boolean same=false;
            for(int j=0;j<this.selectedContainers.length;j++){
                if(containers[i]==this.selectedContainers[j]||
                   containers[i].equals(this.selectedContainers[j])) same=true;
            }
            if(same) continue;
            Rectangle2D box=containers[i].getBoundingBox();
            double x=box.getX();
            double w=box.getWidth();
            double y=box.getY();
            double h=box.getHeight();
            this.putAlign(Align.XALIGN, Align.LEFT, x, containers[i]);
            this.putAlign(Align.XALIGN, Align.CENTER, x+0.5*w, containers[i]);
            this.putAlign(Align.XALIGN, Align.RIGHT, x+w, containers[i]);
            
            this.putAlign(Align.YALIGN, Align.TOP, y, containers[i]);
            this.putAlign(Align.YALIGN, Align.MIDDLE, y+0.5*h, containers[i]);
            this.putAlign(Align.YALIGN, Align.BOTTOM, y+h, containers[i]);
        }

        if(debug>0){
            System.out.println("Print xAlignList");
            for(int i=0;i<this.xAlignList.size();i++){
                Align align=(Align)xAlignList.get(i);
                System.out.println("-- "+align.toString());
            }
            System.out.println("Print yAlignList");
            for(int i=0;i<this.yAlignList.size();i++){
                Align align=(Align)yAlignList.get(i);
                System.out.println("-- "+align.toString());
            }
        }
    }

    private void putAlign(int alignAxis, int alignType, double value, 
            ShapeContainer shapeContainer){
		//sorting by value
        double startValue=-10e+4;
        int index=-1;
        ArrayList alignList=null;
        if(alignAxis==Align.XALIGN) alignList=this.xAlignList;
        if(alignAxis==Align.YALIGN) alignList=this.yAlignList;
        for(int i=0;i<alignList.size();i++){
            Align alignSaved=(Align)alignList.get(i);
            double endValue=alignSaved.getValue();
            if(value>startValue&&value<=endValue){
                index=i;
                break;
            }
            startValue=endValue;
        }
		
        Align align=new Align(alignAxis, alignType, value, shapeContainer);
        if(index<0) {
			alignList.add(align);
		}
        else {
			alignList.add(index, align);
			//this.xyAlignList.add(index, align);
		}
    }

	public void drawHitAligns() {
		this.hitAlignedCouples.clear();
		ShapeContainer targetContainer = this.mousePositionInfo.getContainer();
		if (this.mode == Command.MOVING_ENDPT_MODE) {
			if(this.selectedContainers.length>1){
				System.err.println("*** ERROR in AutoAlign.drawHitAligns: The num of this.selectedContainers must be one");
			}
			targetContainer = this.selectedContainers[0];
		}
	  //--------------------------------------------//  
		this.createHitAlignedCouples(targetContainer);
	  //--------------------------------------------//  
		DrawShapeUtil.clearTempShape("Auto_align");
		ObjectTable.getDrawPanel().repaint();

		int numSel = 6;
		AlignedCouple[] alignedCouplesX = new AlignedCouple[numSel];
		alignedCouplesX[0] = this.selectHitAlignedCouple(Align.LEFT, 0);
		alignedCouplesX[1] = this.selectHitAlignedCouple(Align.LEFT, 1);
		alignedCouplesX[2] = this.selectHitAlignedCouple(Align.CENTER, 0);
		alignedCouplesX[3] = this.selectHitAlignedCouple(Align.CENTER, 1);
		alignedCouplesX[4] = this.selectHitAlignedCouple(Align.RIGHT, 0);
		alignedCouplesX[5] = this.selectHitAlignedCouple(Align.RIGHT, 1);
		
		AlignedCouple[] alignedCouplesY = new AlignedCouple[numSel];
		alignedCouplesY[0] = this.selectHitAlignedCouple(Align.TOP, 0);
		alignedCouplesY[1] = this.selectHitAlignedCouple(Align.TOP, 1);
		alignedCouplesY[2] = this.selectHitAlignedCouple(Align.MIDDLE, 0);
		alignedCouplesY[3] = this.selectHitAlignedCouple(Align.MIDDLE, 1);
		alignedCouplesY[4] = this.selectHitAlignedCouple(Align.BOTTOM, 0);
		alignedCouplesY[5] = this.selectHitAlignedCouple(Align.BOTTOM, 1);

		this.selectedHitAlignedCouples.clear();
		for(int i=0;i<6;i++){
			if (alignedCouplesX[i] == null) continue;
			if (Math.abs(alignedCouplesX[i].getAlignedError()) < this.ErrorMargin){
				this.selectedHitAlignedCouples.add(alignedCouplesX[i]);
			}
		}
		for(int i=0;i<6;i++){
			if (alignedCouplesY[i] == null) continue;
			if (Math.abs(alignedCouplesY[i].getAlignedError()) < this.ErrorMargin){
				this.selectedHitAlignedCouples.add(alignedCouplesY[i]);
			}
		}
		int size=this.selectedHitAlignedCouples.size();
		for (int i = 0; i < size; i++) {
			AlignedCouple alignedCouple=(AlignedCouple)this.selectedHitAlignedCouples.get(i);
				//----------------------------//   
				this.drawAlign(alignedCouple);
				//----------------------------//   
				// **** checkInterSection **** //
				if (debug > 0) {
					System.out.println("alignedCouples i=" + i);
				}
		}

		if (debug > 0) {
			System.out.println("\n** drawHitAligns selectedHitAlignedCouples size=" + size);
			for (int i = 0; i < size; i++) {
				AlignedCouple couple = (AlignedCouple) this.selectedHitAlignedCouples.get(i);
				String str="- AlignedCouple:\n" + couple.toString(1);
				System.out.println(str);
			}
		}
	}

    private void createHitAlignedCouples(ShapeContainer targetContainer){
		if (this.debug >0) {
			System.out.println("* getHitAlignedCouples" + " mode=" + Command.getModeString(this.mode));
		}
		ArrayList alignList = null;
        Rectangle2D targetBox=targetContainer.getBoundingBox();
		int endPTindex=-1;
		if (this.mode == Command.MOVING_ENDPT_MODE) {
			endPTindex=this.selectMovingEndPT();
			if (endPTindex>= 0) {
				Point2D[] PTs = targetContainer.getElement().getEndPTs();
				targetBox = new Rectangle2D.Double(PTs[endPTindex].getX(), PTs[endPTindex].getY(), 0, 0);
			}
		}
		double X = targetBox.getX();
		double Y = targetBox.getY();
		double W = targetBox.getWidth();
		double H = targetBox.getHeight();
		
		Align hitAlign = null;
		int hitAlignType = -1;
		int hitAlignAxis = -1;
		int targetAlignType = -1;
		double value = 0;
		if (debug >0) {
			printAlignList();
		}
		//check x-align
		alignList = this.xAlignList;
		for (int j = 0; j < alignList.size(); j++) {
			hitAlign = (Align) alignList.get(j);
			hitAlignAxis = hitAlign.getAlignAxis();
			hitAlignType = hitAlign.getAlignType();
			value = hitAlign.getValue();
			targetAlignType = 0;
			double[] x = new double[1];
			if (this.mode != Command.MOVING_ENDPT_MODE) x = new double[3];
			for (int i = 0; i < x.length; i++) {
				x[i] = X + 0.5 * W * i;
			}
			for (int i =0; i< x.length; i++) {
				if (Math.abs(x[i] - value) < this.ErrorMargin) {
					if (this.mode == Command.MOVING_MODE || this.mode == Command.RESIZING_MODE) {
						targetAlignType = i;
					} else if (this.mode == Command.MOVING_ENDPT_MODE) {
						targetAlignType = Align.ENDPT;
					}
					int targetAlignAxis = Align.XALIGN;
					Align targetAlign = new Align(targetAlignAxis, targetAlignType, x[i],
							targetContainer, endPTindex);
					AlignedCouple couple = new AlignedCouple(targetAlign, hitAlign); //bug
					this.hitAlignedCouples.add(couple);
				}
			}
		}
		//check y-align
		alignList=this.yAlignList;
		for (int j = 0; j < alignList.size(); j++) {
			hitAlign = (Align) alignList.get(j);
			hitAlignAxis = hitAlign.getAlignAxis();
			hitAlignType = hitAlign.getAlignType();
			value = hitAlign.getValue();
			targetAlignType = 0;
			double[] y = new double[1];
			if (this.mode != Command.MOVING_ENDPT_MODE) y = new double[3];
			for (int i = 0; i < y.length; i++) {
				y[i] = Y + 0.5 * H * i;
			}
			for (int i = 0; i < y.length; i++) {
				if (Math.abs(y[i] - value) < this.ErrorMargin) {
					if (this.mode == Command.MOVING_MODE || this.mode == Command.RESIZING_MODE) {
						targetAlignType = i+3;
					} else if (this.mode == Command.MOVING_ENDPT_MODE) {
						targetAlignType = Align.ENDPT;
					}
					int targetAlignAxis = Align.YALIGN;
					Align targetAlign = new Align(targetAlignAxis, targetAlignType, y[i],
							targetContainer, endPTindex);
					AlignedCouple couple = new AlignedCouple(targetAlign, hitAlign);
					this.hitAlignedCouples.add(couple);
				}
			}
		}
		
        if(debug>0){
            int size=this.hitAlignedCouples.size();
            if(size>0){
                System.out.println("* getHitAlignedCouples HitAlign, endPTindex="
						+endPTindex+", mode=" + Command.getModeString(this.mode));
                for(int j=0;j<size;j++){
                    AlignedCouple couple=(AlignedCouple)this.hitAlignedCouples.get(j);
                    System.out.println(couple);
                }
            }
        } //debug
    }
	private void printAlignList() {
		ArrayList alignList = null;
		Align hitAlign = null;
		alignList = this.xAlignList;
		System.out.println("** printAlignList");
		System.out.println("xAlignList");
		for (int j = 0; j < alignList.size(); j++) {
			hitAlign = (Align) alignList.get(j);
			System.out.println("- align[" + j + "]: " + hitAlign.toString());
		}
		alignList = this.yAlignList;
		System.out.println("yAlignList");
		for (int j = 0; j < alignList.size(); j++) {
			hitAlign = (Align) alignList.get(j);
			System.out.println("- align[" + j + "]: " + hitAlign.toString());
		}
	}
	
	private int selectMovingEndPT() {
		Point2D mousePoint = this.mousePositionInfo.getPoint();
		int movingEndPTindex = -1;
		int containerType = this.selectedContainers[0].getContainerType();
		int type = this.selectedContainers[0].getElement().getTypeE();
		if (containerType == ShapeContainer.GROUP) {
			return movingEndPTindex;
		}
		if (type < Command.LINE || type > Command.GENERAL_CURVE) {
			return movingEndPTindex;
		}
		if (this.selectedContainers[0].getElement().isClosed()) {
			return movingEndPTindex;
		}
		Point2D[] endPTs = this.selectedContainers[0].getElement().getEndPTs();
		if (endPTs != null && endPTs.length > 0) {
			double d0 = Vector2D.dist(mousePoint, endPTs[0]);
			double d1 = Vector2D.dist(mousePoint, endPTs[1]);
			movingEndPTindex = 0;
			if (d1 < d0) {
				movingEndPTindex = 1;
			}
		}
		if (debug > 0) {
			System.out.println("- selectMovingEndPT movingEndPTindex=" + movingEndPTindex);
		}
		return movingEndPTindex;
	}

    private AlignedCouple selectHitAlignedCouple(int alignType, int dir){
        int size=this.hitAlignedCouples.size();
        double distMin=1.0e+5;
        int index=-1;
        for(int i=0;i<size;i++){
            AlignedCouple alignedCouple=(AlignedCouple)this.hitAlignedCouples.get(i);
            int hitAlignType=alignedCouple.hitAlign.getAlignType();
            if(hitAlignType!=alignType) continue;
            int hitSide=0;
            if(!alignedCouple.isHitAlignUpperLeft()) hitSide=1;
            if(hitSide!=dir) continue;
            double dist=0.0;
            if(alignType==Align.LEFT||alignType==Align.CENTER||
                    alignType==Align.RIGHT){
                dist=alignedCouple.getAlignOrthogonalDistance();
            } else if(alignType==Align.TOP||alignType==Align.MIDDLE||
                    alignType==Align.BOTTOM){
                dist=alignedCouple.getAlignOrthogonalDistance();
            }
            if(dist<distMin){
                distMin=dist;
                index=i;
            }
        }
        if(index<0) return null;
        else {
			AlignedCouple retCouple=(AlignedCouple)this.hitAlignedCouples.get(index);
			if(debug>0) {
				System.out.println("- selectHitAlignedCouple "
						+ "alignType=" + Align.alignString[alignType] + ", dir=" + dir
						+ "\n" + retCouple.toString());
			}
			return retCouple;
		}
    }// end of selectHitAlignedCouple

	private void drawAlign(AlignedCouple couple) {
		double scale = DrawParameters.getScale();
		Align hitAlign = couple.hitAlign;
		int hitAlignType = hitAlign.getAlignType();
		Point2D targetP = couple.getTargetAlignPoint();
		Point2D hitP = couple.getHitAlignPoint();
		Line2D line = null;
		String message = "";
		if (hitAlignType == Align.LEFT) {
			line = new Line2D.Double(hitP.getX(), hitP.getY(), hitP.getX(), targetP.getY());
			message = "left align";
		}
		if (hitAlignType == Align.CENTER) {
			line = new Line2D.Double(hitP.getX(), targetP.getY(), hitP.getX(), hitP.getY());
			message = "center align";
		}
		if (hitAlignType == Align.RIGHT) {
			line = new Line2D.Double(hitP.getX(), hitP.getY(), hitP.getX(), targetP.getY());
			message = "right align";
		}
		if (hitAlignType == Align.TOP) {
			line = new Line2D.Double(hitP.getX(), hitP.getY(), targetP.getX(), hitP.getY());
			message = "top align";
		}
		if (hitAlignType == Align.MIDDLE) {
			line = new Line2D.Double(hitP.getX(), hitP.getY(), targetP.getX(), hitP.getY());
			message = "middle align";
		}
		if (hitAlignType == Align.BOTTOM) {
			line = new Line2D.Double(hitP.getX(), hitP.getY(), targetP.getX(), hitP.getY());
			message = "bottom align";
		}
		float[] dash = {2.0f, 3.0f};
		
		BasicStroke stroke = new BasicStroke(1.0f / (float) scale, BasicStroke.CAP_SQUARE,
				BasicStroke.JOIN_MITER, 1.0f, dash, 0.0f);
		DrawShapeUtil.drawTempShape("Auto_align", line, stroke,
				this.color, null, null, null, this.color);
		
		double[] intersect=couple.checkIntersection();
		double val=couple.hitAlign.getValue();
		int alignAxis=couple.hitAlign.getAlignAxis();
		if(intersect!=null){
			if(alignAxis==Align.XALIGN){
				line=new Line2D.Double(val, intersect[0], val, intersect[1]);
			}else{
				line=new Line2D.Double(intersect[0], val, intersect[1], val);
			}
			float[] dash1 = {2.0f, 6.0f};
			stroke = new BasicStroke(3.0f / (float) scale, BasicStroke.CAP_SQUARE,
					BasicStroke.JOIN_MITER, 3.0f, dash1, 0.0f);
			//stroke=new BasicStroke(2.0f / (float) scale);
			//Color crimson=new Color(220,17, 60);
			DrawShapeUtil.drawTempShape("Auto_align", line, stroke,
				Color.red, null, null, null, Color.red);
		}
		if (debug > 0) {
			System.out.println("  -- drawAlign\n    " + couple.toString());
		}
	} // end of drawAlign

    public void ajustAlignment(int ctrl){
        int mousePositionCode=this.mousePositionInfo.getPosition();
        Point2D currentP=ObjectTable.getMousePositionLS().getMousePoint();
		if(this.debug>0) {
			String str = "";
			str += "** ajustment" + " mode=" + Command.getModeString(this.mode)
					+ ", ctrl=" + ctrl + ", mousePositionCode=" + MousePositionInfo.getPositionString(mousePositionCode);
			System.out.println(str);
		}
        AlignedCouple bestCoupleX=null;
		AlignedCouple bestCoupleY=null;
          //move    
        if(this.mode==Command.MOVING_MODE){
            double errorX=0;
            double errorY=0;
            bestCoupleX=this.getBestAlignedCouple(Align.XALIGN);
            if(bestCoupleX!=null) errorX=bestCoupleX.getAlignedError();
            bestCoupleY=this.getBestAlignedCouple(Align.YALIGN);
            if(bestCoupleY!=null) errorY=bestCoupleY.getAlignedError();
            Point2D newP=new Point2D.Double(currentP.getX()+errorX, currentP.getY()+errorY);
			if(debug>0) printAjustment("\n** print Before ajustment", bestCoupleX, bestCoupleY);
            for(int i=0;i<this.selectedContainers.length;i++){
                this.selectedContainers[i].getElement().move(ctrl, newP, true);
            }
        } //end of move
        //resize    
        if(this.mode==Command.RESIZING_MODE ){
            double errorX=0;
            double errorY=0;
            String mousePosition=MousePositionInfo.getPositionString(mousePositionCode);
            switch(mousePositionCode){
                case MousePositionInfo.NW_RESIZE:{
					bestCoupleX = this.getBestAlignedCouple(Align.XALIGN, Align.LEFT);
					if (bestCoupleX != null) {
						errorX = bestCoupleX.getAlignedError();
					}
					bestCoupleY = this.getBestAlignedCouple(Align.YALIGN, Align.TOP);
					if (bestCoupleY != null) {
						errorY = bestCoupleY.getAlignedError();
					}
                    break;
                }
                case MousePositionInfo.NE_RESIZE:{
					bestCoupleX = this.getBestAlignedCouple(Align.XALIGN, Align.RIGHT);
					if (bestCoupleX != null) {
						errorX = bestCoupleX.getAlignedError();
					}
					bestCoupleY = this.getBestAlignedCouple(Align.YALIGN, Align.TOP);
					if (bestCoupleY != null) {
						errorY = bestCoupleY.getAlignedError();
					}
                    break;
                }
                case MousePositionInfo.SE_RESIZE:{
					bestCoupleX = this.getBestAlignedCouple(Align.XALIGN, Align.RIGHT);
					if (bestCoupleX != null) {
						errorX = bestCoupleX.getAlignedError();
					}
					bestCoupleY = this.getBestAlignedCouple(Align.YALIGN, Align.BOTTOM);
					if (bestCoupleY != null) {
						errorY = bestCoupleY.getAlignedError();
					}
                    break;
                }
                case MousePositionInfo.SW_RESIZE:{
					bestCoupleX = this.getBestAlignedCouple(Align.XALIGN, Align.LEFT);
					if (bestCoupleX != null) {
						errorX = bestCoupleX.getAlignedError();
					}
					bestCoupleY = this.getBestAlignedCouple(Align.YALIGN, Align.BOTTOM);
					if (bestCoupleY != null) {
						errorY = bestCoupleY.getAlignedError();
					}
                    break;
                }
                case MousePositionInfo.N_RESIZE:{
					bestCoupleY = this.getBestAlignedCouple(Align.YALIGN, Align.TOP);
					if (bestCoupleY != null) {
						errorY = bestCoupleY.getAlignedError();
					}
                    break;
                }
                case MousePositionInfo.E_RESIZE:{
                    bestCoupleX = this.getBestAlignedCouple(Align.XALIGN, Align.RIGHT);
					if (bestCoupleX != null) {
						errorX = bestCoupleX.getAlignedError();
					}
                    break;
                }
                case MousePositionInfo.S_RESIZE:{
					bestCoupleY = this.getBestAlignedCouple(Align.YALIGN, Align.BOTTOM);
					if (bestCoupleY != null) {
						errorY = bestCoupleY.getAlignedError();
					}
                    break;
                }
                case MousePositionInfo.W_RESIZE:{
                    bestCoupleX = this.getBestAlignedCouple(Align.XALIGN, Align.LEFT);
					if (bestCoupleX != null) {
						errorX = bestCoupleX.getAlignedError();
					}
                    break;
                }
            }
			
            Point2D P0=new Point2D.Double(0,0);
            Point2D P1=new Point2D.Double(errorX, errorY);
            for(int i=0;i<this.selectedContainers.length;i++){
                this.selectedContainers[i].getElement().mouseStart(ctrl, P0);
            }
            for(int i=0;i<this.selectedContainers.length;i++){
                this.selectedContainers[i].getElement().resize(ctrl, P1, mousePosition, true);
            }

        } //end of resize
          //move endPT    
        if (this.mode == Command.MOVING_ENDPT_MODE) {
			double errorX = 0;
			double errorY = 0;
			bestCoupleX = this.getBestAlignedCouple(Align.XALIGN, Align.ENDPT);
			if (bestCoupleX != null) {
				errorX = bestCoupleX.getAlignedError();
			}
			bestCoupleY = this.getBestAlignedCouple( Align.YALIGN, Align.ENDPT);
			if (bestCoupleY != null) {
				errorY = bestCoupleY.getAlignedError();
			}
			int ptIndex = selectMovingEndPT();
			if (ptIndex < 0) {
				return;
			}
			boolean lineDir = false;
			String key = Command.getCommandString(Command.KEEP_LINE_DIRECTION);
			Object obj=this.selectedContainers[0].getProperty(key);
			if(obj!=null) lineDir = (Boolean) this.selectedContainers[0].getProperty(key);
			Point2D[] endPTs = this.selectedContainers[0].getElement().getEndPTs();
			Point2D oldPoint = endPTs[ptIndex];
			Point2D newPoint = new Point2D.Double(oldPoint.getX() + errorX,
					oldPoint.getY() + errorY);
			Point2D projPT = null;
			if (lineDir) {
				projPT = this.projectPT(this.selectedContainers[0], ptIndex, 2, newPoint);
				if (errorX != 0.0 && projPT != null) {
					newPoint = projPT;
				}
				projPT = this.projectPT(this.selectedContainers[0], ptIndex, 1, newPoint);
				if (errorY != 0.0 && projPT != null) {
					newPoint = projPT;
				}
			}
			this.selectedContainers[0].getElement().moveEndPoint(ctrl, ptIndex, newPoint);
		} //end of move endPT 
		if(this.debug>0){
			printAjustment("** print ajustment results", bestCoupleX, bestCoupleY);
		}
    }
	
	private void printAjustment(String message, AlignedCouple bestCoupleX, AlignedCouple bestCoupleY){
		String str = "";
		str += message;
		if (bestCoupleX != null) {
			bestCoupleX.update();
			str += "\n - x-align  errorX="+bestCoupleX.getAlignedError()+ "\n"+bestCoupleX.toString(2);
		} else {
			str += "\n - x-align: none";
		}
		if (bestCoupleY != null) {
			bestCoupleY.update();
			str += "\n - y-align  errorY="+bestCoupleY.getAlignedError()+ "\n"+bestCoupleY.toString(2);
		} else {
			str += "\n - y-align: none";
		}
		System.out.println(str);
	}
/*
    private AlignedCouple getAlignedCouple(int alignType){
        AlignedCouple alignedCouple=null;
        int size=this.selectedHitAlignedCouples.size();
        for(int i=0;i<size;i++){
            AlignedCouple couple=(AlignedCouple)this.selectedHitAlignedCouples.get(i);
            int type=couple.hitAlign.getAlignType();
            if(type==alignType) {
                alignedCouple=couple;
                break;
            }
        }
        return alignedCouple;
    }
*/
    private AlignedCouple getBestAlignedCouple(int alignAxis){
        AlignedCouple bestCouple=null;
		double intersectLenMax=0.0;
        double errorMin=1.0e+5;
        int index=-1;
        double alignedError=1.0e+5;
		double[] intersect=new double[2];
		double intersectLen=0.0;
		int size = this.selectedHitAlignedCouples.size();
		for (int i = 0; i < size; i++) {
			AlignedCouple couple = (AlignedCouple) this.selectedHitAlignedCouples.get(i);
			int targetAxis = couple.targetAlign.getAlignAxis();
			int hitAxis = couple.hitAlign.getAlignAxis();
			int type = couple.hitAlign.getAlignType();
			if (targetAxis != alignAxis||hitAxis != alignAxis) {
				continue;
			}

			intersect = couple.checkIntersection();
			if (intersect != null) {
				intersectLen = Math.abs(intersect[0] - intersect[1]);
			}
			alignedError = Math.abs(couple.getAlignedError());

			if (intersectLen > intersectLenMax) {
				intersectLenMax = intersectLen;
				index = i;
				bestCouple = couple;
			} else if (alignedError < errorMin) {
				errorMin = alignedError;
				index = i;
				bestCouple = couple;
			}
		}
		return bestCouple;
    }

	private AlignedCouple getBestAlignedCouple(int alignAxis, int targetAlignType) {
		AlignedCouple bestCouple = null;
		double intersectLenMax = 0.0;
		double errorMin = 1.0e+5;
		int index = -1;
		double alignedError = 1.0e+5;
		double[] intersect = new double[2];
		double intersectLen = 0.0;
		int size = this.selectedHitAlignedCouples.size();
		//if (targetAlignCode >= Align.LEFT && targetAlignCode <= Align.ENDPT) {
		for (int i = 0; i < size; i++) {
			AlignedCouple couple = (AlignedCouple) this.selectedHitAlignedCouples.get(i);
			int targetAxis = couple.targetAlign.getAlignAxis();
			int hitAxis = couple.hitAlign.getAlignAxis();
			int targetType = couple.targetAlign.getAlignType();
			int hitType = couple.hitAlign.getAlignType();
			if (targetAxis != alignAxis || hitAxis != alignAxis
					|| targetType != targetAlignType) {
				continue;
			}
			intersect = couple.checkIntersection();
			if (intersect != null) {
				intersectLen = Math.abs(intersect[0] - intersect[1]);
			}
			alignedError = Math.abs(couple.getAlignedError());
			if (intersectLen > intersectLenMax) {
				intersectLenMax = intersectLen;
				index = i;
				bestCouple = couple;
			} else if (alignedError < errorMin) {
				errorMin = alignedError;
				index = i;
				bestCouple = couple;
			}
		}
		//}
		return bestCouple;
	}

    private Point2D projectPT(ShapeContainer container, int endPtIndex, int dir, Point2D pt){
        Curve2D curve=container.getElement().getCurve2D();
        int numseg=curve.getNumOfSegments();
        double t=0.0;
        if(endPtIndex>0) t=numseg;
        Point2D p0=curve.getP(t);
        Vector2D tvec=curve.getTangent(t);
        Point2D p1=new Point2D.Double(p0.getX()+tvec.getX(), p0.getY()+tvec.getY());
        Point2D pt1=new Point2D.Double(pt.getX()+1.0, pt.getY());
        if(dir==2){
            pt1=new Point2D.Double(pt.getX(), pt.getY()+1.0);
        }
        CrossCurvePT crossPT=Vector2D.crossPoint(p0, p1, pt, pt1);
        if(crossPT==null) return null;
        return crossPT.getP1();
    }
} 
