/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.util;

import java.awt.Color;

import java.util.HashMap;
import java.util.Map;


/** Definitions and conversions for the colors of the LaTeX
 * <code>colordvi</code> package (approximately) and a predefined set of
 * gray shades.
 * @since 2.0
 */
public final class JaxoColor {
    /** All Colors defined in JaxoColor can be chosen. */
    public static final int JAXO_COLORS_MODE = 0;

    /** Colors defined in JaxoColor except grayscales can be chosen. */
    public static final int JAXO_COLORS_NO_GRAYSCALES_MODE = 1;

    /** Any Color can be chosen. */
    public static final int ALL_COLORS_MODE = 2;

    /** The color GreenYellow. */
    public static final Color GREEN_YELLOW = new Color(214, 232, 15);

    /** The color Yellow. */
    public static final Color YELLOW = new Color(249, 229, 38);

    /** The color Goldenrod. */
    public static final Color GOLDEN_ROD = new Color(249, 214, 22);

    /** The color Dandelion. */
    public static final Color DANDELION = new Color(255, 198, 30);

    /** The color Apricot. */
    public static final Color APRICOT = new Color(252, 165, 119);

    /** The color Peach. */
    public static final Color PEACH = new Color(252, 127, 63);

    /** The color Melon. */
    public static final Color MELON = new Color(249, 130, 127);

    /** The color YellowOrange. */
    public static final Color YELLOW_ORANGE = new Color(234, 175, 15);

    /** The color Orange. */
    public static final Color ORANGE = new Color(221, 79, 5);

    /** The color BurntOrange. */
    public static final Color BURNT_ORANGE = new Color(221, 117, 0);

    /** The color Bittersweet. */
    public static final Color BITTERSWEET = new Color(188, 79, 7);

    /** The color RedOrange. */
    public static final Color RED_ORANGE = new Color(226, 61, 40);

    /** The color Mahagony. */
    public static final Color MAHOGANY = new Color(155, 48, 28);

    /** The color Maroon. */
    public static final Color MAROON = new Color(163, 38, 56);

    /** The color BrickRed. */
    public static final Color BRICK_RED = new Color(175, 38, 38);

    /** The color Red. */
    public static final Color RED = new Color(255, 0, 0);

    /** The color OrangeRed. */
    public static final Color ORANGE_RED = new Color(206, 0, 124);

    /** The color RubineRed. */
    public static final Color RUBINE_RED = new Color(204, 0, 160);

    /** The color WildStrawberry. */
    public static final Color WILDSTRAWBERRY = new Color(211, 5, 71);

    /** The color Salmon. */
    public static final Color SALMON = new Color(252, 140, 153);

    /** The color CarnationPink. */
    public static final Color CARNATION_PINK = new Color(237, 114, 170);

    /** The color Magenta. */
    public static final Color MAGENTA = new Color(219, 40, 165);

    /** The color VioletRed. */
    public static final Color VIOLET_RED = new Color(226, 40, 130);

    /** The color Rhodamine. */
    public static final Color RHODAMINE = new Color(234, 107, 191);

    /** The color Mulberry. */
    public static final Color MULBERRY = new Color(173, 0, 117);

    /** The color RedViolet. */
    public static final Color RED_VIOLET = new Color(170, 0, 102);

    /** The color Fuchsia. */
    public static final Color FUCHSIA = new Color(163, 5, 127);

    /** The color Lavender. */
    public static final Color LAVENDER = new Color(249, 147, 196);

    /** The color Thistle. */
    public static final Color THISTLE = new Color(232, 127, 201);

    /** The color Orchid. */
    public static final Color ORCHID = new Color(211, 107, 198);

    /** The color DarkOrchid. */
    public static final Color DARK_ORCHID = new Color(142, 71, 173);

    /** The color Purple. */
    public static final Color PURPLE = new Color(137, 79, 191);

