package org.freehep.graphicsbase.util.images;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Hashtable;

import javax.swing.Icon;
import javax.swing.ImageIcon;

/**
 * A convenience class for handling images.
 * Maintains a cache of images suitable for small icons which may be reused
 * often.
 *
 * @author Tony Johnson
 * @author Cal Loomis
 * @version $Id: ImageHandler.java 8584 2006-08-10 23:06:37Z duns $
 */

public class ImageHandler
{
    public static final Image brokenImage;
    public static final Cursor brokenCursor;
    public static final Icon brokenIcon;
    private static String[] imageExtensions = { ".png", ".gif", ".jpeg", ".jpg" };
    private static Hashtable<URL, Image> imageCache = new Hashtable<URL, Image>();
    private static Hashtable<URL, Cursor> cursorCache = new Hashtable<URL, Cursor>();
    private static Hashtable<URL, Icon> iconCache = new Hashtable<URL, Icon>();

    // only static methods
    protected ImageHandler()
    {
    }

    static
    {
        Toolkit toolkit = Toolkit.getDefaultToolkit();

        brokenImage = toolkit.getImage(ImageHandler.class.getResource("BrokenImage.gif"));
        if (brokenImage == null)
        {
            throw new RuntimeException("Could not load BrokenIcon .. this looks bad!");
        }
        brokenIcon = new ImageIcon(brokenImage,"BrokenIcon");

        Image brokenCursorImage = toolkit.getImage(ImageHandler.class.getResource("BrokenCursor.gif"));
        if (GraphicsEnvironment.isHeadless()) {
            // if run with headless option no custom cursors are available
            brokenCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
        } else if (brokenCursorImage != null) {
           brokenCursor = toolkit.createCustomCursor(brokenCursorImage, new Point(0,0), "BrokenCursor");
        } else {
           brokenCursor = null;
        }

        if (brokenCursor == null)
        {
            throw new RuntimeException("Could not load BrokenCursor .. this looks bad!");
        }
    }

    /**
     * Load an image from a URL. Images are cached for efficiency. If the image cannot be found
     * will return a brokenImage. No exceptions are thrown.
     * @param url The url of the image to load.
     * @return The image
     */
    public static Image getImage(URL url)
    {
        Image image = null;
        if (url!=null) {
            image = imageCache.get(url);
        } else {
            image = brokenImage;
        }

        if (image==null && url!=null)
        {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            // Note we dont use toolkit.createImage(url) since it returns an image
            // even if the specified URL does not exist
            try
            {
                InputStream in = url.openStream();
                try
                {
                    int length = in.available();
                    if (length == 0) length = 1000;
                    byte[] bytes = new byte[length];
                    for (int offset=0;;)
                    {
                        int rc = in.read(bytes,offset,length-offset);
                        if (rc<0) break;
                        if (rc>0) offset += rc;
                        else
                        {
                            length += 1000;
                            byte[] newBytes = new byte[length];
                            System.arraycopy(bytes,0,newBytes,0,offset);
                            bytes = newBytes;
                        }
                    }
                    image = toolkit.createImage(bytes);
                }
                finally
                {
                    in.close();
                }
            }
            catch (IOException x)
            {
                image = brokenImage;
            }

            // Note we SHOULD include brokenImage in the cache, otherwise repeated requests for
            // the same non-existant url will be very slow
            if (url!=null) imageCache.put(url, image);
        }
        return image;
    }

    /**
     * Create the best cursor by  reading from a URL, with hotspot (0,0).
     */
    public static Cursor getBestCursor(String name, Class<?> clazz, int width, int height) {
        return getBestCursor(name, clazz, width, height, 0, 0);
    }
    
    /**
     * Create the best cursor by  reading from a URL, with hotspot (x, y).
     * The best width and height are inserted into the URL in the spots with %width
     * and %height. Example:
     *
     * getBestCursor("/org/freehep/graphicsbase/swing/images/MyIcon%wx%h.png", 32 32);
     *
     * @param name URL of the cursor
     * @param clazz Class to be used for getting resource
     * @param width suggested width
     * @param height suggested height
     * @param x hotspot
     * @param y hotspot
     * @return The cursor, the default cursor if custom cursors are not supported
     *         or brokenCursor if the cursor cannot be found
     */
    public static Cursor getBestCursor(String name, Class<?> clazz, int width, int height, int x, int y) {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension dimension = toolkit.getBestCursorSize(width, height);
        if ((dimension.width == 0) && (dimension.height == 0)) return Cursor.getDefaultCursor();
        name = name.replaceAll("%w", Integer.toString(dimension.width));
        name = name.replaceAll("%h", Integer.toString(dimension.height));
        return getCursor(name, clazz, x, y);
    }

