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

import java.awt.Color;
import java.awt.color.ColorSpace;
import java.io.File;
import java.net.URI;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import org.apache.xmlgraphics.java2d.color.ColorSpaces;
import org.apache.xmlgraphics.java2d.color.ColorWithAlternatives;
import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
import org.apache.xmlgraphics.java2d.color.RenderingIntent;

import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopFactory;

/**
 * Tests the ColorUtil class.
 */
public class ColorUtilTestCase {
    private FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

    /**
     * Test serialization to String.
     * @throws Exception if an error occurs
     */
    @Test
    public void testSerialization() throws Exception {
        Color col = new Color(1.0f, 1.0f, 0.5f, 1.0f);
        String s = ColorUtil.colorToString(col);

        //This is what the old color spit out. Now it is 80 due to rounding
        //assertEquals("#ffff7f", s);
        assertEquals("#ffff80", s);

        col = new Color(1.0f, 0.0f, 0.0f, 0.8f);
        s = ColorUtil.colorToString(col);
        assertEquals("#ff0000cc", s);
    }

    /**
     * Test deserialization from String.
     * @throws Exception if an error occurs
     */
    @Test
    public void testDeserialization() throws Exception {
        Color col = ColorUtil.parseColorString(null, "#ffff7f");
        assertEquals(255, col.getRed());
        assertEquals(255, col.getGreen());
        assertEquals(127, col.getBlue());
        assertEquals(255, col.getAlpha());

        col = ColorUtil.parseColorString(null, "#ff0000cc");
        assertEquals(255, col.getRed());
        assertEquals(0, col.getGreen());
        assertEquals(0, col.getBlue());
        assertEquals(204, col.getAlpha());
    }

    /**
     * Test equals().
     * @throws Exception if an error occurs
     */
    @Test
    public void testEquals() throws Exception {
        Color col1 = ColorUtil.parseColorString(null, "#ff0000cc");
        Color col2 = ColorUtil.parseColorString(null, "#ff0000cc");
        assertEquals(col1, col2);

        col1 = ColorUtil.parseColorString(null, "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.0,0.0,0.0,0.5)");
        /* The following doesn't work since java.awt.Color from Sun doesn't round consistently
        col2 = ColorUtil.parseColorString(null, "cmyk(0.0,0.0,0.0,0.5)");
        assertEquals(col1, col2);
        */

        col2 = ColorUtil.parseColorString(null, "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.5,0.5,0.5,0.0)");
        assertTrue(col1.equals(col2));
        assertFalse(org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(col1, col2));
    }

