/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included with this distribution in  *
 * the LICENSE file.                                                         *
 *****************************************************************************/

package org.apache.batik.ext.awt;

import java.awt.Color;
import java.awt.Paint;
import java.awt.geom.AffineTransform;

/** This is the superclass for Paints which use a multiple color
 * gradient to fill in their raster.  It provides storage for variables and
 * enumerated values common to LinearGradientPaint and RadialGradientPaint.
 *
 *
 * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
 * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
 * @version $Id: MultipleGradientPaint.java,v 1.4 2003/04/11 13:57:12 vhardy Exp $
 *
 */

public abstract class MultipleGradientPaint implements Paint {

    /** Transparency. */
    protected int transparency;

    /** Gradient keyframe values in the range 0 to 1. */
    protected float[] fractions;

    /** Gradient colors. */
    protected Color[] colors;

    /** Transform to apply to gradient. */
    protected AffineTransform gradientTransform;

    /** The method to use when painting out of the gradient bounds. */
    protected CycleMethodEnum cycleMethod;

    /** The colorSpace in which to perform the interpolation. */
    protected ColorSpaceEnum colorSpace;

    /** Inner class to allow for typesafe enumerated ColorSpace values. */
    public static class ColorSpaceEnum {
    }

    /** Inner class to allow for typesafe enumerated CycleMethod values. */
    public static class CycleMethodEnum {
    }

    /** Indicates (if the gradient starts or ends inside the target region)
     *  to use the terminal colors to fill the remaining area. (default)
     */
    public static final CycleMethodEnum NO_CYCLE = new CycleMethodEnum();

    /** Indicates (if the gradient starts or ends inside the target region),
     *  to cycle the gradient colors start-to-end, end-to-start to fill the
     *  remaining area.
     */
    public static final CycleMethodEnum REFLECT = new CycleMethodEnum();

    /** Indicates (if the gradient starts or ends inside the target region),
     *  to cycle the gradient colors start-to-end, start-to-end to fill the
     *  remaining area.
     */
    public static final CycleMethodEnum REPEAT = new CycleMethodEnum();

    /** Indicates that the color interpolation should occur in sRGB space.
     *  (default)
     */
    public static final ColorSpaceEnum SRGB = new ColorSpaceEnum();

    /** Indicates that the color interpolation should occur in linearized
     *  RGB space.
     */
    public static final ColorSpaceEnum LINEAR_RGB = new ColorSpaceEnum();


     /**
     * Superclass constructor, typical user should never have to call this.
     *
     * @param fractions numbers ranging from 0.0 to 1.0 specifying the
     * distribution of colors along the gradient
     *
     * @param colors array of colors corresponding to each fractional value
     *
     * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
     *
     * @param colorSpace which colorspace to use for interpolation,
     * either SRGB or LINEAR_RGB
     *
     * @param gradientTransform transform to apply to the gradient
     *
     * @throws NullPointerException if arrays are null, or
     * gradientTransform is null
     *
     * @throws IllegalArgumentException if fractions.length != colors.length,
     * or if colors is less than 2 in size, or if an enumerated value is bad.
     *
     * @see PaintContext
     */
    public MultipleGradientPaint(float[] fractions,
                                 Color[] colors,
                                 CycleMethodEnum cycleMethod,
                                 ColorSpaceEnum colorSpace,
                                 AffineTransform gradientTransform) {

        if (fractions == null) {
            throw new IllegalArgumentException("Fractions array cannot be " +
                                               "null");
        }

        if (colors == null) {
            throw new IllegalArgumentException("Colors array cannot be null");
        }

        if (fractions.length != colors.length) {
            throw new IllegalArgumentException("Colors and fractions must " +
                                               "have equal size");
        }

        if (colors.length < 2) {
            throw new IllegalArgumentException("User must specify at least " +
                                               "2 colors");
        }

        if ((colorSpace != LINEAR_RGB) &&
            (colorSpace != SRGB)) {
            throw new IllegalArgumentException("Invalid colorspace for " +
                                               "interpolation.");
        }

        if ((cycleMethod != NO_CYCLE) &&
            (cycleMethod != REFLECT) &&
            (cycleMethod != REPEAT)) {
            throw new IllegalArgumentException("Invalid cycle method.");
        }

        if (gradientTransform == null) {
            throw new IllegalArgumentException("Gradient transform cannot be "+
                                               "null.");
        }

        //copy the fractions array
        this.fractions = new float[fractions.length];
        System.arraycopy(fractions, 0, this.fractions, 0, fractions.length);

        //copy the colors array
        this.colors = new Color[colors.length];
        System.arraycopy(colors, 0, this.colors, 0, colors.length);

        //copy some flags
        this.colorSpace = colorSpace;
        this.cycleMethod = cycleMethod;

        //copy the gradient transform
        this.gradientTransform = (AffineTransform)gradientTransform.clone();

        // Process transparency
        boolean opaque = true;
        for(int i=0; i<colors.length; i++){
            opaque = opaque && (colors[i].getAlpha()==0xff);
        }

        if(opaque) {
            transparency = OPAQUE;
        }

        else {
            transparency = TRANSLUCENT;
        }
    }

    /**
     * Returns a copy of the array of colors used by this gradient.
     * @return a copy of the array of colors used by this gradient
     *
     */
    public Color[] getColors() {
        Color colors[] = new Color[this.colors.length];
        System.arraycopy(this.colors, 0, colors, 0, this.colors.length);
        return colors;
    }

    /**
     * Returns a copy of the array of floats used by this gradient
     * to calculate color distribution.
     * @return a copy of the array of floats used by this gradient to
     * calculate color distribution
     *
     */
    public float[] getFractions() {
        float fractions[] = new float[this.fractions.length];
        System.arraycopy(this.fractions, 0, fractions, 0, this.fractions.length);
        return fractions;
    }

    /**
     * Returns the transparency mode for this LinearGradientPaint.
     * @return an integer value representing this LinearGradientPaint object's
     * transparency mode.
     * @see Transparency
     */
    public int getTransparency() {
        return transparency;
    }

    /**
     * Returns the enumerated type which specifies cycling behavior.
     * @return the enumerated type which specifies cycling behavior
     */
    public CycleMethodEnum getCycleMethod() {
        return cycleMethod;
    }

    /**
     * Returns the enumerated type which specifies color space for
     * interpolation.
     * @return the enumerated type which specifies color space for
     * interpolation
     */
    public ColorSpaceEnum getColorSpace() {
        return colorSpace;
    }

    /**
     * Returns a copy of the transform applied to the gradient.
     * @return a copy of the transform applied to the gradient.
     */
    public AffineTransform getTransform() {
        return (AffineTransform)gradientTransform.clone();
    }
}
