/* 
 * JGimp - A Java extension for the GIMP
 * Copyright (C) 2002  Georgia Tech Research Corp.
 * Written by Michael Terry, mterry@cc.gatech.edu
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package org.gimp.jgimp;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;

import org.gimp.jgimp.nativewrappers.JGimpInt32;
import org.gimp.jgimp.proxy.JGimpProxy;
import org.gimp.jgimp.proxy.JGimpProxyException;

/**
 * A Java-friendly wrapper for GIMP drawables (objects that
 * contain pixel data of some sort or another). This class
 * is the base class for two other classes, {@link JGimpChannel}
 * and {@link JGimpLayer}, representing GIMP channels and layers,
 * respectively. 
 */
public class JGimpDrawable extends JGimpData {

    private GimpApp m_App = null;
    private int m_ID = -1;

    JGimpDrawable(GimpApp app, int ID) {
        m_App = app;
        m_ID = ID;
    }
    JGimpDrawable(JGimpProxy proxy, int ID) {
        m_App = new GimpApp(proxy);
        m_ID = ID;
    }
    
    /**
     * @see org.gimp.jgimp.JGimpData#getGimpPDBArgType()
     */
    public int getGimpPDBArgType() {
        return JGimpPDBArgTypeConstants.PDB_DRAWABLE;
    }

    public String toString() {
        return "JGimpDrawable, value: " + m_ID;
    }

    public boolean equals(Object inRight) {
        if (inRight == null) {
            return false;
        }
        if (inRight instanceof JGimpDrawable) {
            return (((JGimpDrawable) inRight).getID() == this.getID());
        }
        return false;
    }

    /**
     * Returns the GIMP ID of this drawable.
     */
    public int convertToInt() throws JGimpInvalidDataCoercionException {
        return m_ID;
    }

    /**
     * Returns the GIMP ID of this drawable.
     */
    public int getID() {
        return m_ID;
    }

    /**
     * Retrieves the dimensions of this drawable.
     * @return The size of this drawable.
     * @throws JGimpException
     * @pdb gimp-drawable-width
     * @pdb gimp-drawable-height
     */
    public Dimension getSize() throws JGimpException {
        return new Dimension(this.getWidth(), this.getHeight());
    }
    
    /**
     * Returns the width of this drawable.
     * @return The drawable's width.
     * @throws JGimpException
     * @pdb gimp-drawable-width
     */
    public int getWidth() throws JGimpException {
        return m_App.callProcedure("gimp_drawable_width", this)[0].convertToInt();
    }
    
    /**
     * Returns the height of this drawable.
     * @return The drawable's height.
     * @throws JGimpException
     * @pdb gimp-drawable-height
     */
    public int getHeight() throws JGimpException {
        return m_App.callProcedure("gimp_drawable_height", this)[0].convertToInt();
    }

    /**
     * Returns the number of bytes used to represent a single pixel.
     * @return Number of bytes per pixel.
     * @throws JGimpException
     * @pdb gimp-drawable-bytes
     */
    public int getBytesPerPixel() throws JGimpException {
        return m_App.callProcedure("gimp_drawable_bytes", this)[0].convertToInt();
    }

    /**
     * Erases the drawable so that it fills with the foreground color, ignoring
     * any selection.
     * @throws JGimpException
     * @pdb gimp-drawable-fill
     */
    public void eraseToForegroundColor() throws JGimpException {
        m_App.callProcedure("gimp_drawable_fill", this, new JGimpInt32(0));
    }
    
    /**
     * Erases the drawable so that it fills with the background color, ignoring
     * any selection.
     * @throws JGimpException
     * @pdb gimp-drawable-fill
     */
    public void eraseToBackgroundColor() throws JGimpException {
        m_App.callProcedure("gimp_drawable_fill", this, new JGimpInt32(1));
    }
    
