//[skeleton1.
package visual.statik.described;


import java.awt.*;
import java.awt.geom.*;


/**
 * Described visual content that knows how to render and transform
 * itself
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Content 
             extends visual.statik.AbstractTransformableContent
             implements TransformableContent
{
    private Color                color;    
    private Paint                paint;    
    private Rectangle2D.Double   originalBounds;
    private Rectangle2D.Double   transformedBounds;
    private Shape                originalShape;
    private Shape                transformedShape;
    private Stroke               stroke;

    private final Stroke          DEFAULT_STROKE = 
                                   new BasicStroke();

    private final AffineTransform IDENTITY = 
                                   new AffineTransform();
    
    /**
     * Default Constructor
     */
    public Content()
    {
       this(null, null, null, null);
    }
    
    

    /**
     * Explicit Value Constructor
     *
     * @param shape     The Shape
     * @param color     The Color to stroke with (or null for no stroke)
     * @param paint     The Paint to fill with (or null for no fill)
     * @param stroke    The Stroke (or null for the default)
     */
    public Content(Shape  shape,  Color color, Paint paint,
                   Stroke stroke)
    {
       super();       

       setColor(color);
       setPaint(paint);
       setStroke(stroke);       

       setShape(shape);
    }
//]skeleton1.

//[transform.
    /**
     * Create the transformed version of the shape
     */
    private void createTransformedContent()
    {
       createTransformedContent(getAffineTransform());
    }

    /**
     * Create a transformed version of the shape
     *
     * @param at   The AffineTransform to use
     */
    private void createTransformedContent(AffineTransform at)
    {
       transformedShape = at.createTransformedShape(
                                              originalShape);
       setTransformationRequired(false);       

       getBoundsFor(transformedBounds, transformedShape);
    }
//]transform.



//[bounds.
    /**
     * Returns a high precision bounding box of the Content
     * either before or after it is transformed
     *
     * @param    transformed    true to get the BB of the transformed content
     * @return   The bounding box
     */
    public Rectangle2D getBounds2D(boolean transformed)
    {
       if   (transformed) return transformedBounds;
       else               return originalBounds;
    }
//]bounds.

//[skeleton2.

    /**
     * Fill the given Rectangle2D with the bounds
     * from the given Shape
     *
     * @param r    The Rectangle2D to fill (out)
     * @param s    The Shape to use (in)
     */
    private void getBoundsFor(Rectangle2D.Double r, Shape s)
    {
       Rectangle2D       temp;
       
       if (s != null)
       {
          temp = s.getBounds2D();

          if (temp != null)
          {
             r.x      = temp.getX();
             r.y      = temp.getY();
             r.width  = temp.getWidth();
             r.height = temp.getHeight();  
          }
       }
    }
//]skeleton2.

//[getters1.
    /**
     * Get the stroke Color
     *
     * @return The stroke Color
     */
    public Color getColor()
    {
       return color;       
    }


    /**
     * Get the fill Paint
     *
     * @return The Paint
     */
    public Paint getPaint()
    {
       return paint;       
    }
//]getters1.

//[PathIterator.
    /**
     * Get a PathIterator
     *
     * @param transformed  true to get the transformed version
     * @return             The PathIterator
     */
    public PathIterator getPathIterator(boolean transformed)
    {
       if (transformed)
          return transformedShape.getPathIterator(IDENTITY);       
       else
          return originalShape.getPathIterator(IDENTITY);
    }
//]PathIterator.

//[getters2.

    /**
     * Get the Stroke
     *
     * @return The Stroke
     */
    public Stroke getStroke()
    {
       return stroke;
    }
//]getters2.

//[render.
    /**
     * Render this described visual content
     *
     * @param g2   The rendering engine to use
     */
    public void render(Graphics g)
    {
       Color       oldColor;
       Graphics2D  g2;       
       Paint       oldPaint;
       Stroke      oldStroke;
       

       g2 = (Graphics2D)g;
       
       
       // Transform the Shape (if necessary)
       if (isTransformationRequired()) 
       {
          createTransformedContent();
       }
          
       if (transformedShape != null)
       {
          // Save the state
          oldColor     = g2.getColor();
          oldPaint     = g2.getPaint();
          oldStroke    = g2.getStroke();


          // Fill the Shape (if appropriate)
          if (paint != null)
          {
             g2.setPaint(paint);
             g2.fill(transformedShape);
          }

          // Stroke the Shape if appropriate
          if (color != null)
          {
             if (stroke != null) g2.setStroke(stroke);          
             g2.setColor(color);
             g2.draw(transformedShape);          
          }

          // Restore the state
          g2.setColor(oldColor);
          g2.setPaint(oldPaint);
          g2.setStroke(oldStroke);
       }       
    }
//]render.

//[setters1.
    /**
     * Set the Color to use for stroking
     *
     * @param color The Color to use (or null to prevent stroking)
     */
    public void setColor(Color color)
    {
       this.color = color;       
    }


    /**
     * Set the Paint to use for filling
     *
     * @param paint  The Paint to use (or null to prevent filling)
     */
    public void setPaint(Paint paint)
    {
       this.paint = paint;       
    }


    /**
     * Set the Shape
     *
     * @param shape  The Shape (or null to render nothing)
     */
    public void setShape(Shape shape)
    {
       originalShape     = shape;       

       transformedBounds = new Rectangle2D.Double();
       originalBounds    = new Rectangle2D.Double();

       if (originalShape != null)
       {
          if (isTransformationRequired()) 
          {
             createTransformedContent();
          }
          else
          {
             transformedShape  = shape;       
          }

          getBoundsFor(originalBounds, originalShape);       
          getBoundsFor(transformedBounds, transformedShape);
       }
    }
    

    /**
     * Set the Stroke to use
     *
     * @param stroke  The Stroke to use (or null to use the default)
     */
    public void setStroke(Stroke stroke)
    {
       if (stroke == null) this.stroke = DEFAULT_STROKE;       
       else                this.stroke = stroke;
    }
//]setters1.
//[skeleton3.
}
//]skeleton3.