    /** The color Plum. */
    public static final Color PLUM = new Color(81, 45, 68);

    /** The color Violet. */
    public static final Color VIOLET = new Color(48, 68, 181);

    /** The color RoyalPurple. */
    public static final Color ROYAL_PURPLE = new Color(89, 17, 142);

    /** The color BlueViolet. */
    public static final Color BLUE_VIOLET = new Color(53, 0, 109);

    /** The color Periwinkle. */
    public static final Color PERIWINKLE = new Color(147, 122, 204);

    /** The color CadetBlue. */
    public static final Color CADET_BLUE = new Color(94, 104, 196);

    /** The color CornflowerBlue. */
    public static final Color CORNFLOWER_BLUE = new Color(117, 178, 221);

    /** The color MidnightBlue. */
    public static final Color MIDNIGHT_BLUE = new Color(0, 79, 109);

    /** The color NavyBlue. */
    public static final Color NAVY_BLUE = new Color(0, 81, 186);

    /** The color RoyalBlue. */
    public static final Color ROYAL_BLUE = new Color(0, 132, 201);

    /** The color Blue. */
    public static final Color BLUE = new Color(0, 56, 168);

    /** The color Cerulean. */
    public static final Color CERULEAN = new Color(0, 198, 178);

    /** The color Cyan. */
    public static final Color CYAN = new Color(94, 221, 193);

    /** The color ProcessBlue. */
    public static final Color PROCESS_BLUE = new Color(71, 214, 193);

    /** The color SkyBlue. */
    public static final Color SKY_BLUE = new Color(81, 191, 226);

    /** The color Turquoise. */
    public static final Color TURQUOISE = new Color(0, 183, 198);

    /** The color TealBlue. */
    public static final Color TEAL_BLUE = new Color(0, 132, 142);

    /** The color Aquamarine. */
    public static final Color AQUAMARINE = new Color(0, 155, 170);

    /** The color BlueGreen. */
    public static final Color BLUE_GREEN = new Color(0, 158, 160);

    /** The color Emerald. */
    public static final Color EMERALD = new Color(0, 183, 96);

    /** The color JungleGreen. */
    public static final Color JUNGLE_GREEN = new Color(0, 119, 112);

    /** The color SeaGreen. */
    public static final Color SEA_GREEN = new Color(0, 175, 153);

    /** The color Green. */
    public static final Color GREEN = new Color(0, 255, 0);

    /** The color ForestGreen. */
    public static final Color FOREST_GREEN = new Color(0, 107, 63);

    /** The color PineGreen. */
    public static final Color PINE_GREEN = new Color(0, 102, 99);

    /** The color LimeGreen. */
    public static final Color LIME_GREEN = new Color(140, 214, 0);

    /** The color YellowGreen. */
    public static final Color YELLOW_GREEN = new Color(127, 186, 0);

    /** The color SpringGreen. */
    public static final Color SPRING_GREEN = new Color(204, 226, 38);

    /** The color OliveGreen. */
    public static final Color OLIVE_GREEN = new Color(135, 137, 5);

    /** The color RawSienna. */
    public static final Color RAWSIENNA = new Color(153, 89, 5);

    /** The color Sepia. */
    public static final Color SEPIA = new Color(99, 58, 17);

    /** The color Brown. */
    public static final Color BROWN = new Color(132, 63, 15);

    /** The color Tan. */
    public static final Color TAN = new Color(249, 114, 66);

    /** The color Gray. */
    public static final Color GRAY = new Color(102, 109, 112);

    /** The color Black. */
    public static final Color BLACK = new Color(0, 0, 0);

    /** The color GrayScale15. */
    public static final Color GRAYSCALE15 = new Color(15, 15, 15);

    /** The color GrayScale30. */
    public static final Color GRAYSCALE30 = new Color(30, 30, 30);

    /** The color GrayScale45. */
    public static final Color GRAYSCALE45 = new Color(45, 45, 45);

