/*
 * @(#)ImagePaintable.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.*;
import java.net.URL;

/**
 * <p>An <code>ImagePaintable</code> creates an <code>AbstractPaintable</code>
 * using an image, image icon, filename, or URL.</p>
 *
 * <p>Specifically, an <code>ImagePaintable</code> object may be created using
 * one of the following:</p>
 *
 * <ul>
 *   <li>an image specified by an Image object,</li>
 *   <li>an image specified by an ImageIcon object,</li>
 *   <li>an image specified by a filename,</li>
 *   <li>an image specified by a URL.</li>
 * </ul>
 *
 * <p>In 2.3.2, the method <code>getBounds2D</code> was modified to use the
 * default Bounds2D rectangle if that rectangle is non-<code>null</code>
 * before making any other tests or computations.</p>
 *
 * <p>In 2.3.3, the code was made parallel to that of the new class
 * <code>ImagePaintableLite</code>.  The class <code>ImagePaintable</code>
 * retains image data throughout the life time of an instance and thus may
 * paint more rapidly at the cost of larger memory footprint.</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
 * @see     ImagePaintableLite
 */
public class ImagePaintable extends AbstractPaintable
{
    /** Bound property name for set image. */
    public static final String SET_IMAGE           = "set.image";
        
    /** Bound property name for set top left point. */
    public static final String SET_TOP_LEFT_CORNER = "set.top.left.corner";
        
    
    /** The image. */
    private Image image = null;
    
    /** The top left corner x-position. */
    private double xCorner = 0;
    
    /** The top left corner y-position. */
    private double yCorner = 0;
    
    /** The image width. */
    private int width = 0;
    
