/*
 * @(#)PaintMode.java    2.4.0  25 May 2005
 *
 * Copyright 2005
 * 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 edu.neu.ccs.*;

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>
 *
 * <p>In 2.3.5, the class was refactored to be consistent with
 * the new <code>Paintable</code> interface and
 * the new <code>AbstractPaintable</code> class.
 *
 * <p>In 2.4.0, this class was updated to be consistent with refinements
 * to the <code>Paintable</code> interface.</p>
 *
 * @author  Richard Rasala
 * @version 2.4.0
 * @since   2.3
 */
public abstract class PaintMode {
    
    /**
     * <P>Paints the given shape paintable without mutation using
     * the given graphics context.</P>
     *
     * <p>It is assumed that the graphics context has been prepared
     * and mutation has been applied by the caller of this method.</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 originalPaint(ShapePaintable paintable, Graphics g);
    
    
    /**
     * <P>Returns the original 2-dimensional bounds of the paint
     * region that will be changed when the <CODE>originalPaint</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 orignal bounds of the paint region that will be changed
     */
    public abstract XRect getActualBounds2D(ShapePaintable paintable);
    
    
    /**
     * <P>Returns the <CODE>Shape</CODE> representing the paint region
     * that will be changed when the <CODE>originalPaint</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 orignal
     * 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 originalContains(ShapePaintable paintable, double x, double y);
    
    
    /**
     * <P>Tests if a point specified by coordinates is inside the orignal
     * 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 final boolean originalContains(ShapePaintable paintable, Point2D p) {
        if (p == null)
            return false;
        
        return originalContains(paintable, p.getX(), p.getY());
    }
    
    
    /**
     * 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 originalPaint(ShapePaintable paintable, Graphics g) {
                if ((paintable == null) || (g == null))
                    return;
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return;
                
                Graphics2D h = (Graphics2D) g.create();
                
                h.setPaint(paintable.getFillPaint());
                h.fill(shape);
            }
            
            public XRect getActualBounds2D(ShapePaintable paintable) {
                if (paintable == null)
                    return new XRect();
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return new XRect();
                
                return new XRect(shape.getBounds2D());
            }
            
            public Shape getOutline(ShapePaintable paintable) {
                if (paintable == null)
                    return null;
                
                return paintable.getShape();
            }
            
            public boolean originalContains(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);
            }
        };
    
    
    /**
     * 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 originalPaint(ShapePaintable paintable, Graphics g) {
                if ((paintable == null) || (g == null))
                    return;
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return;
                
                Graphics2D h = (Graphics2D) g.create();
                
                h.setPaint(paintable.getDrawPaint());
                h.fill(strokedShape);
            }
            
            public XRect getActualBounds2D(ShapePaintable paintable) {
                if (paintable == null)
                    return new XRect();
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return new XRect();
                
                return new XRect(strokedShape.getBounds2D());
            }
            
            public Shape getOutline(ShapePaintable paintable) {
                if (paintable == null)
                    return null;
                
                return paintable.getStrokedShape();
            }
            
            public boolean originalContains(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);
            }
        };
    
    
    /**
     * 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 originalPaint(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 = (Graphics2D) g.create();
                
                h.setPaint(paintable.getFillPaint());
                h.fill(shape);
                
                h.setPaint(paintable.getDrawPaint());
                h.fill(strokedShape);
            }
            
            public XRect getActualBounds2D(ShapePaintable paintable) {
                if (paintable == null)
                    return new XRect();
                
                Shape shape = paintable.getShape();
                
                if (shape == null)
                    return new XRect();
                
                Shape strokedShape = paintable.getStrokedShape();
                
                if (strokedShape == null)
                    return new XRect();
                
                XRect bounds1 = new XRect(shape.getBounds2D());
                XRect bounds2 = new XRect(strokedShape.getBounds2D());
                
                XRect bounds = bounds1.createUnionRect(bounds2);
                
                return bounds;
            }
            
            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 originalContains(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);
            }
        };
    
    
    /**
     * <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);
    }
    
}
