/*****************************************************************************
 * 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.image.renderable;

import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderContext;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.apache.batik.ext.awt.image.CompositeRule;
import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.ext.awt.image.SVGComposite;
import org.apache.batik.ext.awt.image.rendered.CachableRed;
import org.apache.batik.ext.awt.image.rendered.CompositeRed;
import org.apache.batik.ext.awt.image.rendered.FloodRed;

/**
 * Composites a list of images according to a single composite rule.
 * the image are applied in the order they are in the List given.
 *
 * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
 * @version $Id: CompositeRable8Bit.java,v 1.12 2003/04/11 13:57:47 vhardy Exp $
 */
public class CompositeRable8Bit
    extends    AbstractColorInterpolationRable
    implements CompositeRable, PaintRable {

    protected CompositeRule rule;

    public CompositeRable8Bit(List srcs,
                              CompositeRule rule,
                              boolean csIsLinear) {
        super(srcs);

        setColorSpaceLinear(csIsLinear);

        this.rule = rule;
    }

      /**
       * The sources to be composited togeather.
       * @param srcs The list of images to be composited by the composite rule.
       */
    public void setSources(List srcs) {
        init(srcs, null);
    }

      /**
       * Set the composite rule to use for combining the sources.
       * @param cr Composite rule to use.
       */
    public void setCompositeRule(CompositeRule cr) {
        touch();
        this.rule = rule;
    }

      /**
       * Get the composite rule in use for combining the sources.
       * @return Composite rule currently in use.
       */
    public CompositeRule getCompositeRule() {
        return this.rule;
    }

    /**
     * Should perform the equivilent action as
     * createRendering followed by drawing the RenderedImage to
     * Graphics2D, or return false.
     *
     * @param g2d The Graphics2D to draw to.
     * @return true if the paint call succeeded, false if
     *         for some reason the paint failed (in which
     *         case a createRendering should be used).
     */
    public boolean paintRable(Graphics2D g2d) {
        // This optimization only apply if we are using
        // SrcOver.  Otherwise things break...
        Composite c = g2d.getComposite();
        if (!SVGComposite.OVER.equals(c))
            return false;

        // For the over mode we can just draw them in order...
        if (getCompositeRule() != CompositeRule.OVER)
            return false;

        ColorSpace crCS = getOperationColorSpace();
        ColorSpace g2dCS = GraphicsUtil.getDestinationColorSpace(g2d);
        if ((g2dCS == null) || (g2dCS != crCS)) {
            return false;
        }

        // System.out.println("drawImage : " + g2dCS +
        //                    crCS);
        Iterator i = getSources().iterator();
        while (i.hasNext()) {
            GraphicsUtil.drawImage(g2d, (Filter)i.next());
        }
        return true;
    }

    public RenderedImage createRendering(RenderContext rc) {
        if (srcs.size() == 0)
            return null;

        // Just copy over the rendering hints.
        RenderingHints rh = rc.getRenderingHints();
        if (rh == null) rh = new RenderingHints(null);

        // update the current affine transform
        AffineTransform at = rc.getTransform();

        Shape aoi = rc.getAreaOfInterest();
        Rectangle2D aoiR;
        if (aoi == null)
            aoiR = getBounds2D();
        else {
            aoiR = aoi.getBounds2D();
            Rectangle2D bounds2d = getBounds2D();
            if (bounds2d.intersects(aoiR) == false)
                return null;

            Rectangle2D.intersect(aoiR, bounds2d, aoiR);
        }

        Rectangle devRect = at.createTransformedShape(aoiR).getBounds();

        rc = new RenderContext(at, aoiR, rh);

        Vector srcs = new Vector();

        Iterator i = getSources().iterator();
        while (i.hasNext()) {
            // Get the source to work with...
            Filter filt = (Filter)i.next();

            // Get our sources image...
            RenderedImage ri = filt.createRendering(rc);
            if (ri != null) {
                CachableRed cr;
                cr = convertSourceCS(ri);
                srcs.add(cr);
            } else {

                // Blank image...
                switch (rule.getRule()) {
                case CompositeRule.RULE_IN:
                    // For Mode IN One blank image kills all output
                    // (including any "future" images to be drawn).
                    return null;

                case CompositeRule.RULE_OUT:
                    // For mode OUT blank image clears output
                    // up to this point, so ignore inputs to this point.
                    srcs.clear();
                    break;

                case CompositeRule.RULE_ARITHMETIC:
                    srcs.add(new FloodRed(devRect));
                    break;

                default:
                    // All other cases we simple pretend the image didn't
                    // exist (fully transparent image has no affect).
                    break;
                }
            }
        }

        if (srcs.size() == 0)
            return null;

        // System.out.println("Done General: " + rule);
        CachableRed cr = new CompositeRed(srcs, rule);
        return cr;
    }
}