    /**
     * Erases the drawable so that it fills with white, ignoring
     * any selection.
     * @throws JGimpException
     * @pdb gimp-drawable-fill
     */
    public void eraseToWhite() throws JGimpException {
        m_App.callProcedure("gimp_drawable_fill", this, new JGimpInt32(2));
    }
    /**
     * Erases the drawable so that it is completely transparent, ignoring
     * any selection.
     * @throws JGimpException
     * @pdb gimp-drawable-fill
     */
    public void eraseToFullTransparency() throws JGimpException {
        m_App.callProcedure("gimp_drawable_fill", this, new JGimpInt32(3));
    }
    /**
     * Retrieves an array of bytes representing the pixel data, with each
     * element corresponding to a color channel.
     * @param x The x-coordinate of the pixel.
     * @param y The y-coordinate of the pixel.
     * @return  The pixel data.
     * @throws JGimpException
     * @pdb gimp-drawable-get-pixel
     */
    public byte[] getPixel(int x, int y) throws JGimpException {
        return m_App.callProcedure("gimp_drawable_get_pixel", this, new JGimpInt32(x), new JGimpInt32(y))[0].convertToByteArray();
    }
    /**
     * Sets the pixel at the given location.
     * @param x The x-coordinate of the pixel.
     * @param y The y-coordinate of the pixel.
     * @param pixel The pixel data. For drawables with multiple channels, each byte should correspond to a channel.
     * @throws JGimpException
     * @pdb gimp-drawable-set-pixel
     */
    public void setPixel(int x, int y, byte[] pixel) throws JGimpException {
        m_App.callProcedure("gimp_drawable_set_pixel", this, new JGimpInt32(x), new JGimpInt32(y), pixel);
    }
    
    /**
     * Returns true if drawable has an alpha channel.
     * @return True if drawable has alpha channel.
     * @throws JGimpException
     * @pdb gimp-drawable-has-alpha
     */
    public boolean hasAlpha() throws JGimpException {
        return m_App.callProcedure("gimp_drawable_has_alpha", this)[0].convertToBoolean();
    }

    /**
     * Returns true if this drawable's pixel data type is RGB .
     * @return True if drawable contains RGB data.
     * @throws JGimpException
     * @pdb gimp-drawable-is-rgb
     */
    public boolean isRGB() throws JGimpException {
        return m_App.callProcedure("gimp_drawable_is_rgb", this)[0].convertToBoolean();
    }

    /**
     * Returns true if this drawable's pixel data type is indexed data .
     * @return True if drawable contains indexed data.
     * @throws JGimpException
     * @pdb gimp-drawable-is-indexed
     */
    public boolean isIndexed() throws JGimpException {
        return m_App.callProcedure("gimp_drawable_is_indexed", this)[0].convertToBoolean();
    }

    /**
     * Returns true if this drawable's pixel data type is grayscale.
     * @return True if drawable contains grayscale data.
     * @throws JGimpException
     * @pdb gimp-drawable-is-gray
     */
    public boolean isGrayscale() throws JGimpException {
        return m_App.callProcedure("gimp_drawable_is_gray", this)[0].convertToBoolean();
    }

    /**
     * Returns the GIMP's integer code representing the pixel data type for
     * this drawable.
     * @throws JGimpException
     * @pdb gimp-drawable-type
     */
    public int getType() throws JGimpException {
        return m_App.callProcedure("gimp_drawable_type", this)[0].convertToInt();
    }

    /**
     * Returns the bounds of the current selection, null if no selection
     * is present.
     * @return The selection bounds if there is a selection, null if no selection exists.
     * @pdb gimp-drawable-mask-bounds
     */
    public Rectangle getSelectionBounds() throws JGimpException {
        JGimpData[] results = m_App.callProcedure("gimp_drawable_mask_bounds", this);
        if (!results[0].convertToBoolean()) {
            return null;
        }
        return new Rectangle(results[1].convertToInt(), results[2].convertToInt(), results[3].convertToInt() - results[1].convertToInt(), results[4].convertToInt() - results[2].convertToInt());
    }

    /**
     * Combines the image's shadow buffer with the drawable.
     * @param addToUndoStack  Whether to add this operation to the undo stack or not.
     * @throws JGimpException
     */
    public void mergeShadow(boolean addToUndoStack) throws JGimpException {
        m_App.callProcedure("gimp_drawable_merge_shadow", this, new Boolean(addToUndoStack));
    }

    /**
     * Returns the offset of this drawable relative to the image.
     * @throws JGimpException
     * @pdb gimp-drawable-offsets
     */
    public Point getOffset() throws JGimpException {
        JGimpData[] results = m_App.callProcedure("gimp_drawable_offsets", this);
        return new Point(results[0].convertToInt(), results[1].convertToInt());
    }

