/*
 * $Id: PDFColorSpace.java,v 1.5 2009-03-08 20:46:16 tomoke Exp $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package com.sun.pdfview.colorspace;

import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFPaint;
import com.sun.pdfview.PDFParseException;
import com.sun.pdfview.function.PDFFunction;

import java.awt.Color;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import java.util.Map;


/**
 * A color space that can convert a set of color components into
 * PDFPaint.
 * @author Mike Wessler
 */
public class PDFColorSpace {
    /** the name of the device-dependent gray color space */
    public static final int COLORSPACE_GRAY = 0;

    /** the name of the device-dependent RGB color space */
    public static final int COLORSPACE_RGB = 1;

    /** the name of the device-dependent CMYK color space */
    public static final int COLORSPACE_CMYK = 2;

    /** the name of the pattern color space */
    public static final int COLORSPACE_PATTERN = 3;

    /** the device-dependent color spaces */
    //    private static PDFColorSpace graySpace =
    //            new PDFColorSpace(ColorSpace.getInstance(ColorSpace.CS_GRAY));
    private static PDFColorSpace rgbSpace = new PDFColorSpace(ColorSpace.getInstance(
                ColorSpace.CS_sRGB));
    private static PDFColorSpace cmykSpace = new PDFColorSpace(new CMYKColorSpace());

    /** the pattern space */
    private static PDFColorSpace patternSpace = new PatternSpace();

    /** graySpace and the gamma correction for it. */
    private static PDFColorSpace graySpace;

    static {
        boolean useSGray = true;

        try {
            graySpace = new PDFColorSpace((!useSGray)
                    ? ColorSpace.getInstance(ColorSpace.CS_GRAY)
                    : new ICC_ColorSpace(ICC_Profile.getInstance(
                            PDFColorSpace.class.getResourceAsStream(
                                ("sGray.icc")))));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /** the color space */
    ColorSpace cs;

    /**
     * create a PDFColorSpace based on a Java ColorSpace
     * @param cs the Java ColorSpace
     */
    protected PDFColorSpace(ColorSpace cs) {
        this.cs = cs;
    }

    /**
     * Get a color space by name
     *
     * @param name the name of one of the device-dependent color spaces
     */
    public static PDFColorSpace getColorSpace(int name) {
        switch (name) {
        case COLORSPACE_GRAY:
            return graySpace;

        case COLORSPACE_RGB:
            return rgbSpace;

        case COLORSPACE_CMYK:
            return cmykSpace;

        case COLORSPACE_PATTERN:
            return patternSpace;

        default:
            throw new IllegalArgumentException("Unknown Color Space name: " +
                name);
        }
    }

    /**
     * Get a color space specified in a PDFObject
     *
     * @param csobj the PDFObject with the colorspace information
     */
    public static PDFColorSpace getColorSpace(PDFObject csobj, Map resources)
        throws IOException {
        String name;

        PDFObject colorSpaces = null;

        if (resources != null) {
            colorSpaces = (PDFObject) resources.get("ColorSpace");
        }

        if (csobj.getType() == PDFObject.NAME) {
            name = csobj.getStringValue();

            if (name.equals("DeviceGray") || name.equals("G")) {
                return getColorSpace(COLORSPACE_GRAY);
            } else if (name.equals("DeviceRGB") || name.equals("RGB")) {
                return getColorSpace(COLORSPACE_RGB);
            } else if (name.equals("DeviceCMYK") || name.equals("CMYK")) {
                return getColorSpace(COLORSPACE_CMYK);
            } else if (name.equals("Pattern")) {
                return getColorSpace(COLORSPACE_PATTERN);
            } else if (colorSpaces != null) {
                csobj = (PDFObject) colorSpaces.getDictRef(name);
            }
        }

        if (csobj == null) {
            return null;
        } else if (csobj.getCache() != null) {
            return (PDFColorSpace) csobj.getCache();
        }

        PDFColorSpace value = null;

        // csobj is [/name <<dict>>]
        PDFObject[] ary = csobj.getArray();
        name = ary[0].getStringValue();

        if (name.equals("CalGray")) {
            value = new PDFColorSpace(new CalGrayColor(ary[1]));
        } else if (name.equals("CalRGB")) {
            value = new PDFColorSpace(new CalRGBColor(ary[1]));
        } else if (name.equals("Lab")) {
            value = new PDFColorSpace(new LabColor(ary[1]));
        } else if (name.equals("ICCBased")) {
            ByteArrayInputStream bais = new ByteArrayInputStream(ary[1].getStream());
            ICC_Profile profile = ICC_Profile.getInstance(bais);
            value = new PDFColorSpace(new ICC_ColorSpace(profile));
        } else if (name.equals("Separation") || name.equals("DeviceN")) {
            PDFColorSpace alternate = getColorSpace(ary[2], resources);
            PDFFunction function = PDFFunction.getFunction(ary[3]);

            value = new AlternateColorSpace(alternate, function);
        } else if (name.equals("Indexed") || name.equals("I")) {
            /**
             * 4.5.5 [/Indexed baseColor hival lookup]
             */
            PDFColorSpace refspace = getColorSpace(ary[1], resources);

            // number of indices= ary[2], data is in ary[3];
            int count = ary[2].getIntValue();
            value = new IndexedColor(refspace, count, ary[3]);
        } else if (name.equals("Pattern")) {
            if (ary.length == 1) {
                return getColorSpace(COLORSPACE_PATTERN);
            }

            PDFColorSpace base = getColorSpace(ary[1], resources);

            return new PatternSpace(base);
        } else {
            throw new PDFParseException("Unknown color space: " + name +
                " with " + ary[1]);
        }

        csobj.setCache(value);

        return value;
    }

    /**
     * get the number of components expected in the getPaint command
     */
    public int getNumComponents() {
        return cs.getNumComponents();
    }

    /**
     * get the PDFPaint representing the color described by the
     * given color components
     * @param components the color components corresponding to the given
     * colorspace
     * @return a PDFPaint object representing the closest Color to the
     * given components.
     */
    public PDFPaint getPaint(float[] components) {
        float[] rgb = cs.toRGB(components);

        return PDFPaint.getColorPaint(new Color(rgb[0], rgb[1], rgb[2]));
    }

    /**
     * get the original Java ColorSpace.
     */
    public ColorSpace getColorSpace() {
        return cs;
    }
}
