/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2005 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */

package org.gnu.gdk;

import java.io.File;
import java.io.FileNotFoundException;

import org.gnu.glib.Error;
import org.gnu.glib.GObject;
import org.gnu.glib.Handle;
import org.gnu.glib.JGException;
import org.gnu.glib.Type;

/**
 * 
 * This class describes an image in memory.
 * <p>
 * Image data in a pixbuf is stored in memory in uncompressed, packed format.
 * Rows in the image are stored top to bottom, and in each row pixels are stored
 * from left to right. There may be padding at the end of a row. The "rowstride"
 * value of a pixbuf, as returned by getRowstride(), indicates the number of
 * bytes between rows.
 */
public class Pixbuf extends GObject {

    /**
     * Constructs a Pixbuf from a handle to native resources. This should only
     * be used internally by java-gnome.
     */
    public Pixbuf(Handle handle) {
        super(handle);
    }

    /**
     * Construct a new Pixbuf from an image file.
     * 
     * @param filename
     *            The name of the image file.
     * @throws FileNotFoundException
     *             if filename doesn't exist.
     */
    public Pixbuf(String filename) throws FileNotFoundException, JGException {
        super(createWithFile(filename));
    }

    /**
     * Creates a new Pixbuf by loading an image from a file. The file format is
     * detected automatically. The image will be scaled to fit in the requested
     * size, preserving the image's aspect ration if reserveAspect is set to
     * true.
     * 
     * @param filename
     * @param width
     * @param height
     * @param preserveAspect
     * @throws FileNotFoundException
     * @throws JGException
     */
    public Pixbuf(String filename, int width, int height, boolean preserveAspect)
            throws FileNotFoundException, JGException {
        super(createWithFileSized(filename, width, height, preserveAspect));
    }

    /**
     * Create a new Pixbuf object and allocate a buffer to it.
     * 
     * @param colorSpace
     * @param hasAlpha
     * @param bitsPerSample
     * @param width
     * @param height
     */
    public Pixbuf(Colorspace colorSpace, boolean hasAlpha, int bitsPerSample,
            int width, int height) {
        super(gdk_pixbuf_new(colorSpace.getValue(), hasAlpha, bitsPerSample,
                width, height));
    }

    /**
     * Create a new Pixbuf by parsing XPM data in memory.
     * 
     * @param data
     */
    public Pixbuf(byte[] data) {
        super(gdk_pixbuf_new_from_xpm_data(data));
    }

    /**
     * Create a new Pixbuf from a flat representation that is suitable for
     * storing as inline data in a program. This is useful if you want to ship a
     * program with images but don't want to depend on any external files.
     * 
     * @param data
     * @param copyPixels
     * @throws JGException
     */
    public Pixbuf(byte[] data, boolean copyPixels) throws JGException {
        super(createInline(data, copyPixels));
    }

    /**
     * Create a new Pixbuf which represents a sub-region of another Pixbuf. The
     * new Pixbuf shares its pixels with the original so writing to one affects
     * both.
     * 
     * @param srcPixbuf
     * @param srcX
     * @param srcY
     * @param width
     * @param height
     */
    public Pixbuf(Pixbuf srcPixbuf, int srcX, int srcY, int width, int height) {
        super(gdk_pixbuf_new_subpixbuf(srcPixbuf.getHandle(), srcX, srcY,
                width, height));
    }

    /**
     * Transfers image data from a Drawable and converts it to an RGB(A)
     * representation inside this Pixbuf. In other words, copies image data from
     * a server-side drawable to a client-side RGB(A) buffer. This allows you to
     * efficiently read individual pixels on the client side.
     * 
     * @param drawable
     * @param colormap
     * @param srcX
     * @param srcY
     * @param destX
     * @param destY
     * @param width
     * @param height
     */
    public Pixbuf(Drawable drawable, Colormap colormap, int srcX, int srcY,
            int destX, int destY, int width, int height) {
        super(gdk_pixbuf_get_from_drawable(drawable.getHandle(), colormap
                .getHandle(), srcX, srcY, destX, destY, width, height));
    }

    /**
     * Transfers image data from an Image and converts it to an RGB(A)
     * representation inside this Pixbuf.
     * 
     * @param image
     * @param colormap
     * @param srcX
     * @param srcY
     * @param destX
     * @param destY
     * @param width
     * @param height
     */
    public Pixbuf(Image image, Colormap colormap, int srcX, int srcY,
            int destX, int destY, int width, int height) {
        super(gdk_pixbuf_get_from_drawable(image.getHandle(), colormap
                .getHandle(), srcX, srcY, destX, destY, width, height));
    }