    /** The image height. */
    private int height = 0;
    
    
    /**
     * <p>The default constructor that does not specify the image.</p>
     *
     * <p>Other constructors:</p>
     *
     * <ul><code>
     *   <li>ImagePaintable(Image)</li>
     *   <li>ImagePaintable(Image, Point2D)</li>
     *   <li>ImagePaintable(Image, double, double)</li>
     *   <li>ImagePaintable(ImageIcon)</li>
     *   <li>ImagePaintable(ImageIcon, Point2D)</li>
     *   <li>ImagePaintable(ImageIcon, double, double)</li>
     *   <li>ImagePaintable(String)</li>
     *   <li>ImagePaintable(String, Point2D)</li>
     *   <li>ImagePaintable(String, double, double)</li>
     *   <li>ImagePaintable(URL)</li>
     *   <li>ImagePaintable(URL, Point2D)</li>
     *   <li>ImagePaintable(URL, double, double)</li>
     * </code></ul>
     */
    public ImagePaintable() {}
    
    
    /**
     * <p>The constructor to specify the image directly.</p>
     *
     * @param image the image
     * @see #ImagePaintable()
     */
    public ImagePaintable(Image image) {
        setImage(image);
    }
    
    
    /**
     * <p>The constructor to specify the image directly
     * and the top left corner.</p>
     *
     * @param image the image
     * @param corner the top left corner
     * @see #ImagePaintable()
     */
    public ImagePaintable(Image image, Point2D corner) {
        setImage(image);
        setTopLeftCorner(corner);
    }
    
    
    /**
     * <p>The constructor to specify the image directly
     * and the top left corner (x, y).</p>
     *
     * @param image the image
     * @param x the x-coordinate of the top left corner
     * @param y the y-coordinate of the top left corner
     * @see #ImagePaintable()
     */
    public ImagePaintable(Image image, double x, double y) {
        setImage(image);
        setTopLeftCorner(x, y);
    }
    
    
    /**
     * <p>The constructor to specify the image by image icon.</p>
     *
     * @param imageicon the imageicon to define the image
     * @see #ImagePaintable()
     */
    public ImagePaintable(ImageIcon imageicon) {
        setImageByImageIcon(imageicon);
    }
    
    
    /**
     * <p>The constructor to specify the image by image icon
     * and the top left corner.</p>
     *
     * @param imageicon the imageicon to define the image
     * @param corner the top left corner
     * @see #ImagePaintable()
     */
    public ImagePaintable(ImageIcon imageicon, Point2D corner) {
        setImageByImageIcon(imageicon);
        setTopLeftCorner(corner);
    }
    
    
    /**
     * <p>The constructor to specify the image by image icon
     * and the top left corner (x, y).</p>
     *
     * @param imageicon the imageicon to define the image
     * @param x the x-coordinate of the top left corner
     * @param y the y-coordinate of the top left corner
     * @see #ImagePaintable()
     */
    public ImagePaintable(ImageIcon imageicon, double x, double y) {
        setImageByImageIcon(imageicon);
        setTopLeftCorner(x, y);
    }
    
    
    /**
     * <p>The constructor to specify the image by file name.</p>
     *
     * @param filename the filename for the file with the image
     * @see #ImagePaintable()
     */
    public ImagePaintable(String filename) {
        setImageByFileName(filename);
    }
    
    
    /**
     * <p>The constructor to specify the image by file name
     * and the top left corner.</p>
     *
     * @param filename the filename for the file with the image
     * @param corner the top left corner
     * @see #ImagePaintable()
     */
    public ImagePaintable(String filename, Point2D corner) {
        setImageByFileName(filename);
        setTopLeftCorner(corner);
    }
    
    
    /**
     * <p>The constructor to specify the image by file name
     * and the top left corner (x, y).</p>
     *
     * @param filename the filename for the file with the image
     * @param x the x-coordinate of the top left corner
     * @param y the y-coordinate of the top left corner
     * @see #ImagePaintable()
     */
    public ImagePaintable(String filename, double x, double y) {
        setImageByFileName(filename);
        setTopLeftCorner(x, y);
    }
    
    
    /**
     * <p>The constructor to specify the image by URL.</p>
     *
     * @param url the URL location for the image
     * @see #ImagePaintable()
     */
    public ImagePaintable(URL url) {
        setImageByURL(url);
    }
    
    
    /**
     * The constructor to specify the image by URL
     * and the top left corner.
     *
     * @param url the URL location for the image
     * @param corner the top left corner
     * @see #ImagePaintable()
     */
    public ImagePaintable(URL url, Point2D corner) {
        setImageByURL(url);
        setTopLeftCorner(corner);
    }
    
    
    /**
     * The constructor to specify the image by URL
     * and the top left corner (x, y).
     *
     * @param url the URL location for the image
     * @param x the x-coordinate of the top left corner
     * @param y the y-coordinate of the top left corner
     * @see #ImagePaintable()
     */
    public ImagePaintable(URL url, double x, double y) {
        setImageByURL(url);
        setTopLeftCorner(x, y);
    }
    
    
    /**
     * <P>Paints onto a <CODE>Graphics</CODE> context using information
     * from this object but without the use of the mutator transform.</P>
     *
     * <p>If the current image or the graphics context is <code>null</code>,
     * this method will not paint.</p>
     *
     * @param g the graphics context on which to paint
     */
    public final void originalPaint(Graphics g) {
        if ((image == null) || (g == null) || !isVisible())
            return;
        
        Graphics2D h = (Graphics2D) g.create();
        
        AffineTransform T = TransformFactory.translate(xCorner, yCorner);
        
        h.drawImage(image, T, null);
    }
    
    
    /**
     * <p>Returns the actual bounds of the original paintable or
     * <code>null</code> if the paintable is effectively empty.</p>
     */
    public final XRect getActualBounds2D() {
        if (image == null)
            return null;
        
        return new XRect(xCorner, yCorner, width, height);
    }
    
    
    /**
     * <p>Tests if a point specified by coordinates is inside the
     * original paintable without mutation.</p>
     *
     * @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
     *         original paintable
     */
    public final boolean originalContains(double x, double y) {
        if ((width <= 0) || (height <= 0))
            return false;
        
        return possiblyContains(x, y);
    }
    
    
    /**
     * <p>Sets the image directly.</p>
     *
     * <p>Fires property change: SET_IMAGE.</p>
     * 
     * @param image the image
     */
    public final void setImage(Image image) {
        if (image == this.image)
            return;
        
        removeAndAddForwardingListener(this.image, image);
        
        this.image = image;
        
        setImageSizeParameters();
    }
    
    
    /**
     * <p>Sets the image by image icon.</p>
     *
     * <p>If the image icon is <code>null</code>,
     * the encapsulated image is set to <code>null</code>.</p>
     *
     * <p>Fires property change: SET_IMAGE.</p>
     * 
     * @param imageicon the imageicon to define the image
     */
    public final void setImageByImageIcon(ImageIcon imageicon) {
        if (imageicon == null)
            setImage(null);
        else
            setImage(imageicon.getImage());
    }
    
    
    /**
     * <p>Sets the image by file name.</p>
     *
     * <p>If the file name is <code>null</code>,
     * the encapsulated image is set to <code>null</code>.</p>
     *
     * <p>Fires property change: SET_IMAGE.</p>
     * 
     * @param filename the filename for the file with the image
     */
    public final void setImageByFileName(String filename) {
        if (filename == null)
            setImage(null);
        else
            setImageByImageIcon(new ImageIcon(filename));
    }
    
    
    /**
     * <p>Sets the image by URL.</p>
     *
     * <p>If the url is <code>null</code>,
     * the encapsulated image is set to <code>null</code>.</p>
     *
     * <p>Fires property change: SET_IMAGE.</p>
     * 
     * @param url the URL location for the image
     */
    public final void setImageByURL(URL url) {
        if (url == null)
            setImage(null);
        else
            setImageByImageIcon(new ImageIcon(url));
    }
    
    
    /**
     * Returns the encapsulated image.
     *
     * @return the encapsulated image
     */
    public final Image getImage() {
        return image;
    }
    
    
    /**
     * <p>Sets the top left corner with the given corner.</p>
     *
     * <p>A <code>null</code> parameter is ignored.</p>
     *
     * <p>Fires property change: SET_TOP_LEFT_CORNER.</p>
     * 
     * @param corner the top left corner
     */
    public final void setTopLeftCorner(Point2D corner) {
        if (corner == null)
            return;
        
        setTopLeftCorner(corner.getX(), corner.getY());
    }
    
    
    /**
     * <p>Sets the top left corner with the given corner (x, y).</p>
     *
     * <p>Fires property change: SET_TOP_LEFT_CORNER.</p>
     * 
     * @param x the x-coordinate of the top left corner
     * @param y the y-coordinate of the top left corner
     */
    public final void setTopLeftCorner(double x, double y) {
        if ((x != xCorner) || (y != yCorner)) {
            xCorner = x;
            yCorner = y;
            
            firePropertyChange(SET_TOP_LEFT_CORNER, null, null);
        }
    }
    
    
    /**
     * <p>Returns a copy of the top left corner.</p>
     *
     * @return a copy of the top left corner
     */
    public final Point2D getTopLeftCorner() {
        return new Point2D.Double(xCorner, yCorner);
    }
    
    
    /**
     * <p>Returns the image width.</p>
     *
     * <p>If the current image is <code>null</code>, this method returns 0.</p>
     *
     * @return the image width
     */
    public final int getImageWidth() {
        return width;
    }
    
    
    /**
     * <p>Returns the image height.</p>
     *
     * <p>If the current image is <code>null</code>, this method returns 0.</p>
     *
     * @return the image height
     */
    public final int getImageHeight() {
        return height;
    }
    
    
    /**
     * <p>Sets the image width and height based on the current image or
     * sets this data to (0, 0) if the current image is undefined.</p>
     *
     * <p>Fires property change: SET_IMAGE.</p>
     */
    private final void setImageSizeParameters() {
        if (image == null) {
            width = 0;
            height = 0;
        }
        else {
            JPanel panel = new JPanel();
            
            width  = image.getWidth (panel);
            height = image.getHeight(panel);
        }
        
        firePropertyChange(SET_IMAGE, null, null);
    }
    
}
