/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.pdfbox.pdmodel;

import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;

/**
 * Provides the ability to write to an appearance content stream.
 *
 * @author Ben Litchfield
 */
public final class PDAppearanceContentStream extends PDAbstractContentStream implements Closeable
{
    /**
     * Create a new appearance stream.
     *
     * @param appearance
     *            The appearance stream to write to.
     * @throws IOException If there is an error writing to the content stream.
     */
    public PDAppearanceContentStream(PDAppearanceStream appearance) throws IOException
    {
        this(appearance, appearance.getStream().createOutputStream());
    }

    /**
     * Create a new appearance stream.
     *
     * @param appearance
     *            The appearance stream to add to.
     * @param outputStream
     *            The appearances output stream to write to.
     */
    public PDAppearanceContentStream(PDAppearanceStream appearance, OutputStream outputStream)
    {
        super(null, outputStream, appearance.getResources());
    }

    /**
     * Set the stroking color.
     * 
     * <p>
     * The command is only emitted if the color is not null and the number of
     * components is &gt; 0.
     * 
     * @param color The colorspace to write.
     * @throws IOException If there is an error writing to the content stream.
     * @see PDAbstractContentStream#setStrokingColor(PDColor)
     */
    public boolean setStrokingColorOnDemand(PDColor color) throws IOException
    {
        if (color != null)
        {
            float[] components = color.getComponents();
            if (components.length > 0)
            {
                setStrokingColor(components);
                return true;
            }
        }
        return false;
    }

    /**
     * Set the stroking color.
     * 
     * @see PDAbstractContentStream#setStrokingColor(java.awt.Color)
     * @param components
     *            the color components dependent on the color space being used.
     * @throws IOException If there is an error writing to the content stream.
     */
    public void setStrokingColor(float[] components) throws IOException
    {
        for (float value : components)
        {
            writeOperand(value);
        }

        int numComponents = components.length;
        switch (numComponents)
        {
        case 1:
            writeOperator("G");
            break;
        case 3:
            writeOperator("RG");
            break;
        case 4:
            writeOperator("K");
            break;
        default:
            break;
        }
    }

    /**
     * Set the non stroking color.
     * 
     * <p>
     * The command is only emitted if the color is not null and the number of
     * components is &gt; 0.
     * 
     * @param color The colorspace to write.
     * @throws IOException If there is an error writing to the content stream.
     * @see PDAbstractContentStream#setNonStrokingColor(PDColor)
     */
    public boolean setNonStrokingColorOnDemand(PDColor color) throws IOException
    {
        if (color != null)
        {
            float[] components = color.getComponents();
            if (components.length > 0)
            {
                setNonStrokingColor(components);
                return true;
            }
        }
        return false;
    }

    /**
     * Set the non stroking color.
     * 
     * @see PDAbstractContentStream#setNonStrokingColor(java.awt.Color)
     * @param components
     *            the color components dependent on the color space being used.
     * @throws IOException If there is an error writing to the content stream.
     */
    public void setNonStrokingColor(float[] components) throws IOException
    {
        for (float value : components)
        {
            writeOperand(value);
        }

        int numComponents = components.length;
        switch (numComponents)
        {
        case 1:
            writeOperator("g");
            break;
        case 3:
            writeOperator("rg");
            break;
        case 4:
            writeOperator("k");
            break;
        default:
            break;
        }
    }

    public void setBorderLine(float lineWidth, PDBorderStyleDictionary bs) throws IOException
    {
        // Can't use PDBorderStyleDictionary.getDashStyle() as
        // this will return a default dash style if non is existing
        if (bs != null && bs.getCOSObject().containsKey(COSName.D) && 
                          bs.getStyle().equals(PDBorderStyleDictionary.STYLE_DASHED))
        {
            setLineDashPattern(bs.getDashStyle().getDashArray(), 0);
        }
        setLineWidthOnDemand(lineWidth);
    }

    /**
     * Sets the line width. The command is only emitted if the lineWidth is
     * different to 1.
     * 
     * @param lineWidth the line width of the path.
     * @throws IOException If there is an error writing to the content stream.
     * @see PDAbstractContentStream#setLineWidth(float)
     */
    public void setLineWidthOnDemand(float lineWidth) throws IOException
    {
        // Acrobat doesn't write a line width command
        // for a line width of 1 as this is default.
        // Will do the same.
        if (!(Math.abs(lineWidth - 1) < 1e-6))
        {
            setLineWidth(lineWidth);
        }
    }
    
    /**
     * Draw a shape.
     *
     * <p>
     * Dependent on the lineWidth and whether or not there is a background to be generated there are
     * different commands to be used for draw a shape.
     *
     * @param lineWidth the line width of the path.
     * @param hasStroke shall there be a stroking color.
     * @param hasFill shall there be a fill color.
     * @throws IOException If there is an error writing to the content stream.
     */
    public void drawShape(float lineWidth, boolean hasStroke, boolean hasFill) throws IOException
    {
        // initial setting if stroking shall be done
        boolean resolvedHasStroke = hasStroke;

        // no stroking for very small lines
        if (lineWidth < 1e-6)
        {
            resolvedHasStroke = false;
        }
        if (hasFill && resolvedHasStroke)
        {
            fillAndStroke();
        }
        else if (resolvedHasStroke)
        {
            stroke();
        }
        else if (hasFill)
        {
            fill();
        }
        else
        {
            writeOperator("n");
        }
    }
}

@@ -3690,32 +3690,156 @@
 reak;%0A        %7D%0A
+        //TODO shouldn't we set the stack?%0A        //Or call the appropriate setStrokingColor() method from the base class?%0A
     %7D%0A%0A    /**%0A 
@@ -5388,24 +5388,151 @@
 ;%0A        %7D%0A
+        //TODO shouldn't we set the stack?%0A        //Or call the appropriate setNonStrokingColor() method from the base class?%0A
     %7D%0A%0A    p

add a //TODO