package shape;

import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import DrawTop.*;
import geomExtension.*;
import shapeUtil.*;
import util.*;
import util.UndoConstants;
     
public abstract class ShapeElement {
    public ShapeContainer shapeContainer;
    Curve2D curve2D=null;
    Curve2D curve2DSave=null;
    Rectangle2D textAreaSave=null;
    Point2D startPoint=null;
    Rectangle2D startBox=null;
    DiscreteAngledLine discreteAngledLine=null;
    public final static Dimension MinTextArea=new Dimension(10, 10);
    public final static double ClosedTolerance=3d;
    Vector workVector=new Vector();
    int debug=0;

    public ShapeElement(){}
    
    public abstract int getTypeE();
    public abstract String getShapeIdString();
    
    public ShapeContainer getShapeContainer(){
         if(this.shapeContainer==null) {
             System.err.println("*** Error ShapeElement.getShapeContainer: No shapeContainer");
         }
        return this.shapeContainer;
    }
    
    public void setShapeContainer(ShapeContainer container){
         this.shapeContainer=container;
    }

    public boolean isClosed(){
        if(this.curve2D!=null) return this.curve2D.isClosed();
        return false;
    }

    public Shape getShape(){
        if(this.curve2D!=null) return this.curve2D.getShape();
        return null;
    }

    public Curve2D getCurve2D(){
        return this.curve2D;
    }

    public void setCurve2D(Curve2D curve2D){
        this.curve2D=curve2D;
    }
    
    public GeneralCurveElement getGeneralCurveElement(){
        Curve2D curve=this.getCurve2D();
        Segment2D[] segments=curve.getSegment2Ds();
        Segment2D[] newSegments=new Segment2D[segments.length];
        for(int i=0;i<segments.length;i++){
            newSegments[i]=(Segment2D)segments[i].clone();
        }
        GeneralCurve2DE gcurve=new GeneralCurve2DE(newSegments);
        GeneralCurveElement gelement=new GeneralCurveElement();
        gelement.setCurve2D(gcurve);
        return gelement;
    }
    
    public SerializableCurve2D getSerializableCurve2D(){
        if(this.curve2D==null) return null;
        return this.curve2D.getSerializableCurve2D();
    }

    public void setSerializableCurve2D(SerializableCurve2D sdata){
        SerializableSegment2D[] segs=sdata.serializableSegments;
        int size=segs.length;
        Segment2D[] segments=new Segment2D[size];
        for(int i=0;i<size;i++){
            segments[i]=new Segment2D(segs[i].type, segs[i].shape, segs[i].affineTransform);
        }
        int curve2Dtype=sdata.type;
        if(curve2Dtype==1) curve2Dtype=Command.RECTANGLE;
        if(curve2Dtype==2) curve2Dtype=Command.ROUND_RECTANGLE;
        if(curve2Dtype==3) curve2Dtype=Command.ELLIPSE;
        if(curve2Dtype==4) curve2Dtype=Command.LINE;
        if(curve2Dtype==5) curve2Dtype=Command.POLYLINE;
        if(curve2Dtype==6) curve2Dtype=Command.CUBIC_CURVE;
        if(curve2Dtype==7) curve2Dtype=Command.GENERAL_CURVE;
        if(curve2Dtype==8) curve2Dtype=Command.COMBINED_CURVE;

         switch(curve2Dtype){
            case Command.RECTANGLE :{
                this.curve2D=new Rectangle2DE(segments); break;
            }
            case Command.ROUND_RECTANGLE :{
                this.curve2D=new RoundRectangle2DE(segments); break;
            }
            case Command.ELLIPSE :{
                this.curve2D=new Ellipse2DE(segments); break;
            }
            case Command.LINE:{
                this.curve2D=new Line2DE(segments); break;
            }
            case Command.POLYLINE:{
                this.curve2D=new Polyline2DE(segments); break;
            }
            case Command.CUBIC_CURVE :{
                this.curve2D=new CubicCurve2DE(segments); break;
            }
            case Command.GENERAL_CURVE :{
                this.curve2D=new GeneralCurve2DE(segments); break;
            }
        } //switch
    }