    private static Handle createInline(byte[] data, boolean copyPixels)
            throws JGException {
        Handle error = getNullHandle();
        Handle hndl = gdk_pixbuf_new_from_inline(data.length, data, copyPixels,
                error);
        if (!error.isNull())
            throw new JGException(new Error(error));
        return hndl;

    }

    private static Handle createWithFile(String filename)
            throws FileNotFoundException, JGException {
        checkFileExists(filename);
        Handle error = getNullHandle();
        Handle hndl = gdk_pixbuf_new_from_file(filename, error);
        if (!error.isNull())
            throw new JGException(new Error(error));
        return hndl;

    }

    private static Handle createWithFileSized(String filename, int width,
            int height, boolean preserveAspect) throws FileNotFoundException,
            JGException {
        checkFileExists(filename);
        Handle error = getNullHandle();
        Handle hndl;
        if (preserveAspect)
            hndl = gdk_pixbuf_new_from_file_at_scale(filename, width, height,
                    preserveAspect, error);
        else
            hndl = gdk_pixbuf_new_from_file_at_size(filename, width, height,
                    error);
        if (!error.isNull())
            throw new JGException(new Error(error));
        return hndl;
    }

    private static void checkFileExists(String filename)
            throws FileNotFoundException {
        File aFile = new File(filename);
        if (!aFile.exists())
            throw new FileNotFoundException(filename);
    }

    public static PixbufFormat getFileInformation(String filename) {
        int[] w = new int[1];
        int[] h = new int[1];
        Handle hndl = gdk_pixbuf_get_file_info(filename, w, h);
        return PixbufFormat.getPixbufFormat(hndl);
    }

    /**
     * Return a copy of this Pixbuf.
     */
    public Pixbuf copy() {
        return getPixbufFromHandle(gdk_pixbuf_copy(getHandle()));
    }

    /**
     * Copies a rectangle area from this Pixbuf to the destination Pixbuf.
     * Conversion of Pixbuf formats is done automatically.
     * 
     * @param x
     * @param y
     * @param width
     * @param height
     * @param dest
     * @param destX
     * @param destY
     */
    public void copyArea(int x, int y, int width, int height, Pixbuf dest,
            int destX, int destY) {
        gdk_pixbuf_copy_area(getHandle(), x, y, width, height,
                dest.getHandle(), destX, destY);
    }

    /**
     * Return the width of the pixbuf.
     * 
     * @return The width
     */
    public int width() {
        return gdk_pixbuf_get_width(getHandle());
    }

    /**
     * Return the height of the pixbuf.
     * 
     * @return The height
     */
    public int height() {
        return gdk_pixbuf_get_height(getHandle());
    }

    /**
     * Create a new pixbuf from an existing pixbuf scaled to the size provided.
     */
    public Pixbuf scale(int width, int height, InterpType itype) {
        return getPixbufFromHandle(gdk_pixbuf_scale_simple(getHandle(), width,
                height, itype.getValue()));
    }

    /**
     * Creates a transformation of this Pixbuf by scaling scaleX and scaleY then
     * translating by offsetX and offsetY, then renders the rectangle of the
     * resulting image onto the destination image replacing the previous
     * contents.
     * 
     * @param dest
     * @param destX
     * @param destY
     * @param destWidth
     * @param destHeight
     * @param offsetX
     * @param offsetY
     * @param scaleX
     * @param scaleY
     * @param type
     */
    public Pixbuf scale(Pixbuf dest, int destX, int destY, int destWidth,
            int destHeight, double offsetX, double offsetY, double scaleX,
            double scaleY, InterpType type) {
        gdk_pixbuf_scale(getHandle(), dest.getHandle(), destX, destY,
                destWidth, destHeight, offsetX, offsetY, scaleX, scaleY, type
                        .getValue());
        return dest;
    }

    /**
     * Creates a new Pixbuf by scaling this Pixbuf to the provided width and
     * height and compositing the results with a checkboard of colors color1 and
     * color2.
     * 
     * @param width
     * @param height
     * @param type
     * @param overallAlpha
     * @param checkSize
     * @param color1
     * @param color2
     */
    public Pixbuf composite(int width, int height, InterpType type,
            int overallAlpha, int checkSize, int color1, int color2) {
        return getPixbufFromHandle(gdk_pixbuf_composite_color_simple(
                getHandle(), width, height, type.getValue(), overallAlpha,
                checkSize, color1, color2));
    }