    /**
     * Forces an update on the drawable within the specified region.
     * @param x The x value of the area to update.
     * @param y The y value of the area to update.
     * @param width The width of the area to update.
     * @param height The height of the area to update.
     * @throws JGimpException
     * @pdb gimp-drawable-update
     */
    public void update(int x, int y, int width, int height) throws JGimpException {
        // TODO: For documentation purposes, not sure if this forces an update on the GIMP window or just the pixel data
        m_App.callProcedure("gimp_drawable_update", this, new JGimpInt32(x), new JGimpInt32(y), new JGimpInt32(width), new JGimpInt32(height));
    }
    
    /**
     * @see JGimpProxy#readPixelRegionInNativeByteFormat(JGimpDrawable, int, int, int, int, byte[], int)
     */
    public void readPixelRegionInNativeByteFormat(int x, int y, int width, int height, byte[] buffer, int offset)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException, JGimpBufferOverflowException, JGimpException {
        m_App.getProxy().readPixelRegionInNativeByteFormat(this, x, y, width, height, buffer, offset);
    }

    /**
     * @see JGimpProxy#readPixelRegionInNativeIntFormat(JGimpDrawable, int, int, int, int, int[], int)
     */
    public void readPixelRegionInNativeIntFormat(int x, int y, int width, int height, int[] buffer, int offset)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException, JGimpBufferOverflowException {
            m_App.getProxy().readPixelRegionInNativeIntFormat(this, x, y, width, height, buffer, offset);
        }

    /**
     * @see JGimpProxy#readPixelRegionInNativeByteFormat(JGimpDrawable, int, int, int, int)
     */
    public byte[] readPixelRegionInNativeByteFormat(int x, int y, int width, int height)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException, JGimpBufferOverflowException, JGimpException {
            return m_App.getProxy().readPixelRegionInNativeByteFormat(this, x, y, width, height);
        }
    /**
     * @see JGimpProxy#readPixelRegionInNativeIntFormat(JGimpDrawable, int, int, int, int)
     */
    public int[] readPixelRegionInNativeIntFormat(int x, int y, int width, int height)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException, JGimpBufferOverflowException, JGimpException {
            return m_App.getProxy().readPixelRegionInNativeIntFormat(this, x, y, width, height);
        }

    /**
     * @see JGimpProxy#readPixelRegionInJavaIntFormat(JGimpDrawable, int, int, int, int, int[], int)
     */
    public void readPixelRegionInJavaIntFormat(int x, int y, int width, int height, int[] buffer, int offset)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException, JGimpBufferOverflowException {
            m_App.getProxy().readPixelRegionInJavaIntFormat(this, x, y, width, height, buffer, offset);
        }

    /**
     * @see JGimpProxy#readPixelRegionInJavaIntFormat(JGimpDrawable, int, int, int, int)
     */
    public int[] readPixelRegionInJavaIntFormat(int x, int y, int width, int height)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException, JGimpBufferOverflowException, JGimpException {
            return m_App.getProxy().readPixelRegionInJavaIntFormat(this, x, y, width, height);
        }

    /**
     * @see JGimpProxy#writePixelRegionInNativeByteFormat(JGimpDrawable, int, int, int, int, byte[], int)
     */
    public void writePixelRegionInNativeByteFormat(int x, int y, int width, int height, byte[] buffer, int offset)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException {
            m_App.getProxy().writePixelRegionInNativeByteFormat(this, x, y, width, height, buffer, offset);
        }
    /**
     * @see JGimpProxy#writePixelRegionInNativeIntFormat(JGimpDrawable, int, int, int, int, int[], int)
     */
    public void writePixelRegionInNativeIntFormat(int x, int y, int width, int height, int[] buffer, int offset)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException {
            m_App.getProxy().writePixelRegionInNativeIntFormat(this, x, y, width, height, buffer, offset);
        }

    /**
     * @see JGimpProxy#writePixelRegionInJavaIntFormat(JGimpDrawable, int, int, int, int, int[], int)
     */
    public void writePixelRegionInJavaIntFormat(int x, int y, int width, int height, int[] buffer, int offset)
        throws JGimpProxyException, JGimpInvalidPixelRegionException, JGimpInvalidDrawableException {
            m_App.getProxy().writePixelRegionInJavaIntFormat(this, x, y, width, height, buffer, offset);
        }
}