    /** The color GrayScale60. */
    public static final Color GRAYSCALE60 = new Color(60, 60, 60);

    /** The color GrayScale75. */
    public static final Color GRAYSCALE75 = new Color(75, 75, 75);

    /** The color GrayScale90. */
    public static final Color GRAYSCALE90 = new Color(90, 90, 90);

    /** The color GrayScale105. */
    public static final Color GRAYSCALE105 = new Color(105, 105, 105);

    /** The color GrayScale120. */
    public static final Color GRAYSCALE120 = new Color(120, 120, 120);

    /** The color GrayScale135. */
    public static final Color GRAYSCALE135 = new Color(135, 135, 135);

    /** The color GrayScale150. */
    public static final Color GRAYSCALE150 = new Color(150, 150, 150);

    /** The color GrayScale165. */
    public static final Color GRAYSCALE165 = new Color(165, 165, 165);

    /** The color GrayScale180. */
    public static final Color GRAYSCALE180 = new Color(180, 180, 180);

    /** The color GrayScale195. */
    public static final Color GRAYSCALE195 = new Color(195, 195, 195);

    /** The color GrayScale210. */
    public static final Color GRAYSCALE210 = new Color(210, 210, 210);

    /** The color GrayScale225. */
    public static final Color GRAYSCALE225 = new Color(225, 225, 225);

    /** The color GrayScale240. */
    public static final Color GRAYSCALE240 = new Color(240, 240, 240);

    /** The color White. */
    public static final Color WHITE = new Color(255, 255, 255);

    /** List containing all the colors of JaxoColor. */
    private static final Color[] COLOR_LIST =
    {
        GREEN_YELLOW, YELLOW, GOLDEN_ROD, DANDELION, APRICOT, PEACH, MELON,
        YELLOW_ORANGE, ORANGE, BURNT_ORANGE, BITTERSWEET, RED_ORANGE, MAHOGANY,
        MAROON, BRICK_RED, RED, ORANGE_RED, RUBINE_RED, WILDSTRAWBERRY, SALMON,
        CARNATION_PINK, MAGENTA, VIOLET_RED, RHODAMINE, MULBERRY, RED_VIOLET,
        FUCHSIA, LAVENDER, THISTLE, ORCHID, DARK_ORCHID, PURPLE, PLUM, VIOLET,
        ROYAL_PURPLE, BLUE_VIOLET, PERIWINKLE, CADET_BLUE, CORNFLOWER_BLUE,
        MIDNIGHT_BLUE, NAVY_BLUE, ROYAL_BLUE, BLUE, CERULEAN, CYAN,
        PROCESS_BLUE, SKY_BLUE, TURQUOISE, TEAL_BLUE, AQUAMARINE, BLUE_GREEN,
        EMERALD, JUNGLE_GREEN, SEA_GREEN, GREEN, FOREST_GREEN, PINE_GREEN,
        LIME_GREEN, YELLOW_GREEN, SPRING_GREEN, OLIVE_GREEN, RAWSIENNA, SEPIA,
        BROWN, TAN, GRAY, BLACK, GRAYSCALE15, GRAYSCALE30, GRAYSCALE45,
        GRAYSCALE60, GRAYSCALE75, GRAYSCALE90, GRAYSCALE105, GRAYSCALE120,
        GRAYSCALE135, GRAYSCALE150, GRAYSCALE165, GRAYSCALE180, GRAYSCALE195,
        GRAYSCALE210, GRAYSCALE225, GRAYSCALE240, WHITE
    };

