/*****************************************************************************
 * 
 * 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.preflight.font.util;

import java.awt.Image;
import java.io.IOException;
import java.util.List;

import org.apache.fontbox.util.BoundingBox;
import org.apache.pdfbox.cos.COSFloat;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSNumber;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDInlinedImage;
import org.apache.pdfbox.preflight.PreflightContext;
import org.apache.pdfbox.preflight.content.ContentStreamEngine;
import org.apache.pdfbox.util.ImageParameters;
import org.apache.pdfbox.util.PDFOperator;

/**
 * This class is used to parse a glyph of a Type3 font program. If the glyph is parsed without error, the width of the
 * glyph is accessible through the getWidth method.
 */
public class PDFAType3StreamParser extends ContentStreamEngine
{
    private boolean firstOperator = true;
    private float width = 0;

    private PDInlinedImage image = null;
    private BoundingBox box = null;

    public PDFAType3StreamParser(PreflightContext context, PDPage page)
    {
        super(context, page);
    }

    /**
     * This will parse a type3 stream and create an image from it.
     * 
     * @param type3Stream
     *            The stream containing the operators to draw the image.
     * 
     * @return The image that was created.
     * 
     * @throws IOException
     *             If there is an error processing the stream.
     */
    public Image createImage(COSStream type3Stream) throws IOException
    {
        resetEngine();
        processSubStream(null, null, type3Stream);
        return image.createImage();
    }

    /**
     * This is used to handle an operation.
     * 
     * @param operator
     *            The operation to perform.
     * @param arguments
     *            The list of arguments.
     * 
     * @throws IOException
     *             If there is an error processing the operation.
     */
    protected void processOperator(PDFOperator operator, List arguments) throws IOException
    {
        super.processOperator(operator, arguments);
        String operation = operator.getOperation();

        if (operation.equals("BI"))
        {
            ImageParameters params = operator.getImageParameters();
            image = new PDInlinedImage();
            image.setImageParameters(params);
            image.setImageData(operator.getImageData());

            validImageFilter(operator);
            validImageColorSpace(operator);
        }

        if (operation.equals("d0"))
        {
            // set glyph with for a type3 font
            // COSNumber horizontalWidth = (COSNumber)arguments.get( 0 );
            // COSNumber verticalWidth = (COSNumber)arguments.get( 1 );
            // width = horizontalWidth.intValue();
            // height = verticalWidth.intValue();

            checkType3FirstOperator(arguments);

        }
        else if (operation.equals("d1"))
        {
            // set glyph with and bounding box for type 3 font
            // COSNumber horizontalWidth = (COSNumber)arguments.get( 0 );
            // COSNumber verticalWidth = (COSNumber)arguments.get( 1 );
            COSNumber llx = (COSNumber) arguments.get(2);
            COSNumber lly = (COSNumber) arguments.get(3);
            COSNumber urx = (COSNumber) arguments.get(4);
            COSNumber ury = (COSNumber) arguments.get(5);

            // width = horizontalWidth.intValue();
            // height = verticalWidth.intValue();
            box = new BoundingBox();
            box.setLowerLeftX(llx.floatValue());
            box.setLowerLeftY(lly.floatValue());
            box.setUpperRightX(urx.floatValue());
            box.setUpperRightY(ury.floatValue());

            checkType3FirstOperator(arguments);
        }

        checkColorOperators(operation);
        validRenderingIntent(operator, arguments);
        checkSetColorSpaceOperators(operator, arguments);
        validNumberOfGraphicStates(operator);
        firstOperator = false;
    }

    /**
     * According to the PDF Reference, the first operator in a CharProc of a Type3 font must be "d0" or "d1". This
     * method process this validation. This method is called by the processOperator method.
     * 
     * @param arguments
     * @throws IOException
     */
    private void checkType3FirstOperator(List arguments) throws IOException
    {
        if (!firstOperator)
        {
            throw new IOException("Type3 CharProc : First operator must be d0 or d1");
        }

        Object obj = arguments.get(0);
        if (obj instanceof Number)
        {
            width = ((Number) obj).intValue();
        }
        else if (obj instanceof COSInteger)
        {
            width = ((COSInteger) obj).floatValue();
        }
        else if (obj instanceof COSFloat)
        {
            width = ((COSFloat) obj).floatValue();
        }
        else
        {
            throw new IOException("Unexpected argument type. Expected : COSInteger or Number / Received : "
                    + obj.getClass().getName());
        }
    }

    /**
     * @return the width of the CharProc glyph description
     */
    public float getWidth()
    {
        return this.width;
    }
}