    public Rectangle2D getBoundingBox(){
        if(this.curve2D==null) return null;
        return this.curve2D.getBoundingBox();
    }

    //public abstract Point2D[] getEndPTs();
    public Point2D[] getEndPTs(){
        Point2D[] PTs=new Point2D[0];
        if(this.curve2D==null) return PTs;
        if(isClosed()) return PTs;
        PTs=new Point2D[2];
        int numseg=this.curve2D.getNumOfSegments();
        PTs[0]=this.curve2D.getP(0);
        PTs[1]=this.curve2D.getP(numseg);
        return PTs;
    }
    
    public abstract Rectangle2D createTextArea();

    public abstract SegmentModifier[] getSegmentModifiers();
    public abstract Point2D[] getModifierPTs();


    public void mouseStart(int ctrl, Point2D startPoint){
        this.curve2DSave=null;
        if(this.curve2D!=null){
            this.curve2DSave=(Curve2D)this.curve2D.clone();
        }
        this.textAreaSave=null;
        if(this.shapeContainer.isTextBox()){
            this.textAreaSave=(Rectangle2D)this.shapeContainer.getTextBox().getTextArea().clone();
        }
        this.startPoint=startPoint;
        this.discreteAngledLine=new DiscreteAngledLine(0.0, 90.0, this.startPoint);;
        this.startBox=this.getBoundingBox();
    }

    public void mouseEnd(){}
    public abstract void create(Point2D startPoint, Point2D currentPoint);
    public abstract void create(Point2D[] points);
    public abstract void create(Rectangle2D boundingBox);

    public void move(int ctrl, Point2D currentPoint, boolean moveTextBox){
        if(this.getShapeContainer()==null) {
            System.err.println("*** Error ShapeElement.move: No shapeContainer\n"
                     +this.getShapeIdString()+"  "+ this.toString());
            return;
        }
        Point2D newPoint=this.discreteAngledLine.getControlledPT(ctrl, currentPoint);
        Vector2D moveVec=Vector2D.sub(newPoint, this.startPoint);
        double moveX=moveVec.getX();
        double moveY=moveVec.getY();
        Rectangle2D newBox=new Rectangle2D.Double(this.startBox.getX()+moveX, this.startBox.getY()+moveY,
                this.startBox.getWidth(), this.startBox.getHeight());
        this.getShapeContainer().setChangeCode(UndoConstants.SIZE_POSITION);
        int size=this.curve2DSave.getNumOfSegments();
        Segment2D[] segments=new Segment2D[size];
        for(int i=0;i<size;i++){
            segments[i]=this.curve2DSave.getSegment2D(i).resizeSegment(this.startBox, newBox);
        }
        this.curve2D.setData(segments);
        if(this.shapeContainer.isTextBox()&&moveTextBox){
            Rectangle2D newTextArea=ShapeElementUtil.reseizeRectangle(this.textAreaSave, this.startBox, newBox);
            this.shapeContainer.getTextBox().setTextArea(newTextArea);
        }
    }

