/*
 * @(#)ImagePaintableLite.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>ImagePaintableLite</code> creates a <code>Paintable</code> using
 * an image filename or an image URL.</p>
 *
 * <p>To save memory, an <code>ImagePaintableLite</code> does not cache its image
 * but rather retrieves its image as needed for paint operations.  Thus the class
 * trades time for space.  This design enables a program to access a much larger
 * collection of images at one time than would be possible by directly using the
 * Java classes <code>Image</code> or <code>ImageIcon</code> or the JPT class
 * <code>ImagePaintable</code>.  These other classes all retain the bit data for
 * an image and thus entail a high memory cost.</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.3
 * @see     ImagePaintable
 */
public class ImagePaintableLite extends AbstractPaintable
{
    /** Bound property name for set image. */
    public static final String SET_IMAGE =
        ImagePaintable.SET_IMAGE;
        
    /** Bound property name for set top left point. */
    public static final String SET_TOP_LEFT_CORNER =
        ImagePaintable.SET_TOP_LEFT_CORNER;
        
    
    /** The image file name. */
    private String imageFileName = null;
    
    /** The image URL. */
    private URL imageURL = 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>ImagePaintableLite(String)</li>
     *   <li>ImagePaintableLite(String, Point2D)</li>
     *   <li>ImagePaintableLite(String, double, double)</li>
     *   <li>ImagePaintableLite(URL)</li>
     *   <li>ImagePaintableLite(URL, Point2D)</li>
     *   <li>ImagePaintableLite(URL, double, double)</li>
     * </code></ul>
     */
    public ImagePaintableLite() { }
    
    
    /**
     * <p>The constructor to specify the image by an image file name.</p>
     *
     * @param filename the filename for the file with the image
     * @see #ImagePaintableLite()
     */
    public ImagePaintableLite(String filename) {
        setImageByFileName(filename);
    }
    
    
    /**
     * <p>The constructor to specify the image by an image 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 #ImagePaintableLite()
     */
    public ImagePaintableLite(String filename, Point2D corner) {
        setImageByFileName(filename);
        setTopLeftCorner(corner);
    }
    
    
    /**
     * <p>The constructor to specify the image by an image 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 #ImagePaintableLite()
     */
    public ImagePaintableLite(String filename, double x, double y) {
        setImageByFileName(filename);
        setTopLeftCorner(x, y);
    }
    
    
    /**
     * <p>The constructor to specify the image by an image URL.</p>
     *
     * @param url the URL location for the image
     * @see #ImagePaintableLite()
     */
    public ImagePaintableLite(URL url) {
        setImageByURL(url);
    }
    
    
    /**
     * <p>The constructor to specify the image by an image URL
     * and the top left corner.</p>
     *
     * @param url the URL location for the image
     * @param corner the top left corner
     * @see #ImagePaintableLite()
     */
    public ImagePaintableLite(URL url, Point2D corner) {
        setImageByURL(url);
        setTopLeftCorner(corner);
    }
    
    
    /**
     * <p>The constructor to specify the image by an image URL
     * and the top left corner (x, y).</p>
     *
     * @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 #ImagePaintableLite()
     */
    public ImagePaintableLite(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>
     *
     * <p>If the image cannot be loaded, this method will not paint.</p>
     *
     * @param g the graphics context on which to paint
     */
    public final void originalPaint(Graphics g) {
        if ((g == null) || !isVisible())
            return;
        
        Image image = fetchImage();
        
        if (image == null)
            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 ((width == 0) || (height == 0))
            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 by file name.</p>
     *
     * <p>Fires property change: SET_IMAGE.</p>
     * 
     * @param filename the filename for the file with the image
     */
    public final void setImageByFileName(String filename) {
        imageURL = null;
        imageFileName = filename;
        
        setImageSizeParameters();
    }
    
    
    /**
     * <p>Sets the image by URL.</p>
     *
     * <p>Fires property change: SET_IMAGE.</p>
     * 
     * @param url the URL location for the image
     */
    public final void setImageByURL(URL url) {
        imageFileName = null;
        imageURL = url;
        
        setImageSizeParameters();
    }
    
    
    /**
     * <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 undefined, 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 undefined, this method returns 0.</p>
     *
     * @return the image height
     */
    public final int getImageHeight() {
        return height;
    }
    
    
    /**
     * <p>Returns the current image icon associated with a file name or URL
     * or returns <code>null</code> if the current image is undefined.</p>
     *
     * @return the current image icon
     */
    private final ImageIcon fetchImageIcon() {
        if (imageFileName != null)
            return new ImageIcon(imageFileName);
        
        if (imageURL != null)
            return new ImageIcon(imageURL);
        
        return null;
    }
    
    
    /**
     * <p>Returns the current image associated with a file name or URL
     * or returns <code>null</code> if the current image is undefined.</p>
     *
     * @return the current image
     */
    private final Image fetchImage() {
        ImageIcon imageicon = fetchImageIcon();
        
        if (imageicon == null)
            return null;
        
        return imageicon.getImage();
    }
    
    
    /**
     * <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() {
        Image image = fetchImage();
        
        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);
    }
    
}