    /**
     * Creates a transformation of this image by scaling scaleX and scaleY then
     * translating by offsetX and offsetY. This gives an image in the
     * coordinates of the destination pixbuf. The rectangle (destX, destY,
     * destWidth, destHeight) is then composited onto the corresponding
     * rectangle of the original destination image.
     * 
     * @param dest
     * @param destX
     * @param destY
     * @param destWidth
     * @param destHeight
     * @param offsetX
     * @param offsetY
     * @param scaleX
     * @param scaleY
     * @param type
     * @param overallAlpha
     */
    public Pixbuf composite(Pixbuf dest, int destX, int destY, int destWidth,
            int destHeight, double offsetX, double offsetY, double scaleX,
            double scaleY, InterpType type, int overallAlpha) {
        gdk_pixbuf_composite(getHandle(), dest.getHandle(), destX, destY,
                destWidth, destHeight, offsetX, offsetY, scaleX, scaleY, type
                        .getValue(), overallAlpha);
        return dest;
    }

    /**
     * Creates a transformation of this image by scaling scaleX and scaleY then
     * translating by offsetX and offsetY then composites the rectangle (destX,
     * destY, destWidth, destHeight) of the resulting image with a checkboard of
     * the colors color1 and color2 and renders it onto the destination image.
     * 
     * @param dest
     * @param destX
     * @param destY
     * @param destWidth
     * @param destHeight
     * @param offsetX
     * @param offsetY
     * @param scaleX
     * @param scaleY
     * @param type
     * @param overallAlpha
     * @param checkX
     * @param checkY
     * @param checkSize
     * @param color1
     * @param color2
     */
    public Pixbuf composite(Pixbuf dest, int destX, int destY, int destWidth,
            int destHeight, double offsetX, double offsetY, double scaleX,
            double scaleY, InterpType type, int overallAlpha, int checkX,
            int checkY, int checkSize, int color1, int color2) {
        gdk_pixbuf_composite_color(getHandle(), dest.getHandle(), destX, destY,
                destWidth, destHeight, offsetX, offsetY, scaleX, scaleY, type
                        .getValue(), overallAlpha, checkX, checkY, checkSize,
                color1, color2);
        return dest;
    }

    /**
     * Rotate this pixbuf and return the results as a new Pixbuf.
     * 
     * @param direction
     */
    public Pixbuf rotate(PixbufRotation direction) {
        return getPixbufFromHandle(gdk_pixbuf_rotate_simple(getHandle(),
                direction.getValue()));
    }

    /**
     * Flips a Pixbuf horizontally or vertically and returns the result in a new
     * Pixbuf.
     * 
     * @param horizontal
     */
    public Pixbuf flip(boolean horizontal) {
        return getPixbufFromHandle(gdk_pixbuf_flip(getHandle(), horizontal));
    }

    /**
     * Returns the Colorspace for the Pixbuf.
     */
    public Colorspace getColorspace() {
        return Colorspace.intern(gdk_pixbuf_get_colorspace(getHandle()));
    }

    /**
     * Returns the number of channels for the Pixbuf.
     */
    public int getNumChannels() {
        return gdk_pixbuf_get_n_channels(getHandle());
    }

    public boolean hasAlpha() {
        return gdk_pixbuf_get_has_alpha(getHandle());
    }

    public int getBitsPerSample() {
        return gdk_pixbuf_get_bits_per_sample(getHandle());
    }

    /**
     * Returns the pixel data for the Pixbuf
     */
    public byte[] getPixels() {
        return gdk_pixbuf_get_pixels(getHandle());
    }

    /**
     * Returns the rowstride which is the number of bytes between the start of a
     * row and the start of the next row.
     */
    public int getRowstride() {
        return gdk_pixbuf_get_rowstride(getHandle());
    }

    public String getOption(String key) {
        return gdk_pixbuf_get_option(getHandle(), key);
    }

    /**
     * Saves a Pixbuf to a file in the format type provided. By default the only
     * supported formats are "jpeg", "png", or "ico".
     * 
     * @param filename
     * @param type
     * @param optionKeys
     * @param optionValues
     * @throws JGException
     */
    public void save(String filename, String type, String[] optionKeys,
            String[] optionValues) throws JGException {
        Handle error = getNullHandle();
        boolean val = gdk_pixbuf_savev(getHandle(), filename, type, optionKeys,
                optionValues, error);
        if (!val)
            throw new JGException(new Error(error));
    }