    /** List containing the names of all the colors of JaxoColor.
     *  For <code>colordvi</code> colors, these are the english names as defined
     * by colordvi.sty.
     *  The names are at the same time the keys for the translation into other
     *  languages.
     */
    private static final String[] COLOR_NAME =
    {
        "GreenYellow", "Yellow", "Goldenrod", "Dandelion", "Apricot", "Peach",
        "Melon", "YellowOrange", "Orange", "BurntOrange", "Bittersweet",
        "RedOrange", "Mahogany", "Maroon", "BrickRed", "Red", "OrangeRed",
        "RubineRed", "WildStrawberry", "Salmon", "CarnationPink", "Magenta",
        "VioletRed", "Rhodamine", "Mulberry", "RedViolet", "Fuchsia",
        "Lavender", "Thistle", "Orchid", "DarkOrchid", "Purple", "Plum",
        "Violet", "RoyalPurple", "BlueViolet", "Periwinkle", "CadetBlue",
        "CornflowerBlue", "MidnightBlue", "NavyBlue", "RoyalBlue", "Blue",
        "Cerulean", "Cyan", "ProcessBlue", "SkyBlue", "Turquoise", "TealBlue",
        "Aquamarine", "BlueGreen", "Emerald", "JungleGreen", "SeaGreen",
        "Green", "ForestGreen", "PineGreen", "LimeGreen", "YellowGreen",
        "SpringGreen", "OliveGreen", "RawSienna", "Sepia", "Brown", "Tan",
        "Gray", "Black", "GrayScale15", "GrayScale30", "GrayScale45",
        "GrayScale60", "GrayScale75", "GrayScale90", "GrayScale105",
        "GrayScale120", "GrayScale135", "GrayScale150", "GrayScale165",
        "GrayScale180", "GrayScale195", "GrayScale210", "GrayScale225",
        "GrayScale240", "White"
    };

    // Maps colorName -> color, color -> colorName, color -> index
    // for the predefined colors
    private static final Map<String, Color> NAME_TO_COLOR;

    // Maps colorName -> color, color -> colorName, color -> index
    // for the predefined colors
    private static final Map<Color, String> COLOR_TO_NAME;

    // Maps colorName -> color, color -> colorName, color -> index
    // for the predefined colors
    private static final Map<Color, Integer> COLOR_TO_INDEX;
    private static final int FIRST_GRAYSCALE_INDEX;
    private static final int LAST_GRAYSCALE_INDEX;
    private static final String LATEX_COMMAND_BASE = "\\SetColor{";
    private static final String LATEX_COMMAND_CLOSING = "}";

    static {
        COLOR_TO_INDEX = new HashMap<Color, Integer>(COLOR_NAME.length);
        NAME_TO_COLOR = new HashMap<String, Color>(COLOR_NAME.length);
        COLOR_TO_NAME = new HashMap<Color, String>(COLOR_NAME.length);

        for (int i = 0; i < COLOR_NAME.length; i++) {
            NAME_TO_COLOR.put(COLOR_NAME[i], COLOR_LIST[i]);
            COLOR_TO_INDEX.put(COLOR_LIST[i], i);
            COLOR_TO_NAME.put(COLOR_LIST[i], COLOR_NAME[i]);
        }

        FIRST_GRAYSCALE_INDEX = getIndex(GRAYSCALE15);
        LAST_GRAYSCALE_INDEX = getIndex(GRAYSCALE240);
    }

    /** Empty private constructor to prevent the class from being
     * explicitly instantiated.
     */
    private JaxoColor() {
        // empty on purpose
    }

    ////////////////////////////////////////////////
    // Misc


    /** Checks whether the given color is a defined Color.
      * @param color A color.
      * @return True if color is one of the colors defined by this class.
      */
    public static boolean isDefinedColor(final Color color) {
        return COLOR_TO_NAME.containsKey(color);
    }

    /** Checks whether the given color is a defined Color in the given mode.
     * @param color A color.
     * @param mode the color mode.
     * @return True if color is defined.
     */
    public static boolean isDefinedColor(final Color color, final int mode) {
        if (mode == ALL_COLORS_MODE) {
            return true;
        } else if (mode == JAXO_COLORS_MODE) {
            return isDefinedColor(color);
        } else if (mode == JAXO_COLORS_NO_GRAYSCALES_MODE) {
            final Integer v = COLOR_TO_INDEX.get(color);

            return (v != null) && !isGrayScaleIndex(v.intValue());
        }

        return false;
    }

