/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2007, GeoTools Project Managment Committee (PMC)
 *    (C) 2007, Geomatys
 *   
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 */
package org.geotools.image.io;

import java.awt.Transparency;
import java.awt.image.DataBuffer;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.ComponentSampleModel;
import javax.media.jai.ComponentSampleModelJAI;
import javax.imageio.ImageTypeSpecifier;
import java.io.IOException;

import org.geotools.resources.image.ComponentColorModelJAI;


/**
 * A factory for building {@linkplain ColorModel color model} suitable for
 * {@link DataBuffer#TYPE_FLOAT}.
 *
 * @since 2.4
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio/src/main/java/org/geotools/image/io/ContinuousPalette.java $
 * @version $Id: ContinuousPalette.java 26708 2007-08-27 19:42:59Z desruisseaux $
 * @author Martin Desruisseaux
 */
final class ContinuousPalette extends Palette {
    /**
     * Tells if we should use {@link ComponentSampleModelJAI} instead of the more standard
     * {@link ComponentSampleModel}. There is two problems with models provided with J2SE 1.4:
     * <p>
     * <ul>
     *   <li>As of J2SE 1.4.0, {@link ImageTypeSpecifier#createBanded} doesn't accept
     *       {@link DataBuffer#TYPE_FLOAT} and {@link DataBuffer#TYPE_DOUBLE} argument.</li>
     *   <li>As of JAI 1.1, operators don't accept Java2D's {@link java.awt.image.DataBufferFloat}
     *       and {@link java.awt.image.DataBufferDouble}. They require JAI's DataBuffer instead.</li>
     * </ul>
     * <p>
     * This flag is set to {@code true} for J2SE 1.4. It may be
     * changed to {@code false} with future J2SE and JAI versions.
     */
    static final boolean USE_JAI_MODEL = true;

    /**
     * The minimal value, inclusive.
     */
    protected final float minimum;

    /**
     * The maximal value, inclusive.
     */
    protected final float maximum;

    /**
     * The data type, as a {@link DataBuffer#TYPE_FLOAT} or {@link DataBuffer#TYPE_DOUBLE}
     * constant.
     */
    private final int dataType;

    /**
     * Creates a palette with the specified name.
     *
     * @param factory     The originating factory.
     * @param name        The palette name.
     * @param minimum     The minimal sample value expected.
     * @param maximum     The maximal sample value expected.
     * @param dataType    The data type as a {@link DataBuffer#TYPE_FLOAT}
     *                    or {@link DataBuffer#TYPE_DOUBLE} constant.
     * @param numBands    The number of bands (usually 1).
     * @param visibleBand The band to use for color computations (usually 0).
     */
    protected ContinuousPalette(final PaletteFactory factory, final String name, final float minimum,
            final float maximum, final int dataType, final int numBands, final int visibleBand)
    {
        super(factory, name, numBands, visibleBand);
        this.minimum  = minimum;
        this.maximum  = maximum;
        this.dataType = dataType;
    }

    /**
     * Returns the scale from <cite>normalized values</cite> (values in the range [0..1])
     * to values in the range of this palette.
     */
    //@Override
    double getScale() {
        return maximum - minimum;
    }

    /**
     * Returns the offset from <cite>normalized values</cite> (values in the range [0..1])
     * to values in the range of this palette.
     */
    //@Override
    double getOffset() {
        return minimum;
    }

    /**
     * Creates a grayscale image type for this palette.
     * The image type is suitable for floating point values.
     *
     * @return  A default color space scaled to fit data.
     * @throws  IOException If an I/O operation was needed and failed.
     */
    public synchronized ImageTypeSpecifier getImageTypeSpecifier() throws IOException {
        ImageTypeSpecifier its = queryCache();
        if (its != null) {
            return its;
        }
        final ColorSpace colorSpace;
        if (minimum < maximum && !Float.isInfinite(minimum) && !Float.isInfinite(maximum)) {
            colorSpace = new ScaledColorSpace(numBands, visibleBand, minimum, maximum);
        } else {
            colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
        }
        final int[] bankIndices = new int[numBands];
        final int[] bandOffsets = new int[numBands];
        for (int i=numBands; --i>=0;) {
            bankIndices[i] = i;
        }
        if (USE_JAI_MODEL) {
            final ColorModel cm = new ComponentColorModelJAI(
                    colorSpace, null, false, false, Transparency.OPAQUE, dataType);
            its = new ImageTypeSpecifier(cm, new ComponentSampleModelJAI(
                    dataType, 1, 1, 1, 1, bankIndices, bandOffsets));
        } else {
            its = ImageTypeSpecifier.createBanded(
                    colorSpace, bankIndices, bandOffsets, dataType, false, false);
        }
        cache(its);
        return its;
    }

    /**
     * Returns a hash value for this palette.
     */
    //@Override
    public int hashCode() {
        return 37 * (37 * (37 * super.hashCode() + Float.floatToIntBits(minimum)) +
                Float.floatToIntBits(maximum)) + dataType;
    }

    /**
     * Compares this palette with the specified object for equality.
     */
    //@Override
    public boolean equals(final Object object) {
        if (object == this) {
            return true;
        }
        if (super.equals(object)) {
            final ContinuousPalette that = (ContinuousPalette) object;
            return Float.floatToIntBits(this.minimum) == Float.floatToIntBits(that.minimum) &&
                   Float.floatToIntBits(this.maximum) == Float.floatToIntBits(that.maximum) &&
                   this.dataType == that.dataType;
        }
        return false;
    }

    /**
     * Returns a string representation of this palette. Used for debugging purpose only.
     */
    //@Override
    public String toString() {
        return name + " [" + minimum + " ... " + maximum + ']';
    }
}
