/* 
 * 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 edu.gatech.cc.mterry.plugin;

import java.awt.Dimension;

import org.gimp.jgimp.GimpApp;
import org.gimp.jgimp.JGimpBufferOverflowException;
import org.gimp.jgimp.JGimpData;
import org.gimp.jgimp.JGimpDrawable;
import org.gimp.jgimp.JGimpException;
import org.gimp.jgimp.JGimpImage;
import org.gimp.jgimp.JGimpInvalidDrawableException;
import org.gimp.jgimp.JGimpInvalidPixelRegionException;
import org.gimp.jgimp.JGimpLayer;
import org.gimp.jgimp.JGimpPDBArgTypeConstants;
import org.gimp.jgimp.nativewrappers.JGimpInt32;
import org.gimp.jgimp.plugin.JGimpImageTypeConstructor;
import org.gimp.jgimp.plugin.JGimpMenuPathConstructor;
import org.gimp.jgimp.plugin.JGimpParamDescriptor;
import org.gimp.jgimp.plugin.JGimpPlugIn;
import org.gimp.jgimp.plugin.JGimpPlugInReturnValues;
import org.gimp.jgimp.plugin.JGimpProcedureDescriptor;
import org.gimp.jgimp.proxy.JGimpProxyException;

/**
 * This plug-in is intended for Nikon digital cameras that take a 4x4 grid
 * of pictures and place them in the same image. The plug-in divides the
 * chosen <em>layer</em> into a 4x4 grid and copies each cell into a new
 * layer in a new image.
 * <br>
 * This plug-in functions correctly with drawables/layers of any bit-depth (including
 * 16-bit images in Film GIMP) because it transfers pixels using raw bytes,
 * rather than as ints or Java ints. (It also doesn't interpret or change
 * those bytes at all, making it a rather simple example.)
 * <br>
 * The plug-in demonstrates how to read and write pixels from images.
 * It could be extended to include:
 * <ul>
 * <li> the option to specify the number of rows and columns in the image,
 * <li> a dialog box to prompt the user for this information, and
 * <li> a progress bar,
 * </ul> but these goodies are left as an exercise for the reader :)
 * (I kept this one simple to make it easy to see how to write a simple plug-in).
 */
public class ImageDividerPlugIn implements JGimpPlugIn
{
	/**
	 * Returns "true" if this plug-in is instantiated only once, with multiple
	 * calls to run() going to the same object. The alternative is that a new object
	 * is created each time "run" is called.
	 */
	public boolean remainResident(GimpApp app)
	{
		// We are instantiated every time (no need to remain resident)
		return false;
	}

	/**
	 * Returns an array of JGimpProcedureDescriptor's that will be used
	 * to install the plug-in in the GIMP's PDB.
	 */
    public JGimpProcedureDescriptor[] getPlugInInfo(GimpApp app)
	{
		/*
		 * Make an array of one parameter descriptor corresponding to our single plug-in
		 */
		JGimpProcedureDescriptor[] plugInInfoArray = new JGimpProcedureDescriptor[1];

		/*
		 * Build a list of the parameters of this plug-in
		 */
		JGimpParamDescriptor[] pdbParams = new JGimpParamDescriptor[3];
		pdbParams[0] = new JGimpParamDescriptor( JGimpPDBArgTypeConstants.PDB_INT32,
                                                 "run_mode",
												 "Interactive, non-interactive" ); // Can run interactively, or non-interactively
		pdbParams[1] = new JGimpParamDescriptor( JGimpPDBArgTypeConstants.PDB_IMAGE,
				                                 "image",
												 "Input image" ); // The image we're going to work on
		pdbParams[2] = new JGimpParamDescriptor( JGimpPDBArgTypeConstants.PDB_DRAWABLE,
				                                 "drawable",
												 "Input drawable"); // The drawable we're going to work on

		/*
		 * Create the complete descriptor for our plug-in
		 */
		plugInInfoArray[0] = new JGimpProcedureDescriptor(
				/* name of the plug-in as installed in the PDB */ "plug_in_image_divider",
				/* description of the plug-in                  */ "Simple Java Plug-In example for 4x4 digital images",
				/* the "help" message for this plug-in         */ "This plug-in was built specifically for Nikon Coolpix images that place 16 images in a 4x4 grid on one image. The plug-in will extract the 16 images and place them in layers in a new image.",
				/* Author                                      */ "Michael Terry",
				/* Copyright information                       */ "Copyright 2002, Michael Terry",
				/* Date of writing                             */ "23 June, 2002",
				/* Menu path where it is installed             */ JGimpMenuPathConstructor.createImageMenuPath("Filters/Digital Cameras/Nikon Image Divider"),
				/* Types of images this plug-in can work on    */ JGimpImageTypeConstructor.createAnyImageType().toString(),
				/* The parameters we accept                    */ pdbParams,
				/* Return value descriptions (there are none)  */ null);

		return plugInInfoArray;
	}