    /**
     * Create a cursor by reading from a URL, with hotspot (0,0).
     * @param url The URL to read
     * @return The cursor, or brokenCursor if the cursor cannot be found
     */
    public static Cursor getCursor(URL url) {
        return getCursor(url, 0, 0);
    }

    /**
     * Create a cursor by reading from a URL
     * @param url The URL to read
     * @param x x position of hotspot
     * @param y y position of hotspot
     * @return The cursor, or brokenCursor if the cursor cannot be found
     */
    public static Cursor getCursor(URL url, int x, int y)
    {
        Cursor cursor = (url!=null) ? cursorCache.get(url) : null;
        if (cursor == null)
        {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Image image = getImage(url);
            if (image == brokenImage) cursor = brokenCursor;
            else cursor = toolkit.createCustomCursor(image, new Point(x, y), url.getFile());
            if (url != null) cursorCache.put(url, cursor);
        }
        return cursor;
    }

    /**
     * Create an Icon by reading from a URL
     * @param url The URL to read
     * @return The icon, or brokenIcon if it cannot be loaded
     */
    public static Icon getIcon(URL url)
    {
        Icon icon = (url!=null) ? iconCache.get(url) : null;
        if (icon == null)
        {
            Image image = getImage(url);
            if (image == brokenImage) icon = brokenIcon;
            else icon = new ImageIcon(image,url.getFile());
            if (url != null) iconCache.put(url, icon);
        }
        return icon;
    }
    /**
     * Convenience routine, equivalent to getImage(clazz.getResource(name));
     * As an additional convenience, if the name does not contain an extension (eg .gif)
     * then a list of common extensions will be tried.
     * @param name The relative address of the image to load
     * @param clazz The class from which the address is based
     * @return The image, or brokenImage if no image is found.
     */
    public static Image getImage(String name, Class<?> clazz)
    {
        if (name.indexOf('.') >= 0) return getImage(clazz.getResource(name));
        for (int i=0; i<imageExtensions.length; i++)
        {
            URL url = clazz.getResource(name+imageExtensions[i]);
            if (url != null) return getImage(url);
        }
        return brokenImage;
    }
    
    /**
     * Convenience routine, equivalent to getCursor(clazz.getResource(name));
     * As an additional convenience, if the name does not contain an extension (eg .gif)
     * then a list of common extensions will be tried.
     * @param name The relative address of the cursor to load
     * @param clazz The class from which the address is based
     * @return The cursor, or brokenCursor if no cursor is found.
     */
    public static Cursor getCursor(String name, Class<?> clazz) {
        return getCursor(name, clazz, 0, 0);
    }
    
    /**
     * Convenience routine, equivalent to getCursor(clazz.getResource(name));
     * As an additional convenience, if the name does not contain an extension (eg .gif)
     * then a list of common extensions will be tried.
     * @param name The relative address of the cursor to load
     * @param clazz The class from which the address is based
     * @param x hotspot
     * @param y hotspot
     * @return The cursor, or brokenCursor if no cursor is found.
     */
    public static Cursor getCursor(String name, Class<?> clazz, int x, int y)
    {
        if (name.indexOf('.') >= 0) return getCursor(clazz.getResource(name), x, y);

        for (int i=0; i<imageExtensions.length; i++)
        {
            URL url = clazz.getResource(name+imageExtensions[i]);
            if (url != null) return getCursor(url, x, y);
        }
        return brokenCursor;
    }

    /**
     * Convenience routine, equivalent to getIcon(clazz.getResource(name));
     * As an additional convenience, if the name does not contain an extension (eg .gif)
     * then a list of common extensions will be tried.
     * @param name The relative address of the icon to load
     * @param clazz The class from which the address is based
     * @return The icon, or brokenIcon if no icon is found.
     */
    public static Icon getIcon(String name, Class<?> clazz)
    {
        if (name.indexOf('.') >= 0) return getIcon(clazz.getResource(name));

        for (int i=0; i<imageExtensions.length; i++)
        {
            URL url = clazz.getResource(name+imageExtensions[i]);
            if (url != null) return getIcon(url);
        }
        return brokenIcon;
    }
}