    public void resize(int ctrl, Point2D currentPoint, String mousePosition, boolean resizeTextBox){
        if(this.getShapeContainer()==null) {
            System.err.println("*** Error ShapeElement.move: No shapeContainer\n"
                     +this.getShapeIdString()+"  "+ this.toString());
            return;
        }
        boolean resizable=this.shapeContainer.getBooleanProperty("Enable_Resizing","ShapeElement");
        boolean keep_aspect_ratio=this.shapeContainer.getBooleanProperty("Keep_Aspect_Ratio","ShapeElement");
        if(!resizable) return;
        //int mousePositionCode=this.info.getPosition();
        if(keep_aspect_ratio) ctrl=1;
        Rectangle2D newBox=ShapeElementUtil.resizeRectangle(ctrl, this.startBox, this.startPoint,
            currentPoint, mousePosition);
        this.getShapeContainer().setChangeCode(UndoConstants.SIZE_POSITION);
        int size=this.curve2DSave.getNumOfSegments();
        Segment2D[] segments=new Segment2D[size];
        for(int i=0;i<size;i++){
            segments[i]=this.curve2DSave.getSegment2D(i).resizeSegment(this.startBox, newBox);
        }
        this.curve2D.setData(segments);
        if(this.shapeContainer.isTextBox()&&resizeTextBox){
            Rectangle2D newTextArea=ShapeElementUtil.reseizeRectangle(this.textAreaSave, this.startBox, newBox);
            this.shapeContainer.getTextBox().setTextArea(newTextArea);
        }
    }

    public void moveResize(Rectangle2D oldBox, Rectangle2D newBox, boolean resizeTextBox){
        if(this.getShapeContainer()==null) {
            System.err.println("*** Error ShapeElement.resize: No shapeContainer\n"
                     +this.getShapeIdString()+"  "+ this.toString());
            return;
        }
        boolean result=this.resizeCheck(this.getShapeContainer(), oldBox, newBox);
        this.getShapeContainer().setChangeCode(UndoConstants.SIZE_POSITION);
        int size=this.curve2D.getNumOfSegments();
        Segment2D[] segments=new Segment2D[size];
        for(int i=0;i<size;i++){
            segments[i]=this.curve2D.getSegment2D(i).resizeSegment(oldBox, newBox);
        }
        this.curve2D.setData(segments);
        if(this.shapeContainer.isTextBox()&&resizeTextBox){
            this.shapeContainer.getTextBox().resizeTextArea(oldBox, newBox);
        }
    }

    private boolean resizeCheck(ShapeContainer container, Rectangle2D oldBox,
            Rectangle2D newBox) {
        boolean result=true;
        int type=container.getElement().getTypeE();
        boolean resizable=container.getBooleanProperty("Enable_Resizing", "ShapeElement");
        boolean aspectRatio=container.getBooleanProperty("Keep_Aspect_Ratio", "ShapeElement");
        boolean lineDir=container.getBooleanProperty("Keep_Line_Direction", "ShapeElement");
        double oldRatio=oldBox.getHeight()/oldBox.getWidth();
        double newRatio=newBox.getHeight()/newBox.getWidth();
        if(aspectRatio){
            if(Math.abs(oldRatio-newRatio)>1.0e-4){
                System.err.println("Warning : check for ShapeElement.resize  "
                        + " illegal aspect ratio  old(h/w)="+oldRatio+", new(h/w)="+newRatio+
                        ", shapeId="+container.getShapeId());
                result=false;
            }
        }
        if(lineDir){
            if(Math.abs(oldRatio-newRatio)>1.0e-4){
                System.err.println("Warning : check for ShapeElement.resize  "
                        + " illegal line direction  old(h/w)="+oldRatio+", new(h/w)="+newRatio+
                        ", shapeId="+container.getShapeId());;
                result=false;
            }
        }
        return result;
    }

    public abstract void moveEndPoint(int ctrl, int movePtIndex, Point2D currentPoint);
    public abstract void moveEndPoint(int ctrl, int movePtIndex,  Point2D oldPoint, Point2D newPoint);

    public abstract void modify(int ctrl, Point2D oldPoint, Point2D newPoint);
    public abstract void modifyPoint(Point2D point, String command);


    public void transform(Matrix2D M){
        this.getShapeContainer().setChangeCode(UndoConstants.SHAPE);
        Curve2D curve=this.getCurve2D();
        int numseg=curve.getNumOfSegments();
        Segment2D[] segments=curve.getSegment2Ds();
        for(int i=0;i<numseg;i++){
            segments[i]=segments[i].transformSegment(M);
        }
        curve.setData(segments);
    }
    
    public abstract Object clone();
    public abstract String toString();
}