    /**
     * Saves a Pixbuf to a buffer in the format type provided. The only
     * supported formats are "jpeg", "png", or "ico". The possible errors
     * contained in the JGException are those described in PixbufError.
     * 
     * @param type
     * @param optionKeys
     * @param optionValues
     * @throws JGException
     */
    public byte[] saveToBuffer(String type, String[] optionKeys,
            String[] optionValues) throws JGException {
        Handle error = getNullHandle();
        byte[] val = gdk_pixbuf_save_to_bufferv(getHandle(), type, optionKeys,
                optionValues, error);
        if (error.isNull() == false)
            throw new JGException(new Error(error));
        return val;
    }

    /**
     * Adds alpha channel to this Pixbuf and returns the results. If this Pixbuf
     * already has an alpha channel, the channel values are copied into the
     * newly created Pixbuf; otherwise the alpha channel is initialized to 255
     * (full opacity).
     * <p>
     * If <i>substituteColor</i> is true the color specified by (red, green,
     * blue) will be assigned zero opacity. That is, if you pass (255, 255, 255)
     * for the substitute color all white pixels will become fully transparent.
     * 
     * @param substituteColor
     * @param red
     * @param green
     * @param blue
     */
    public Pixbuf addAlpha(boolean substituteColor, int red, int green, int blue) {
        return getPixbufFromHandle(gdk_pixbuf_add_alpha(getHandle(),
                substituteColor, red, green, blue));
    }

    /**
     * Clears the Pixbuf to a given RGBA value, converting the RGBA value into
     * the Pixbuf's pixel format. The alpha will be ignored if the Pixbuf
     * doesn't have an alpha channel.
     * 
     * @param pixel
     */
    public void fill(int pixel) {
        gdk_pixbuf_fill(getHandle(), pixel);
    }

    /**
     * Modifies saturation and optionally pixelates this Pixbuf placing the
     * result in the destination Pixbuf. The Pixbufs may be the same Pixbuf with
     * no ill effects. If saturation is 1.0 then saturation is not changed. If
     * it's less than 1.0, saturation is reduced (the image is darkened); if
     * greater than 1.0, saturation is increased (the image is brightened). If
     * pixelate is true, then pixels are faded in a checkerboard pattern to
     * create a pixelated image. The Pixbufs must have the same image format,
     * size, and rowstride.
     * 
     * @param dest
     * @param saturation
     * @param pixelate
     */
    public Pixbuf saturateAndPixelate(Pixbuf dest, double saturation,
            boolean pixelate) {
        gdk_pixbuf_saturate_and_pixelate(getHandle(), dest.getHandle(),
                saturation, pixelate);
        return dest;
    }
    
    /**
     * Retrieve the runtime type used by the GLib library.
     */
    public static Type getType() {
        return new Type(gdk_pixbuf_get_type());
    }

    /**
     * Constructs a Pixbuf from a handle to native resources. This should only
     * be used internally by Java-Gnome. This should be used in preference to
     * {@link #Pixbuf(Handle)} unless the call is being made by a subclass of
     * Pixbuf from its own Handle constructor.
     */
    public static Pixbuf getPixbufFromHandle(Handle hndl) {
        if (hndl != null) {
            GObject obj = GObject.getGObjectFromHandle(hndl);
            return (obj != null) ? (Pixbuf) obj : new Pixbuf(hndl);
        }
        return null;
    }

    // native static final protected void gdk_pixbuf_render_threshold_alpha(
    // Handle pixbuf, Handle bitmap, int srcX, int srcY, int destX,
    // int destY, int width, int height, int alphaThreshold);
    // native static final protected void gdk_pixbuf_render_to_drawable(
    // Handle pixbuf, Handle drawable, Handle gc, int srcX, int srcY, int destX,
    // int destY, int width, int height, int dither, int xDither,
    // int yDither);
    // native static final protected void gdk_pixbuf_render_to_drawable_alpha(
    // Handle pixbuf, Handle drawable, int srcX, int srcY, int destX, int destY,
    // int width, int height, int alphaMode, int alphaThreshold,
    // int dither, int xDither, int yDither);
    // native static final protected void
    // gdk_pixbuf_render_pixmap_and_mask_for_colormap(
    // Handle pixbuf, Handle colormap, Handle pixmapReturn, Handle maskReturn,
    // int alphaThreshold);
    // native static final protected void gdk_pixbuf_render_pixmap_and_mask(
    // Handle pixbuf, Handle pixmapReturn, Handle maskReturn, int
    // alphaThreshold);
    native static final protected Handle gdk_pixbuf_get_from_drawable(
            Handle src, Handle cmap, int srcX, int srcY, int destX, int destY,
            int width, int height);