	/**
	 * Executes the plug-in of the following name with the following data.
	 * If the plug-in can remain resident, this method must be thread-safe.
	 */
    public JGimpPlugInReturnValues run(GimpApp app, String inName, JGimpData[] inParams) {
        try {
            /*
             * Grab our parameters
             */
            JGimpInt32 runInteractive = (JGimpInt32)inParams[0];
            JGimpImage theImage = (JGimpImage)inParams[1];
            JGimpDrawable theDrawable = (JGimpDrawable)inParams[2];

            // Get the size of the drawable
            Dimension drawableSize = theDrawable.getSize();

            // Calculate the size of our new image
            final int GRID_WIDTH = 4;
            final int GRID_HEIGHT = 4;
            int newWidth = drawableSize.width / GRID_WIDTH;
            int newHeight = drawableSize.height / GRID_HEIGHT;

            /*
             * Create a new image
             */
            JGimpImage newImage = null;
            if (theDrawable.isRGB()) {
                newImage = app.createRGBImage(newWidth, newHeight);
            } else if (theDrawable.isGrayscale()) {
                newImage = app.createGrayscaleImage(newWidth, newHeight);
            } else if (theDrawable.isIndexed()) {
                newImage = app.createIndexedImage(newWidth, newHeight);
            } else {
                throw new JGimpException("Unknown image type");
            }

            // Copy each segment into its own layer
            for (int i = 0; i < GRID_HEIGHT; i++) {
                for (int j = 0; j < GRID_WIDTH; j++) {
                    // Figure out our coordinates for this copy
                    int x = newWidth * j;
                    int y = newHeight * i;
                    // Make a new layer to paste into
                    int layerNum = i * GRID_WIDTH + j + 1;
                    copyIntoNewLayer(x, y, newWidth, newHeight, layerNum, theDrawable, newImage);
                }
            }

            // Display the new image
            newImage.display();
            // Flush the displays
            app.flushDisplays();

        } catch (Exception e) {
            System.err.println("Error caught in ImageDividerPlugIn: " + e.getMessage());
            e.printStackTrace();
            JGimpPlugInReturnValues.returnExecutionError();
        }

        return JGimpPlugInReturnValues.returnSuccess();
    }

    private void copyIntoNewLayer(
        int source_x,
        int source_y,
        int width,
        int height,
        int layerNum,
        JGimpDrawable sourceDrawable,
        JGimpImage targetImage)
        throws
            JGimpException,
            JGimpProxyException,
            JGimpInvalidPixelRegionException,
            JGimpInvalidDrawableException,
            JGimpBufferOverflowException {
                
        JGimpLayer newLayer =
            targetImage.appendNewLayer(
                width,
                height,
                sourceDrawable.hasAlpha(),
                "Layer number " + layerNum,
                100,
                0);
        
        // Read the pixels in 256x256 pixel chunks
        byte[] pixelBuf = null;
        
        final int TILE_SIZE = 256;
        for (int x_offset = 0; x_offset < width; x_offset += TILE_SIZE) {
            for (int y_offset = 0; y_offset < height; y_offset += TILE_SIZE) {
                int thisWidth = ((width - x_offset) < TILE_SIZE) ? (width - x_offset) : TILE_SIZE;
                int thisHeight = ((height - y_offset) < TILE_SIZE) ? (height - y_offset) : TILE_SIZE;
                if (pixelBuf == null) {
                    pixelBuf = sourceDrawable.readPixelRegionInNativeByteFormat(source_x + x_offset, source_y + y_offset, thisWidth, thisHeight);
                } else {
                    sourceDrawable.readPixelRegionInNativeByteFormat(source_x + x_offset, source_y + y_offset, thisWidth, thisHeight, pixelBuf, 0);
                }
                // Copy them into the new layer
                newLayer.writePixelRegionInNativeByteFormat(x_offset, y_offset, thisWidth, thisHeight, pixelBuf, 0);
            }
        }
    }

    /**
     * Called when the application is quitting
     */
    public void stop(GimpApp inApp) {
    }
}