    /**
     * Tests the rgb() function.
     * @throws Exception if an error occurs
     */
    @Test
    public void testRGB() throws Exception {

        FOUserAgent ua = fopFactory.newFOUserAgent();
        Color colActual;

        colActual = ColorUtil.parseColorString(ua, "rgb(255, 40, 0)");
        assertEquals(255, colActual.getRed());
        assertEquals(40, colActual.getGreen());
        assertEquals(0, colActual.getBlue());
        assertEquals(255, colActual.getAlpha());
        assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colActual.getColorSpace());
    }

    /**
     * Tests the fop-rgb-icc() function.
     * @throws Exception if an error occurs
     */
    @Test
    public void testRGBICC() throws Exception {
        FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
        // [TBD] FIXME - use getResource() to access profile [GA]
        URI sRGBLoc = new URI("../fop-core/src/main/resources/org/apache/fop/pdf/sRGB.icc");
        ColorSpace cs = fopFactory.getColorSpaceCache().get(
                "sRGBAlt", sRGBLoc.toASCIIString(), RenderingIntent.AUTO);
        assertNotNull("Color profile not found", cs);

        FOUserAgent ua = fopFactory.newFOUserAgent();
        ColorWithFallback colActual;

        //fop-rgb-icc() is used instead of rgb-icc() inside FOP!
        String colSpec = "fop-rgb-icc(1.0,0.0,0.0,sRGBAlt,"
            + "\"" + sRGBLoc.toASCIIString() + "\",1.0,0.0,0.0)";
        colActual = (ColorWithFallback)ColorUtil.parseColorString(ua, colSpec);
        assertEquals(cs, colActual.getColorSpace());
        assertEquals(255, colActual.getRed(), 2f); //Java 5: 253, Java 6: 255
        assertEquals(0, colActual.getGreen(), 25f); //Java 5: 25, Java 6: 0
        assertEquals(0, colActual.getBlue());
        //I don't understand the difference. Maybe Java's sRGB and HP's sRGB are somehow not
        //equivalent. This is only going to be a problem if anyone actually makes use of the
        //RGB fallback in any renderer.
        //TODO Anyone know what's going on here?
        float[] comps = colActual.getColorComponents(null);
        assertEquals(3, comps.length);
        assertEquals(1f, comps[0], 0);
        assertEquals(0f, comps[1], 0);
        assertEquals(0f, comps[2], 0);
        assertEquals(0, colActual.getAlternativeColors().length);

        Color fallback = colActual.getFallbackColor();
        assertTrue(fallback.getColorSpace().isCS_sRGB());
        assertEquals(255, fallback.getRed());
        assertEquals(0, fallback.getGreen());
        assertEquals(0, fallback.getBlue());

        assertEquals(colSpec, ColorUtil.colorToString(colActual));

        colSpec = "fop-rgb-icc(1.0,0.5,0.0,blah,"
            + "\"invalid.icm\",1.0,0.5,0.0,0.15)";
        Color colFallback = ColorUtil.parseColorString(ua, colSpec);
        assertEquals(new Color(1.0f, 0.5f, 0.0f), colFallback);
    }

    /**
     * Tests the cmyk() function.
     * @throws Exception if an error occurs
     */
    @Test
    public void testCMYK() throws Exception {
        ColorWithAlternatives colActual;
        String colSpec;

        colSpec = "cmyk(0.0, 0.0, 1.0, 0.0)";
        colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
        assertEquals(255, colActual.getRed());
        assertEquals(255, colActual.getGreen());
        assertEquals(0, colActual.getBlue());
        Color alt = colActual.getAlternativeColors()[0];
        assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace());
        float[] comps = alt.getColorComponents(null);
        assertEquals(4, comps.length);
        assertEquals(0f, comps[0], 0);
        assertEquals(0f, comps[1], 0);
        assertEquals(1f, comps[2], 0);
        assertEquals(0f, comps[3], 0);
        assertEquals("fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)",
                ColorUtil.colorToString(colActual));

        colSpec = "cmyk(0.0274, 0.2196, 0.3216, 0.0)";
        colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
        assertEquals(248, colActual.getRed(), 1);
        assertEquals(199, colActual.getGreen(), 1);
        assertEquals(172, colActual.getBlue(), 1);
        alt = colActual.getAlternativeColors()[0];
        assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace());
        comps = alt.getColorComponents(null);
        assertEquals(0.0274f, comps[0], 0.001);
        assertEquals(0.2196f, comps[1], 0.001);
        assertEquals(0.3216f, comps[2], 0.001);
        assertEquals(0f, comps[3], 0);
        assertEquals("fop-rgb-icc(0.9726,0.7804,0.67840004,#CMYK,,0.0274,0.2196,0.3216,0.0)",
                ColorUtil.colorToString(colActual));

        colSpec = "fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)";
        colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
        assertEquals(255, colActual.getRed());
        assertEquals(255, colActual.getGreen());
        assertEquals(0, colActual.getBlue());
        alt = colActual.getAlternativeColors()[0];
        assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace());
        comps = alt.getColorComponents(null);
        assertEquals(4, comps.length);
        assertEquals(0f, comps[0], 0);
        assertEquals(0f, comps[1], 0);
        assertEquals(1f, comps[2], 0);
        assertEquals(0f, comps[3], 0);
        assertEquals("fop-rgb-icc(1.0,1.0,0.0,#CMYK,,0.0,0.0,1.0,0.0)",
                ColorUtil.colorToString(colActual));

        colSpec = "fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.0,0.0,0.0,0.5)";
        colActual = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
        assertEquals(127, colActual.getRed(), 1);
        assertEquals(127, colActual.getGreen(), 1);
        assertEquals(127, colActual.getBlue(), 1);
        alt = colActual.getAlternativeColors()[0];
        assertEquals(ColorSpaces.getDeviceCMYKColorSpace(), alt.getColorSpace());
        comps = alt.getColorComponents(null);
        assertEquals(4, comps.length);
        assertEquals(0f, comps[0], 0);
        assertEquals(0f, comps[1], 0);
        assertEquals(0f, comps[2], 0);
        assertEquals(0.5f, comps[3], 0);
        assertEquals("fop-rgb-icc(0.5,0.5,0.5,#CMYK,,0.0,0.0,0.0,0.5)",
                ColorUtil.colorToString(colActual));

        //Verify that the cmyk() and fop-rgb-icc(#CMYK) functions have the same results
        ColorWithAlternatives colCMYK = (ColorWithAlternatives)ColorUtil.parseColorString(
                null, "cmyk(0,0,0,0.5)");
        assertEquals(colCMYK.getAlternativeColors()[0], colActual.getAlternativeColors()[0]);
        //The following doesn't work:
        //assertEquals(colCMYK, colActual);
        //java.awt.Color does not consistenly calculate the int RGB values:
        //Color(ColorSpace cspace, float components[], float alpha): 0.5 --> 127
        //Color(float r, float g, float b): 0.5 --> 128
        if (!colCMYK.equals(colActual)) {
            System.out.println("Info: java.awt.Color does not consistently calculate"
                    + " int RGB values from float RGB values.");
        }
    }

    /**
     * Tests color for the #Separation pseudo-colorspace.
     * @throws Exception if an error occurs
     */
    @Test
    public void testSeparationColor() throws Exception {
        ColorWithFallback colActual;
        String colSpec;

        colSpec = "fop-rgb-icc(1.0,0.8,0.0,#Separation,,Postgelb)";
        colActual = (ColorWithFallback)ColorUtil.parseColorString(null, colSpec);
        assertEquals(255, colActual.getRed(), 5);
        assertEquals(204, colActual.getGreen(), 3);
        assertEquals(0, colActual.getBlue(), 12);
        //sRGB results differ between JDKs

        Color fallback = colActual.getFallbackColor();
        assertEquals(255, fallback.getRed());
        assertEquals(204, fallback.getGreen());
        assertEquals(0, fallback.getBlue());

        assertFalse(colActual.hasAlternativeColors());

        assertTrue(colActual.getColorSpace() instanceof NamedColorSpace);
        NamedColorSpace ncs;
        ncs = (NamedColorSpace)colActual.getColorSpace();
        assertEquals("Postgelb", ncs.getColorName());
        float[] comps = colActual.getColorComponents(null);
        assertEquals(1, comps.length);
        assertEquals(1f, comps[0], 0);
        assertEquals(colSpec, ColorUtil.colorToString(colActual));

    }

    /**
     * Tests the fop-rgb-named-color() function.
     * @throws Exception if an error occurs
     */
    @Test
    public void testNamedColorProfile() throws Exception {
        FopFactory fopFactory = FopFactory.newInstance(new File("./").toURI());
        URI ncpLoc = new URI("test/resources/color/ncp-example.icc");
        ColorSpace cs = fopFactory.getColorSpaceCache().get(
                "NCP", ncpLoc.toASCIIString(), RenderingIntent.AUTO);
        assertNotNull("Color profile not found", cs);

        FOUserAgent ua = fopFactory.newFOUserAgent();
        ColorWithFallback colActual;

        //fop-rgb-named-color() is used instead of rgb-named-color() inside FOP!
        String colSpec = "fop-rgb-named-color(1.0,0.8,0.0,NCP,"
            + "\"" + ncpLoc.toASCIIString() + "\",Postgelb)";
        colActual = (ColorWithFallback)ColorUtil.parseColorString(ua, colSpec);
        assertEquals(255, colActual.getRed(), 2);
        assertEquals(193, colActual.getGreen(), 2);
        assertEquals(0, colActual.getBlue());

        Color fallback = colActual.getFallbackColor();
        assertEquals(255, fallback.getRed());
        assertEquals(204, fallback.getGreen());
        assertEquals(0, fallback.getBlue());
        assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), fallback.getColorSpace());

        float[] comps = fallback.getColorComponents(null);
        assertEquals(3, comps.length);
        assertEquals(1f, comps[0], 0);
        assertEquals(0.8f, comps[1], 0);
        assertEquals(0f, comps[2], 0);

        assertTrue(colActual.getColorSpace() instanceof NamedColorSpace);
        NamedColorSpace ncs;
        ncs = (NamedColorSpace)colActual.getColorSpace();
        assertEquals("Postgelb", ncs.getColorName());
        comps = colActual.getColorComponents(null);
        assertEquals(1, comps.length);
        assertEquals(1f, comps[0], 0);

        assertEquals(colSpec, ColorUtil.colorToString(colActual));
    }

    @Test
    public void testAlphaColor() throws Exception {
        String colSpec = "fop-rgb-icc(0.6,0.6,0.4,#alpha,0.4,#CMYK,,0.0,0.0,0.2,0.4)";
        ColorWithAlternatives colActual2 = (ColorWithAlternatives)ColorUtil.parseColorString(null, colSpec);
        assertEquals(colActual2.getAlternativeColors()[0].getAlpha(), 102);
        assertEquals(ColorUtil.colorToString(colActual2), colSpec);
    }
}