    native static final protected Handle gdk_pixbuf_get_from_image(Handle src,
            Handle cmap, int srcX, int srcY, int destX, int destY, int width,
            int height);

    native static final protected int gdk_pixbuf_get_colorspace(Handle pixbuf);

    native static final protected int gdk_pixbuf_get_n_channels(Handle pixbuf);

    native static final protected boolean gdk_pixbuf_get_has_alpha(Handle pixbuf);

    native static final protected int gdk_pixbuf_get_bits_per_sample(
            Handle pixbuf);

    native static final protected byte[] gdk_pixbuf_get_pixels(Handle pixbuf);

    native static final protected int gdk_pixbuf_get_width(Handle pixbuf);

    native static final protected int gdk_pixbuf_get_height(Handle pixbuf);

    native static final protected int gdk_pixbuf_get_rowstride(Handle pixbuf);

    native static final protected Handle gdk_pixbuf_new(int colorspace,
            boolean hasAlpha, int bitsPerSample, int width, int height);

    native static final protected Handle gdk_pixbuf_copy(Handle pixbuf);

    native static final protected Handle gdk_pixbuf_new_subpixbuf(
            Handle srcPixbuf, int srcX, int srcY, int width, int height);

    native static final protected Handle gdk_pixbuf_new_from_file(
            String filename, Handle error);

    native static final protected Handle gdk_pixbuf_new_from_file_at_size(
            String filename, int width, int height, Handle error);

    native static final protected Handle gdk_pixbuf_new_from_file_at_scale(
            String filename, int width, int height, boolean preserveAspect,
            Handle error);

    native static final protected Handle gdk_pixbuf_get_file_info(
            String filename, int[] width, int[] height);

    native static final protected Handle gdk_pixbuf_new_from_xpm_data(
            byte[] data);

    native static final protected Handle gdk_pixbuf_new_from_inline(
            int dataLength, byte[] data, boolean copyPixels, Handle error);

    native static final protected boolean gdk_pixbuf_savev(Handle pixbuf,
            String filename, String type, String[] optionKeys,
            String[] optionValues, Handle error);

    native static final protected byte[] gdk_pixbuf_save_to_bufferv(
            Handle pixbuf, String type, String[] optionKeys,
            String[] optionValues, Handle error);

    native static final protected Handle gdk_pixbuf_rotate_simple(
            Handle pixbuf, int angle);

    native static final protected Handle gdk_pixbuf_flip(Handle pixbuf,
            boolean horizontal);

    native static final protected void gdk_pixbuf_fill(Handle pixbuf, int pixel);

    native static final protected Handle gdk_pixbuf_add_alpha(Handle pixbuf,
            boolean substituteColor, int r, int g, int b);

    native static final protected void gdk_pixbuf_copy_area(Handle srcPixbuf,
            int srcX, int srcY, int width, int height, Handle destPixbuf,
            int destX, int destY);

    native static final protected void gdk_pixbuf_saturate_and_pixelate(
            Handle src, Handle dest, double saturation, boolean pixelate);

    native static final protected void gdk_pixbuf_scale(Handle src,
            Handle dest, int destX, int destY, int destWidth, int destHeight,
            double offsetX, double offsetY, double scaleX, double scaleY,
            int interpType);

    native static final protected void gdk_pixbuf_composite(Handle src,
            Handle dest, int destX, int destY, int destWidth, int destHeight,
            double offsetX, double offsetY, double scaleX, double scaleY,
            int interpType, int overallAlpha);

    native static final protected void gdk_pixbuf_composite_color(Handle src,
            Handle dest, int destX, int destY, int destWidth, int destHeight,
            double offsetX, double offsetY, double scaleX, double scaleY,
            int interpType, int overallAlpha, int checkX, int checkY,
            int checkSize, int color1, int color2);

    native static final protected Handle gdk_pixbuf_scale_simple(Handle src,
            int destWidth, int destHeight, int interpType);

    native static final protected Handle gdk_pixbuf_composite_color_simple(
            Handle src, int destWidth, int destHeight, int interpType,
            int overallAlpha, int checkSize, int color1, int color2);

    native static final protected String gdk_pixbuf_get_option(Handle pixbuf,
            String key);
    
    native static final private int gdk_pixbuf_get_type();

}