    /** Checks whether the given name is for a defined Color.
      * @param cName a color name
      * @return True if 'cName' is a name of one of the colors defined by this class.
      */
    public static boolean isDefinedColorName(final String cName) {
        return NAME_TO_COLOR.containsKey(cName);
    }

    /** Checks whether the given name is for a Color defined in the given mode.
     * @param cName a color name.
     * @param mode the color mode.
     * @return True if 'cName' is defined.
     */
    public static boolean isDefinedColorName(final String cName, final int mode) {
        final Color c = getColorOrNull(cName, mode);

        return (c != null) && isDefinedColor(c, mode);
    }

    ////////////////////////////////////////////////
    // Index-related code

    /** Returns the number of colors defined by JaxoColor.
     * @return The number of colors defined by JaxoColor.
     */
    public static int getColorCount() {
        return COLOR_LIST.length;
    }

    /** Returns the color at index i of the colorList.
     * @param i Index of the color to be returned
     * @return Color at index i.
     */
    public static Color getColor(final int i) {
        return COLOR_LIST[i];
    }

    /** Returns the name of the color at index i in the colorList.
     * @param i Index of the color whose name is to be returned.
     * @return Name of the color at index i.
     */
    public static String getColorName(final int i) {
        return COLOR_NAME[i];
    }

    /** Returns the index in the colorList of the color color.
     * @param color The color whose index is to be returned.
     * @return The index of the color color.
     */
    private static int getIndex(final Color color) {
        final Integer v = COLOR_TO_INDEX.get(color);

        return (v == null) ? getColorCount() // undocumented!
                           : v.intValue();
    }

    ////////////////////////////////////////////////
    // Default colors

    /** Returns the current default color.
     * @return Returns {@link #BLACK}.
     */
    public static Color getDefaultColor() {
        return JaxoColor.BLACK;
    }

    /** Returns the current default fill color for the given line color.
     * @param lineColor The line color.
     * @return Returns GRAYSCALE225 if the argument is BLACK,
     * GRAY otherwise.
     */
    public static Color getDefaultFillColor(final Color lineColor) {
        if (lineColor.equals(BLACK)) {
            return GRAYSCALE225;
        } else {
            return GRAY;
        }
    }

    ////////////////////////////////////////////////
    // Color/Name conversion

    /** Returns the color corresponding to the name cName,
     *   or 'null' if none.
     * @param cName The name of the color to be returned.
     * @return The color corresponding to the name cName,
     * or 'null' if none.
     */
    private static Color getColorOrNull(final String cName) {
        return NAME_TO_COLOR.get(cName);
    }

    /** Returns the color corresponding to the name cName,
     * or the default color if none.
     * @param cName The name of the color to be returned.
     * @return The color corresponding to the name cName.
     * @see #getDefaultColor()
     */
    public static Color getColor(final String cName) {
        final Color c = getColorOrNull(cName);

        return (c == null) ? getDefaultColor() : c;
    }

    /** Returns the color corresponding to the name cName, or the
     *  default color if none.
     * @param cName The name of the color to be returned.
     * @param mode The mode defining the color space
     * @return The color corresponding to the name cName.
     */
    public static Color getColor(final String cName, final int mode) {
        final Color c = getColorOrNull(cName, mode);

        return (c == null) ? getDefaultColor() : c;
    }

