/*
 * 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.java2d.color;

import java.awt.Color;


/**
 * Generic Color helper class.
 * <p>
 * This class supports parsing string values into color values and creating
 * color values for strings. It provides a list of standard color names.
 */
public final class ColorUtil {

    /**
     * Private constructor since this is an utility class.
     */
    private ColorUtil() {
    }


    /**
     * Lightens up a color for groove, ridge, inset and outset border effects.
     * @param col the color to lighten up
     * @param factor factor by which to lighten up (negative values darken the color)
     * @return the modified color
     */
    public static Color lightenColor(Color col, float factor) {
        // TODO: This function converts the color into the sRGB namespace.
        // This should be avoided if possible.
        float[] cols = new float[4];
        cols = col.getRGBComponents(cols);
        if (factor > 0) {
            cols[0] += (1.0f - cols[0]) * factor;
            cols[1] += (1.0f - cols[1]) * factor;
            cols[2] += (1.0f - cols[2]) * factor;
        } else {
            cols[0] -= cols[0] * -factor;
            cols[1] -= cols[1] * -factor;
            cols[2] -= cols[2] * -factor;
        }
        return new Color(cols[0], cols[1], cols[2], cols[3]);
    }



    /**
     * Indicates whether the color is a gray value.
     * @param col the color
     * @return true if it is a gray value
     */
    public static boolean isGray(Color col) {
        return (col.getRed() == col.getBlue() && col.getRed() == col.getGreen());
    }

    /**
     * Creates an uncalibrated CMYK color with the given gray value.
     * @param black the gray component (0 - 1)
     * @return the CMYK color
     */
    public static Color toCMYKGrayColor(float black) {
        //Calculated color components
        float[] cmyk = new float[] {0f, 0f, 0f, 1.0f - black};
        //Create native color
        return DeviceCMYKColorSpace.createCMYKColor(cmyk);
    }

    /**
     * Converts an arbitrary {@link Color} to a plain sRGB color doing the conversion at the
     * best possible conversion quality.
     * @param col the original color
     * @return the sRGB equivalent
     */
    public static Color toSRGBColor(Color col) {
        if (col.getColorSpace().isCS_sRGB()) {
            return col; //Don't convert if already sRGB to avoid conversion differences
        }
        float[] comps = col.getColorComponents(null);
        float[] srgb = col.getColorSpace().toRGB(comps);
        comps = col.getComponents(null);
        float alpha = comps[comps.length - 1];
        return new Color(srgb[0], srgb[1], srgb[2], alpha);
    }

    /**
     * Checks if two colors are the same color. This check is much more restrictive than
     * {@link Color#equals(Object)} in that it doesn't only check if both colors result in the
     * same sRGB value. For example, if two colors not of the same exact class are compared,
     * they are treated as not the same.
     * <p>
     * Note: At the moment, this method only supports {@link Color} and
     * {@link ColorWithAlternatives} only. Other subclasses of {@link Color} are checked only using
     * the {@link Color#equals(Object)} method.
     * @param col1 the first color
     * @param col2 the second color
     * @return true if both colors are the same color
     */
    public static boolean isSameColor(Color col1, Color col2) {
        //Check fallback sRGB values first, then go into details
        if (!col1.equals(col2)) {
            return false;
        }

        //Consider same-ness only between colors of the same class (not subclasses)
        //but consider a ColorWithAlternatives without alternatives to be the same as a Color.
        Class<?> cl1 = col1.getClass();
        if (col1 instanceof ColorWithAlternatives
                && !((ColorWithAlternatives) col1).hasAlternativeColors()) {
            cl1 = Color.class;
        }
        Class<?> cl2 = col2.getClass();
        if (col2 instanceof ColorWithAlternatives
                && !((ColorWithAlternatives) col2).hasAlternativeColors()) {
            cl2 = Color.class;
        }
        if (cl1 != cl2) {
            return false;
        }

        //Check color space
        if (!col1.getColorSpace().equals(col2.getColorSpace())) {
            return false;
        }

        //Check native components
        float[] comps1 = col1.getComponents(null);
        float[] comps2 = col2.getComponents(null);
        if (comps1.length != comps2.length) {
            return false;
        }
        for (int i = 0, c = comps1.length; i < c; i++) {
            if (comps1[i] != comps2[i]) {
                return false;
            }
        }

        //Compare alternative colors, order is relevant
        if (col1 instanceof ColorWithAlternatives && col2 instanceof ColorWithAlternatives) {
            ColorWithAlternatives ca1 = (ColorWithAlternatives) col1;
            ColorWithAlternatives ca2 = (ColorWithAlternatives) col2;
            return ca1.hasSameAlternativeColors(ca2);
        }

        return true;
    }

}
