package zutil;

import zutil.converter.Converter;

/**
 * A utility class for color calculations and functions.
 */
public class ColorUtil {

    /**
     * Generates a Hex String in the format '#RRGGBB'
     *
     * @param red   the red color value between 0-255
     * @param green the green color value between 0-255
     * @param blue  the blue color value between 0-255
     * @return a String hex
     */
    public static String getHexString(int red, int green, int blue) {
        byte redByte = Converter.toByte(red);
        byte greenByte = Converter.toByte(green);
        byte blueByte = Converter.toByte(blue);
        return "#" + Converter.toHexString(new byte[]{redByte, greenByte, blueByte});
    }

    /**
     * Converts a hex string with the format '#RRGGBB' to a int array of length 3 containing the red, green, blue integer values.
     *
     * @param hex a String hex.
     * @return a int array representing RGB.
     */
    public static int[] getRgbFromHexString(String hex) {
        if (hex.length() != 7)
            throw new IllegalArgumentException("Color hex length is not 7 but was " + hex.length());
        else if (hex.charAt(0) != '#')
            throw new IllegalArgumentException("Color hex is required to start with the char #");

        byte[] rgbByte = Converter.hexToByte(hex.substring(1));
        return new int[]{
                Converter.toInt(rgbByte[0]),
                Converter.toInt(rgbByte[1]),
                Converter.toInt(rgbByte[2])};
    }

    /**
     * Calculates a hue value based on RGB values.
     *
     * @param red   the red color value between 0-255
     * @param green the green color value between 0-255
     * @param blue  the blue color value between 0-255
     * @return a hue grade in the range of 0-360 degrees.
     */
    public static double getHue(int red, int green, int blue) {
        double red_percent   = red / 255.0;
        double green_percent = green / 255.0;
        double blue_percent  = blue / 255.0;

        double cMax = Math.max(red_percent, Math.max(green_percent, blue_percent));
        double cMin = Math.min(red_percent, Math.min(green_percent, blue_percent));
        double delta = cMax - cMin;

        if (delta == 0)
            return 0;
        else if (cMax == red_percent)
            return 60 * (((green_percent - blue_percent) / delta) % 6);
        else if (cMax == green_percent)
            return 60 * (((blue_percent - red_percent) / delta) + 2);
        else if (cMax == blue_percent)
            return 60 * (((red_percent - green_percent) / delta) + 4);

        throw new IllegalStateException();
    }

    /**
     * Calculates a saturation percentage based on RGB values.
     *
     * @param red   the red color value between 0-255
     * @param green the green color value between 0-255
     * @param blue  the blue color value between 0-255
     * @return a percentage of saturation in the range of 0.0 to 1.0.
     */
    public static double getSaturation(int red, int green, int blue) {
        double red_percent   = red / 255.0;
        double green_percent = green / 255.0;
        double blue_percent  = blue / 255.0;

        double cMax = Math.max(red_percent, Math.max(green_percent, blue_percent));
        double cMin = Math.min(red_percent, Math.min(green_percent, blue_percent));
        double delta = cMax - cMin;

        if (delta == 0)
            return 0;
        else
            return delta / (1 - Math.abs(2 * getLightness(red, green, blue) - 1));
    }

    /**
     * Calculates a lightness percentage based on RGB values.
     *
     * @param red   the red color value between 0-255
     * @param green the green color value between 0-255
     * @param blue  the blue color value between 0-255
     * @return a percentage of lightness in the range of 0.0 to 1.0.
     */
    public static double getLightness(int red, int green, int blue) {
        double red_percent   = red / 255.0;
        double green_percent = green / 255.0;
        double blue_percent  = blue / 255.0;

        double cMax = Math.max(red_percent, Math.max(green_percent, blue_percent));
        double cMin = Math.min(red_percent, Math.min(green_percent, blue_percent));

        return (cMax + cMin) / 2.0;
    }
}