    /** Returns the color corresponding to the name cName, or null if none.
     * @param cName The name of the color to be returned.
     * @param mode The mode defining the color space
     * @return The color corresponding to the name cName.
     */
    private static Color getColorOrNull(final String cName, final int mode) {
        if (mode == ALL_COLORS_MODE) {
            try {
                // Careful: a color can be a defined Color even in
                // the ALL_COLORS_MODE case; only in the case it is not
                // should the rgb values be returned!
                Color color = getColorOrNull(cName);

                if (color == null) {
                    if (cName.charAt(0) == '#') {
                        color = Color.decode(cName);
                    } else {
                        color =
                            new Color(getRComponent(cName),
                                getGComponent(cName), getBComponent(cName));
                    }
                }
                return color;
            } catch (NumberFormatException e) {
                return null;
            }
        } else {
            final Color c = getColor(cName);
            if (mode == JAXO_COLORS_NO_GRAYSCALES_MODE && isGrayScale(c)) {
                return null;
            }
            return c;
        }
    }

    /** Returns the name of the closest defined color to 'color'.
     * @param color Color whose name is to be returned.
     * @return Name of the color color.
     */
    public static String getColorName(final Color color) {
        // Convert the color into a defined Color
        final Color closestColor =
            getClosestColorTo(color, JAXO_COLORS_MODE);

        return COLOR_TO_NAME.get(closestColor);
    }

    /** Returns the name of the closest defined color (in the mode)
     *  color.
     * @param color Color whose name is to be returned.
     * @param space defining the allowed colors.
     * @return Name of the color color.
     */
    public static String getColorName(final Color color, final int space) {
        if (space == ALL_COLORS_MODE) {
            // TODO: Why not check first if it is a colordvi color and
            // return the nicer name then?
            String clrName = "";
            clrName = "r" + color.getRed() + "g" + color.getGreen()
                    + "b" + color.getBlue();
            return clrName;
        } else {
            if (space == JAXO_COLORS_NO_GRAYSCALES_MODE && isGrayScale(color)) {
                return null;
            }
            return getColorName(color);
        }
    }

    /////////////////////////////////////////////////////////
    /// GrayScales

    /** Returns a string representation of a Color that gives the grayscale
     * of color, or sets it to '0.0' (black), if color is not a grayscale.
     * @param color The color whose grayscale value is to be returned.
     * @return The float grayness of the grayscale corresponding to color.
     */
    public static float getGrayScaleFloat(final Color color) {
        float result = 0.0f;

        // Convert the color into a defined Color
        final Color closestColor =
            getClosestColorTo(color, JAXO_COLORS_MODE);

        // TODO: needs better algorithm to find a grayscale?
        if (isDefinedGrayScale(closestColor) || closestColor.equals(WHITE)) {
            result =
                (float) ((int) ((1000 * (float) color.getRed()) / 255)) / 1000;
        }

        return result;
    }

    private static boolean isGrayScaleIndex(final int index) {
        return (FIRST_GRAYSCALE_INDEX <= index) && (
            index <= LAST_GRAYSCALE_INDEX
        );
    }

    /** Checks whether the 'color' is closest to one of the gray scales
     * defined in this class.
     * @param color A color.
     * @return True if color is a grayscale, false otherwise.
     */
    public static boolean isGrayScale(final Color color) {
        // Convert the color into a defined Color
        final Color closestColor =
            getClosestColorTo(color, JAXO_COLORS_MODE);

        return isDefinedGrayScale(closestColor);
    }

    /** Checks whether the given color is one of the gray scales
      * defined in this class, without using the getClosestColorTo.
      * to convert it into a defined Color (as we know it is)
      * @param color A color.
      * @return True if color is a grayscale, false otherwise.
      */
    private static boolean isDefinedGrayScale(final Color color) {
        return isGrayScaleIndex(getIndex(color));
    }

    /** Returns the grayscale that corresponds to the float value f.
     * or null if f is not between 0.f and 1.f
     * @param f A float between 0.f and 1.f
     * @return The gray scale corresponding to the float value f.
     */
    public static Color getGrayScaleColor(final float f) {
        Color grayScale = null;

        if ((f >= 0.f) && (f <= 1.f)) {
            final int offset = Math.round(17.f * f);
            grayScale = COLOR_LIST[getIndex(BLACK) + offset];
        }

        return grayScale;
    }

