package cnp.ew.diagram;

import java.awt.*;
import java.util.*;

import cnp.ew.lightweight.*;

public class CpDependentLineLc extends CpAbstractDiagrammableLc
{
    public static final int MOVE_POINT = 30;
    public static final int POINTS = 31;

    public static final int NONE = 0;
    public static final int DOT = 1;
    public static final int ARROW = 2;
    public static final int DIAMOND = 3;

    Dimension decor = new Dimension(8, 8);

    Vector points;
    public int startDecoration = NONE;
    public int endDecoration = NONE;

    public CpDependentLineLc()
    {
        super(0, 0, 0, 0);
    }

    public CpDependentLineLc(Vector newPoints)
    {
        super(0, 0, 0, 0);
        setPoints(newPoints);
    }

    // NEED A CpMoveLinePointHandle subclass of ParameterHandle, constructor keeps the index of the point to move, passes
    // that in as the arg with the point to setParam

/*    public void basicSetParameter(int parameter, Object value)
	{
	    switch (parameter) {
	        case MOVE_POINT : {
	            Vector args = (Vector)value;
	            int index = ((Integer)args.elementAt(0)).intValue();
	            Point p = (Point)args.elementAt(1);
	            setPoint(index, p);
	            break;
	        }
	        case POINTS : {
	            setPoints((Vector)value);
	            break;
	        }
	        default :
	            super.basicSetParameter(parameter, value);
	    }
	}
	*/

    public void setPoint(int index, Point newPoint)
    {
        boolean diff = !((Point)points.elementAt(index)).equals(newPoint);
        points.setElementAt(newPoint, index);
        enableDamageRepair(false);
        reshape(calculateBoundingBox());
        enableDamageRepair(true);
        if (diff) {
            repaint();
        }
    }

    public boolean differentFrom(Vector newPoints)
    {
        if (points == null) {
            return true;
        }
        if (points.size() != newPoints.size()) {
            return true;
        }
        for (int i = 0; i < points.size(); i++) {
            if (!((Point)points.elementAt(i)).equals((Point)newPoints.elementAt(i))) {
                return true;
            }
        }
        return false;
    }

    // Don't let anyone move it where it shoudn't be!  When first added to something, the dependsOn things move, causing this to move, but then THIS is move too.  Wrong.
    public void move(int x, int y)
    {
        if (points == null) {
            super.move(x, y);
        } else {
            Rectangle r = calculateBoundingBox();
            if (r.x == x && r.y == y) {
                super.move(x, y);
            }
        }
    }

    public void setPoints(Vector newPoints)
    {
        boolean diff = differentFrom(newPoints);
        points = newPoints;
        enableDamageRepair(false);
        reshape(calculateBoundingBox());
        enableDamageRepair(true);
        if (diff) {
            repaint();
        }
    }

    public Vector getPoints()
    {
        return points;
    }

	public boolean intersects(Rectangle r)
	{
	    Point p1, p2;

        if (points.isEmpty()) {
            return false;
        }

	    p1 = (Point)points.elementAt(0);
	    for (int i = 1; i < points.size(); i++) {
            p2 = (Point)points.elementAt(i);
            if (CpGraphicsUtilities.lineIntersectsRect(p1, p2, r)) {
                return true;
            }
            p1 = p2;
        }
        return false;
    }

	public boolean insideDiagram(int x, int y)
	{
        return segmentHitBy(x, y) != null;
    }

	public Point segmentHitBy(int x, int y)
	{
		Point p1, p2;

        if (points.isEmpty()) {
            return null;
        }

	    p1 = (Point)points.elementAt(0);
	    for (int i = 1; i < points.size(); i++) {
            p2 = (Point)points.elementAt(i);
            if (CpGraphicsUtilities.lineHit(p1, p2, x, y)) {
                return new Point(i - 1, i);
            }
            p1 = p2;
        }
        return null;
    }

    public Rectangle calculateBoundingBox()
    {
        Point p;
        int left = Integer.MAX_VALUE;
        int right = 0;
        int top = Integer.MAX_VALUE;
        int bottom = 0;
        for (int i = 0; i < points.size(); i++) {
            p = (Point)points.elementAt(i);
            left = Math.min(p.x, left);
            top = Math.min(p.y, top);
            right = Math.max(p.x, right);
            bottom = Math.max(p.y, bottom);
        }
        // the expand by 1 is to handle the fact that lines draw 1 extra pixel, compared to rects
        // Make that 5.  simple hack to help if line has end decorations.
        return  new Rectangle(left - decor.width, top - decor.width, right - left + decor.width + decor.width, bottom - top + decor.height + decor.height);
    }

	public boolean canBeSelected()
	{
   	    return false;
	}

	public Vector getHandles()
	{
	    Point p;

	    Vector v = new Vector();
        for (int i = 0; i < points.size(); i++) {
            p = (Point)points.elementAt(i);
    		v.addElement(new CpMoveLinePointHandle(this, new CpLinePointLocator(i), i));
    	}
		return v;
	}

