/*
 * 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.
 */

/* $Id$ */

package org.apache.xmlgraphics.ps;

import java.awt.Dimension;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.image.RenderedImage;
import java.io.IOException;

/**
 * Abstract helper class for generating PostScript forms.
 */
public class ImageFormGenerator extends FormGenerator {

    //Mode 1 (RenderedImage)
    private RenderedImage image;

    //Mode 2 (ImageEncoder)
    private ImageEncoder encoder;
    private ColorSpace colorSpace;
    private int bitsPerComponent = 8;

    private boolean invertImage;
    private Dimension pixelDimensions;

    /**
     * Main constructor.
     * @param formName the form's name
     * @param title the form's title or null
     * @param dimensions the form's dimensions in units (usually points)
     * @param image the image
     * @param invertImage true if the image shall be inverted
     */
    public ImageFormGenerator(String formName, String title,
            Dimension2D dimensions,
            RenderedImage image, boolean invertImage) {
        super(formName, title, dimensions);
        this.image = image;
        this.encoder = ImageEncodingHelper.createRenderedImageEncoder(image);
        this.invertImage = invertImage;
        this.pixelDimensions = new Dimension(image.getWidth(), image.getHeight());
    }

    /**
     * Main constructor.
     * @param formName the form's name
     * @param title the form's title or null
     * @param dimensions the form's dimensions in units (usually points)
     * @param dimensionsPx the form's dimensions in pixels
     * @param encoder the image encoder
     * @param colorSpace the target color space
     * @param bitsPerComponent the bits per component
     * @param invertImage true if the image shall be inverted
     */
    public ImageFormGenerator(String formName, String title,
            Dimension2D dimensions, Dimension dimensionsPx,
            ImageEncoder encoder,
            ColorSpace colorSpace, int bitsPerComponent, boolean invertImage) {
        super(formName, title, dimensions);
        this.pixelDimensions = dimensionsPx;
        this.encoder = encoder;
        this.colorSpace = colorSpace;
        this.bitsPerComponent = bitsPerComponent;
        this.invertImage = invertImage;
    }

    /**
     * Main constructor.
     * @param formName the form's name
     * @param title the form's title or null
     * @param dimensions the form's dimensions in units (usually points)
     * @param dimensionsPx the form's dimensions in pixels
     * @param encoder the image encoder
     * @param colorSpace the target color space
     * @param invertImage true if the image shall be inverted
     */
    public ImageFormGenerator(String formName, String title,
            Dimension2D dimensions, Dimension dimensionsPx,
            ImageEncoder encoder,
            ColorSpace colorSpace, boolean invertImage) {
        this(formName, title, dimensions, dimensionsPx, encoder, colorSpace, 8, invertImage);
    }

    /**
     * Returns the name of the data segment associated with this image form.
     * @return the data segment name
     */
    protected String getDataName() {
        return getFormName() + ":Data";
    }

    private String getAdditionalFilters(PSGenerator gen) {
        String implicitFilter = encoder.getImplicitFilter();
        if (implicitFilter != null) {
            return "/ASCII85Decode filter " + implicitFilter + " filter";
        } else {
            if (gen.getPSLevel() >= 3) {
                return "/ASCII85Decode filter";
            } else {
                return "/ASCII85Decode filter /RunLengthDecode filter";
            }
        }
    }

    /** {@inheritDoc} */
    protected void generatePaintProc(PSGenerator gen) throws IOException {
        if (gen.getPSLevel() == 2) {
            gen.writeln("    userdict /i 0 put"); //rewind image data
        } else {
            gen.writeln("    " + getDataName() + " 0 setfileposition"); //rewind image data
        }
        String dataSource;
        if (gen.getPSLevel() == 2) {
            dataSource = "{ " + getDataName() + " i get /i i 1 add store } bind";
        } else {
            dataSource = getDataName();
            if (gen.getPSLevel() >= 3) {
                String implicitFilter = encoder.getImplicitFilter();
                if (implicitFilter == null) {
                    dataSource += " /FlateDecode filter";
                }
            }
        }
        AffineTransform at = new AffineTransform();
        at.scale(getDimensions().getWidth(), getDimensions().getHeight());
        gen.concatMatrix(at);
        PSDictionary imageDict = new PSDictionary();
        imageDict.put("/DataSource", dataSource);
        if (this.image != null) {
            PSImageUtils.writeImageCommand(this.image, imageDict, gen);
        } else {
            imageDict.put("/BitsPerComponent", Integer.toString(this.bitsPerComponent));
            PSImageUtils.writeImageCommand(imageDict,
                    this.pixelDimensions, this.colorSpace, this.invertImage,
                    gen);
        }
    }

    /** {@inheritDoc} */
    protected void generateAdditionalDataStream(PSGenerator gen) throws IOException {
        gen.writeln("/" + getDataName() + " currentfile");
        gen.writeln(getAdditionalFilters(gen));
        if (gen.getPSLevel() == 2) {
            //Creates a data array from the inline file
            gen.writeln("{ /temp exch def ["
                    + " { temp 16384 string readstring not {exit } if } loop ] } exec");
        } else {
            gen.writeln("/ReusableStreamDecode filter");
        }
        PSImageUtils.compressAndWriteBitmap(encoder, gen);
        gen.writeln("def");
    }

}
