package ngp.shapes;

import java.awt.*;
import java.awt.geom.*;
import ngp.*;
import ngp.fills.*;


/**
 * Simple shape that knows its position and velocity.
 */
public abstract class AbstractShape implements ngp.Animatable
{
    private Point2D myCenter;
    private Dimension mySize;
    private Color myColor;
    private Fill myFill;
    private double myHeading;
    private Group myGroup = null;
    private Shape myRepresentation;
    private java.util.List myBehaviors;


    public AbstractShape ()
    {
        this(new Point2D.Double(0, 0),
             new Dimension(20, 20),
             Color.BLACK,
             new Rectangle2D.Double());
    }

    public AbstractShape (Point2D center,
                          Dimension size,
                          Color color,
                          Shape graphic)
    {
        myCenter = center;
        myColor = color;
        mySize = size;
        myFill = new Solid(color);
        myHeading = 0;
        myRepresentation = graphic;
        myBehaviors = new java.util.ArrayList();
    }


    public Point2D getCenter ()
    {
        if (myGroup == null)
            return myCenter;
        else
            return myGroup.getTransform().transform(myCenter, null);
    }

    public double getLeft ()
    {
        return getCenter().getX() - getSize().width / 2;
    }

    public double getTop ()
    {
        return getCenter().getY() - getSize().height / 2;
    }

    public double getRight ()
    {
        return getCenter().getX() + getSize().width / 2;
    }

    public double getBottom ()
    {
        return getCenter().getY() + getSize().height / 2;
    }

    public Dimension getSize ()
    {
        return mySize;
    }

    public Color getColor ()
    {
        return myColor;
    }

    public Fill getFill ()
    {
        return myFill;
    }

    public double getHeading ()
    {
        return myHeading;
    }


    public void setColor (Color color)
    {
        myColor = color;
    }

    public void setCenter (Point2D center)
    {
        myCenter = center;
    }

    public void setSize (Dimension size)
    {
        mySize = size;
    }

    public void setHeading (double degrees)
    {
        myHeading = Math.toRadians(degrees);
    }

    public void setFill (Fill fill)
    {
        myFill = fill;
    }


    public void translate (double dx, double dy)
    {
        myCenter.setLocation(myCenter.getX() + dx,
			     myCenter.getY() + dy);
    }

    public void scale (double xFactor, double yFactor)
    {
        mySize.width  *= xFactor;
        mySize.height *= yFactor;
    }

    public void rotate (double degrees)
    {
        myHeading += Math.toRadians(degrees);
    }



    public void add (Behavior action)
    {
        myBehaviors.add(action);
    }

    public void remove (Behavior action)
    {
        myBehaviors.remove(action);
    }


    public void update (ngp.Canvas canvas)
    {
        java.util.Iterator iter = myBehaviors.iterator();
        while (iter.hasNext())
        {
            ((Behavior)iter.next()).step(this);
        }
    }

    public void render (java.awt.Graphics2D pen)
    {
        // save current graphics state
        AffineTransform old = pen.getTransform();
        // correctly position shape relative to others
        pen.transform(getTransform());
        // actually show the shape
        paint(pen);
        // restore graphics state
        pen.setTransform(old);
    }

    protected void paint (java.awt.Graphics2D pen)
    {
        myFill.paint(pen, myRepresentation);
    }

    protected AffineTransform getTransform ()
    {
        AffineTransform result = new AffineTransform();

        result.translate(myCenter.getX(), myCenter.getY());
        result.rotate(myHeading);
        result.translate(-mySize.width / 2, -mySize.height / 2);
        result.scale(mySize.width, mySize.height);

        return result;
    }

    protected void addToGroup (Group parent)
    {
        myGroup = parent;
    }

    protected void removeFromGroup (Group parent)
    {
        myGroup = null;
    }

    protected boolean intersects (AbstractShape lhs, AbstractShape rhs)
    {
        return ((lhs.getRight() > rhs.getLeft()) &&
                (lhs.getBottom() > rhs.getTop()) &&
                (lhs.getLeft() < rhs.getRight()) && 
                (lhs.getTop() < rhs.getBottom()));
    }
}