    //////////////////////////////////////////////
    // LaTeX

    /** Returns a String containing the LaTex color command
     * (as used by the package colordvi) to set the color.
     * @param color The color to be set in the LaTex command.
     * @return The String representation of the LaTex command.
     */
    public static String getLatexColorCommand(final Color color) {

        // Convert the color into a defined Color
        final Color closestColor =
            getClosestColorTo(color, JAXO_COLORS_MODE);

        String thisColor;
        // grayscale colors can only be used for filling,
        // for drawing we take gray
        if (isGrayScale(closestColor)) {
            thisColor = "Gray";
        } else {
            thisColor = getColorName(closestColor);
        }

        return LATEX_COMMAND_BASE.concat(thisColor.concat(LATEX_COMMAND_CLOSING));
    }

    //////////////////////////////////////////////
    // closest color

    /**
     * Return the Color closest (in the specified mode) to the given color.
     *
     * @param refcolor the color to be matched.
     * @param mode mode to use
     *
     * @return A Color in the color space of the given mode,
     *      or null if the given color was null.
     */
    public static Color getClosestColorTo(final Color refcolor, final int mode) {

        if (refcolor == null) {
            return null;
        }

        if (mode == ALL_COLORS_MODE) {
            return refcolor;
        }

        final int red = refcolor.getRed();
        final int green = refcolor.getGreen();
        final int blue = refcolor.getBlue();

        int minRGBdistanceSquared = 255 * 255 * 3;

        Color closest = JaxoColor.WHITE;

        final boolean grayScalesAllowed =
            mode != JAXO_COLORS_NO_GRAYSCALES_MODE;

        for (int i = 0; i < COLOR_LIST.length; i++) {
            final Color c = COLOR_LIST[i];

            if (grayScalesAllowed || !isGrayScaleIndex(i)) {
                final int curRGBdistanceSquared =
                    (int) Math.pow(c.getRed() - red, 2)
                    + (int) Math.pow(c.getGreen() - green, 2)
                    + (int) Math.pow(c.getBlue() - blue, 2);

                if (curRGBdistanceSquared == 0) {
                    closest = c;
                    break;
                } else if (curRGBdistanceSquared < minRGBdistanceSquared) {
                    minRGBdistanceSquared = curRGBdistanceSquared;
                    closest = c;
                }
            } else {
                // return GRAY for a grayscale in NO_GRAYSCALES_MODE
                return GRAY;
            }
        }
        return closest;
    }

    private static int getRComponent(final String cName) {
        String rcompstr;
        int rcompint;

        final int start = cName.indexOf('r');
        final int stop = cName.indexOf('g', start + 1);

        if ((start == -1) || (stop == -1)) {
            throw new NumberFormatException();
        }

        rcompstr = cName.substring(start + 1, stop);
        rcompint = Integer.parseInt(rcompstr);

        return rcompint;
    }

    private static int getGComponent(final String cName) {
        String gcompstr;
        int gcompint = 0;

        final int start = cName.indexOf('g');
        final int stop = cName.indexOf('b', start + 1);

        if ((start == -1) || (stop == -1)) {
            throw new NumberFormatException();
        }

        gcompstr = cName.substring(start + 1, stop);
        gcompint = Integer.parseInt(gcompstr);

        return gcompint;
    }

    private static int getBComponent(final String cName) {
        String bcompstr;
        int bcompint = 0;

        final int start = cName.indexOf('b');
        final int stop = cName.length();

        if ((start == -1) || (stop == -1)) {
            throw new NumberFormatException();
        }

        bcompstr = cName.substring(start + 1, stop);
        bcompint = Integer.parseInt(bcompstr);

        return bcompint;
    }
}