	public CpHandle getDefaultHandle()
    {
	    return new CpLineSplitterHandle(this, null);
	}

	// To act as a locator:
    public Point getPoint(int index)
	{
	    return (Point)points.elementAt(index);
	}

	public void scale(double newScale)
	{
	    // This is done by dependency instead.  WHAT ABOUT INDEPENDENT LINES?  FLAG?
        double factor = newScale / getScale();
	    decor = new Dimension(Math.max(1, (int)(decor.width * factor)), Math.max(1, (int)(decor.height * factor)));
        setScale(newScale);
	  //  super.scale(newScale);
	    /*
	    Vector newPoints = new Vector();
	    for (int i = 0; i < points.size(); i++) {
            Point p = (Point)points.elementAt(i);
            Point newPoint = new Point((int)(p.x * factor), (int)(p.y * factor));
            newPoints.addElement(newPoint);
        }
        setPoints(newPoints); */
    }

    // Hmmmm... keeping the points as "global" coords and drawing them starting at 0,0.  Should we keep them as local points?
	public void paint(Graphics g, Rectangle clip)
	{
	    Point p1, p2, loc;

        if (points.isEmpty()) {
            return;
        }
        g.setColor(getForeground());
        loc = new Point(boundsDiagram().x, boundsDiagram().y);
	    p1 = (Point)points.elementAt(0);
	    for (int i = 1; i < points.size(); i++) {
            p2 = (Point)points.elementAt(i);
            g.drawLine(p1.x - loc.x, p1.y - loc.y, p2.x - loc.x, p2.y - loc.y);
            p1 = p2;
        }

        paintDecoration(g, (Point)points.elementAt(1), (Point)points.elementAt(0), startDecoration);
        paintDecoration(g, (Point)points.elementAt(points.size() - 2), (Point)points.elementAt(points.size() - 1), endDecoration);
    }

    public void paintDecoration(Graphics g, Point from, Point to, int decoration)
    {
        if (decoration == NONE) {
            return;
        }

        Polygon p = new Polygon();
        Point startPoint;

	    Point loc;
        loc = new Point(boundsDiagram().x, boundsDiagram().y);
        to = new Point(to.x - loc.x, to.y - loc.y);
        from = new Point(from.x - loc.x, from.y - loc.y);

        int deltaX = to.x - from.x;
        int deltaY = to.y - from.y;

        if (Math.abs(deltaX) > Math.abs(deltaY)) {  // Primarily left right
            if (deltaX > 0) { // left to right
                startPoint = new Point(to.x - decor.width, to.y - decor.height / 2);
                if (decoration == ARROW || decoration == DIAMOND) {
                    p.addPoint(to.x, to.y);
                    p.addPoint(startPoint.x, startPoint.y);
                    if (decoration == DIAMOND) {
                        p.addPoint(startPoint.x - decor.width, to.y);
                    }
                    p.addPoint(startPoint.x, startPoint.y + decor.height);
                    p.addPoint(to.x, to.y);
                }
            } else {
                startPoint = new Point(to.x, to.y - decor.height / 2);
                if (decoration == ARROW || decoration == DIAMOND) {
                    p.addPoint(to.x, to.y);
                    p.addPoint(to.x + decor.width, startPoint.y);
                    if (decoration == DIAMOND) {
                        p.addPoint(to.x + decor.width + decor.width, to.y);
                    }
                    p.addPoint(to.x + decor.width, startPoint.y + decor.height);
                    p.addPoint(to.x, to.y);
                }
            }
        } else {
            if (deltaY > 0) { // top to bottom
                startPoint = new Point(to.x - decor.width / 2, to.y - decor.height);
                if (decoration == ARROW || decoration == DIAMOND) {
                    p.addPoint(to.x, to.y);
                    p.addPoint(startPoint.x, startPoint.y);
                    if (decoration == DIAMOND) {
                        p.addPoint(to.x, startPoint.y - decor.height);
                    }
                    p.addPoint(startPoint.x + decor.width, startPoint.y);
                    p.addPoint(to.x, to.y);
                }
            } else {
                startPoint = new Point(to.x - decor.width / 2, to.y);
                if (decoration == ARROW || decoration == DIAMOND) {
                    p.addPoint(to.x, to.y);
                    p.addPoint(startPoint.x, to.y + decor.height);
                    if (decoration == DIAMOND) {
                        p.addPoint(to.x, to.y + decor.height + decor.height);
                    }
                    p.addPoint(startPoint.x + decor.width, to.y + decor.height);
                    p.addPoint(to.x, to.y);
                }
            }
        }


        g.setColor(Color.black);
        switch (decoration) {
            case DOT : {
                g.fillRect(startPoint.x, startPoint.y, decor.width, decor.height);
                break;
            }
            case DIAMOND : {
                g.setColor(new Color(0, 0, 128));
                g.fillPolygon(p);
                g.setColor(Color.black);
                g.drawPolygon(p);
                break;
            }
            case ARROW : {
                g.fillPolygon(p);
            }
        }
	}

	public boolean isTransparent()
	{
	    return true;
	}
}