/*
 * @(#)PaintMode.java    2.3  9 December 2003
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;

/**
 * <P>Abstract class <CODE>PaintMode</CODE> defines the requirements for an object
 * that can control the painting of a <CODE>ShapePaintable</CODE>.</P>
 *
 * <P>Class <CODE>PaintMode</CODE> provides some examples of its own objects.</P>
 *
 * <P>Class <CODE>PaintMode</CODE> also provides some static convenience methods.</P>
 *
 * @author  Richard Rasala
 * @version 2.3
 * @since   2.3
 */
public abstract class PaintMode {
    
    /**
     * <P>Paints the given shape paintable using the given graphics
     * context.</P>
     *
     * <P>If the given shape paintable or given graphics context is
     * <CODE>null</CODE>, then this method should do nothing.</P>
     *
     * <P>When this method call is complete, the internal state of
     * the given graphics context should be unchanged.</P>
     *
     * @param paintable the shape paintable
     * @param g the graphics context on which to paint
     */
    public abstract void paint(ShapePaintable paintable, Graphics g);
    
    
    /**
     * <P>Returns the 2-dimensional bounds of the paint region
     * that will be changed when the <CODE>paint</CODE> method
     * is called on the given shape paintable.</P>
     *
     * <P>If the given shape paintable is <CODE>null</CODE> or if the
     * bounds may not otherwise be computed then this method should
     * return <CODE>new Rectangle2D.Double()</CODE>.</P>
     *
     * <P>This method should not return <CODE>null</CODE>.</P>
     *
     * @param paintable the shape paintable
     * @return the bounds of the paint region that will be changed
     */
    public abstract Rectangle2D getBounds2D(ShapePaintable paintable);
    
    
    /**
     * <P>Returns the <CODE>Shape</CODE> representing the paint region
     * that will be changed when the <CODE>paint</CODE> method is
     * called on the given shape paintable.</P>
     *
     * <P>If the given shape paintable is <CODE>null</CODE>, then this
     * method should return <CODE>null</CODE>.</P>
     *
     * @param paintable the shape paintable
     * @return the shape of the paint region that will be changed
     */
    public abstract Shape getOutline(ShapePaintable paintable);
    
    
    /**
     * <P>Tests if a point specified by coordinates is inside the outline
     * of the given shape paintable.</P>
     *
     * <P>If the given shape paintable is <CODE>null</CODE>, then this
     * method should return <CODE>false</CODE>.</P>
     *
     * @param paintable the shape paintable
     * @param  x the x-coordinate of the point
     * @param  y the y-coordinate of the point
     * @return whether or not a specified point is inside the paintable
     */
    public abstract boolean contains(ShapePaintable paintable, double x, double y);
    
    
    /**
     * <P>Tests if a specified <CODE>Point2D</CODE> is inside the outline
     * of the given shape paintable.</P>
     *
     * <P>If the given shape paintable is <CODE>null</CODE>, then this
     * method should return <CODE>false</CODE>.</P>
     *
     * @param paintable the shape paintable
     * @param  p a specified point
     * @return whether or not a specified point is inside the paintable
     */
    public abstract boolean contains(ShapePaintable paintable, Point2D p);
    
    
    /**
     * The paint mode corresponding to filling the shape
     * of the shape paintable using its fill paint.
     */
    public static final PaintMode FILL
        = new PaintMode() {
            public void paint(ShapePaintable paintable, Graphics g) {
                if ((paintable == null) || (g == null))
                    return;
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return;
                
                Graphics2D h = paintable.getPreparedGraphics2D(g);
                
                h.setPaint(paintable.getFillPaint());
                h.fill(shape);
            }
            
            public Rectangle2D getBounds2D(ShapePaintable paintable) {
                if (paintable == null)
                    return new Rectangle2D.Double();
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return new Rectangle2D.Double();
                
                return shape.getBounds2D();
            }
            
            public Shape getOutline(ShapePaintable paintable) {
                if (paintable == null)
                    return null;
                
                return paintable.getShape();
            }
            
            public boolean contains(ShapePaintable paintable, double x, double y) {
                if (paintable == null)
                    return false;
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return false;
                
                return shape.contains(x, y);
            }
            
            public boolean contains(ShapePaintable paintable, Point2D p) {
                if (p == null)
                    return false;
                
                return contains(paintable, p.getX(), p.getY());
            }
        };
    
    
    /**
     * The paint mode corresponding to drawing the shape
     * of the shape paintable using its draw paint and
     * draw stroke.
     */
    public static final PaintMode DRAW
        = new PaintMode() {
            public void paint(ShapePaintable paintable, Graphics g) {
                if ((paintable == null) || (g == null))
                    return;
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return;
                
                Graphics2D h = paintable.getPreparedGraphics2D(g);
                
                h.setPaint(paintable.getDrawPaint());
                h.fill(strokedShape);
            }
            
            public Rectangle2D getBounds2D(ShapePaintable paintable) {
                if (paintable == null)
                    return new Rectangle2D.Double();
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return new Rectangle2D.Double();
                
                return strokedShape.getBounds2D();
            }
            
            public Shape getOutline(ShapePaintable paintable) {
                if (paintable == null)
                    return null;
                
                return paintable.getStrokedShape();
            }
            
            public boolean contains(ShapePaintable paintable, double x, double y) {
                if (paintable == null)
                    return false;
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return false;
                
                return strokedShape.contains(x, y);
            }
            
            public boolean contains(ShapePaintable paintable, Point2D p) {
                if (p == null)
                    return false;
                
                return contains(paintable, p.getX(), p.getY());
            }
        };
    
    
    /**
     * The paint mode corresponding to filling the shape
     * of the shape paintable using its fill paint and
     * then drawing the shape using its draw paint and
     * draw stroke.
     */
    public static final PaintMode FILL_DRAW
        = new PaintMode() {
            public void paint(ShapePaintable paintable, Graphics g) {
                if ((paintable == null) || (g == null))
                    return;
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return;
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return;
                
                Graphics2D h = paintable.getPreparedGraphics2D(g);
                
                h.setPaint(paintable.getFillPaint());
                h.fill(shape);
                
                h.setPaint(paintable.getDrawPaint());
                h.fill(strokedShape);
            }
            
            public Rectangle2D getBounds2D(ShapePaintable paintable) {
                if (paintable == null)
                    return new Rectangle2D.Double();
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return new Rectangle2D.Double();
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return new Rectangle2D.Double();
                
                Rectangle2D bounds1 = shape.getBounds2D();
                Rectangle2D bounds2 = strokedShape.getBounds2D();
                
                bounds1.add(bounds2);
                
                return bounds1;
            }
            
            public Shape getOutline(ShapePaintable paintable) {
                if (paintable == null)
                    return null;
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return null;
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return null;
                
                Area area1 = new Area(shape);
                Area area2 = new Area(strokedShape);
                
                area1.add(area2);
                
                return area1;
            }
            
            public boolean contains(ShapePaintable paintable, double x, double y) {
                if (paintable == null)
                    return false;
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return false;
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return false;
                
                return shape.contains(x, y) || strokedShape.contains(x, y);
            }
            
            public boolean contains(ShapePaintable paintable, Point2D p) {
                if (p == null)
                    return false;
                
                return contains(paintable, p.getX(), p.getY());
            }
        };
    
    
    /**
     * <P>Fills the given shape using the given fillpaint
     * in the given graphics context.</P>
     *
     * @param g the graphics context
     * @param shape the shape to paint
     * @param fillpaint the paint to use for fill
     * @see #FILL
     */
    public static void fill(
        Graphics  g,
        Shape     shape,
        Paint     fillpaint)
    {
        ShapePaintable paintable =
            new ShapePaintable(shape, FILL, fillpaint);
        
        paintable.paint(g);
    }
    
    
    /**
     * <P>Draws the given shape using the given drawpaint
     * in the given graphics context.</P>
     *
     * @param g the graphics context
     * @param shape the shape to paint
     * @param drawpaint the paint to use for draw
     * @see #DRAW
     */
    public static void draw(
        Graphics  g,
        Shape     shape,
        Paint     drawpaint)
    {
        ShapePaintable paintable =
            new ShapePaintable(shape, DRAW, null, drawpaint);
        
        paintable.paint(g);
    }
    
    
    /**
     * <P>Draws the given shape using the given drawpaint and
     * the given drawstroke in the given graphics context.</P>
     *
     * @param g the graphics context
     * @param shape the shape to paint
     * @param drawpaint the paint to use for draw
     * @param drawstroke the stroke to use for draw
     * @see #DRAW
     */
    public static void draw(
        Graphics  g,
        Shape     shape,
        Paint     drawpaint,
        Stroke    drawstroke)
    {
        ShapePaintable paintable =
            new ShapePaintable(shape, DRAW, null, drawpaint, drawstroke);
        
        paintable.paint(g);
    }
    
    
    /**
     * <P>Fills and draws the given shape using the given fillpaint
     * and the given drawpaint in the given graphics context.</P>
     *
     * @param g the graphics context
     * @param shape the shape to paint
     * @param fillpaint the paint to use for fill
     * @param drawpaint the paint to use for draw
     * @see #FILL_DRAW
     */
    public static void filldraw(
        Graphics  g,
        Shape     shape,
        Paint     fillpaint,
        Paint     drawpaint)
    {
        ShapePaintable paintable =
            new ShapePaintable(shape, FILL_DRAW, fillpaint, drawpaint);
        
        paintable.paint(g);
    }
    
    
    /**
     * <P>Fills and draws the given shape using the given fillpaint,
     * the given drawpaint, and the given drawstroke
     * in the given graphics context.</P>
     *
     * @param g the graphics context
     * @param shape the shape to paint
     * @param fillpaint the paint to use for fill
     * @param drawpaint the paint to use for draw
     * @param drawstroke the stroke to use for draw
     * @see #FILL_DRAW
     */
    public static void filldraw(
        Graphics  g,
        Shape     shape,
        Paint     fillpaint,
        Paint     drawpaint,
        Stroke    drawstroke)
    {
        ShapePaintable paintable =
            new ShapePaintable(shape, FILL_DRAW, fillpaint, drawpaint, drawstroke);
        
        paintable.paint(g);
    }
    
}
