/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.javatest.tool;

import java.awt.Color;
import com.sun.javatest.Status;
import com.sun.javatest.util.I18NResourceBundle;

/**
 * Utility class to get general internationalization properties and
 * perform general transformations.
 */
public class I18NUtils {
    /**
     * Get the base color for a test status.
     * @param status the status for which the color is required: one of
     * {@link #PASSED}, {@link #FAILED}, {@link #ERROR}, {@link #NOT_RUN}
     * @return the base color for the specified test status
     */
    public static Color getStatusColor(int status) {
        return getColorForState(status);
    }

    /**
     * Get the color for a bar for a test status.
     * @param status the status for which the color is required: one of
     * {@link #PASSED}, {@link #FAILED}, {@link #ERROR}, {@link #NOT_RUN}
     * @return the color for a bar for the specified test status
     */
    public static Color getStatusBarColor(int status) {
        return lighter(getColorForState(status));
    }

    /**
     * Get the localized status string for a particular test status.
     * @param status the status for which the color is required: one of
     * {@link #PASSED}, {@link #FAILED}, {@link #ERROR}, {@link #NOT_RUN}
     * @return the color for a bar for the specified test status
     * @see com.sun.javatest.Status
     */
    public static String getStatusString(int status) {
        if (STATUS_STRINGS == null)
            loadStatusStrings();

        if (status < STATUS_STRINGS.length)
            return STATUS_STRINGS[status];
        else
            return i18n.getString("i18n.unknown");
    }

    /**
     * Get localized version of the message string.
     * This includes a localized version of the status (e.g. "Passed") and
     * the raw status message.
     * @param status The status object for format.  May not be null.
     * @return A formatted, internationalized string representation of the
     *         status object (state and reason).
     * @see com.sun.javatest.Status#getReason
     */
    public static String getStatusMessage(Status status) {
        if (STATUS_STRINGS == null)
            loadStatusStrings();

        return STATUS_STRINGS[status.getType()] + " " +
                status.getReason();
    }

    static Color getColorForState(int state) {
        switch (state) {
        case PASSED:
            return passedColor;

        case FAILED:
            return failedColor;

        case ERROR:
            return errorColor;

        case NOT_RUN:
            return notRunColor;

        case FILTERED_OUT:
            return filteredOutColor;

        default:
            return null;
        }
    }

    private static void loadStatusStrings() {
        synchronized (I18NUtils.class) {
            if (STATUS_STRINGS == null) {
                STATUS_STRINGS = new String[NUM_STATES];

                for (int i = 0; i < NUM_STATES; i++)
                    STATUS_STRINGS[i] = i18n.getString("i18n.status" + i);
            }
        }
    }

    /**
     * Create a color derived from the given color, but lighter.
     * This is currently done by decreasing it's saturation and brightness.
     * @param c The color to lighten.
     * @return The derived color.
     */
    public static Color lighter(Color c) {
        float[] hsb = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
        hsb[1] = hsb[1] * 0.75f;                // reduce saturation by 25%
        hsb[2] = Math.min(hsb[2] * 1.1f, 1.0f); // increase brightness by 10%
        return new Color(Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]));
    }

    private static Color getI18NColor(String key, int deflt) {
        String value = i18n.getString(key + ".clr");
        try {
            if (value != null)
                return Color.decode(value);
        }
        catch (Exception e) {
            // ignore
        }
        return new Color(deflt);
    }

    /**
     * This is for internal use to read startup color prefs.
     * Will return the color specifed by the color's system property,
     * previous preference, or as defined in the resource bundle.  The
     * backup value will be used if a value cannot be located in any of
     * those three locations.  If read from system properties, the pref
     * key is prefixed by <code>javatest.</code>.
     * @param pref The system property and preferences key for the color value.
     * @param bundle The resource bundle key for the colors value.
     * @param syncPrefs Write system property value to the preferences if
     *        present.
     * @param backup The color to use if no color settings are available.
     */
    private static Color getPreferredColor(String pref, String bundle,
                                           int backup, boolean syncPrefs) {
        // read colors from system props and write into prefs
        // else read from prefs
        // else use defaults from the bundle
        Color result = null;

        // read from system properties
        result = Color.getColor("javatest." + pref);
        if (result != null) {
            if (syncPrefs) {
                Preferences prefs = Preferences.access();
                prefs.setPreference(pref, Integer.toString(result.getRGB()) );
            }

            return result;
        }

        // read from existing prefs
        Preferences prefs = Preferences.access();
        String val = prefs.getPreference(pref);

        if (val != null) {
            try {
                result = Color.decode(val);
            }
            catch (NumberFormatException e) {
                // XXX log error somehow?
                result = null;
            }   // catch

            // success, otherwise fall through
            if (result != null)
                return result;
        }

        result = getI18NColor(bundle, backup);
        return result;
    }

    private static I18NResourceBundle i18n =
        I18NResourceBundle.getBundleForClass(I18NUtils.class);
    private static Color passedColor;
    private static Color failedColor;
    private static Color errorColor;
    private static Color notRunColor;
    private static Color filteredOutColor;

    private static final String PASS_COLOR_PREF = "color.passed";
    private static final String FAIL_COLOR_PREF = "color.failed";
    private static final String ERR_COLOR_PREF = "color.error";
    private static final String NOTRUN_COLOR_PREF = "color.notrun";
    private static final String FILTERED_COLOR_PREF = "color.filter";

    static {
        passedColor = getPreferredColor(PASS_COLOR_PREF, "i18n.passed",
                                        0x00ff00, true);
        failedColor = getPreferredColor(FAIL_COLOR_PREF, "i18n.failed",
                                        0xff0000, true);
        errorColor = getPreferredColor(ERR_COLOR_PREF, "i18n.error",
                                        0x3f3fff, true);
        notRunColor = getPreferredColor(NOTRUN_COLOR_PREF, "i18n.notRun",
                                        0xffffff, true);
        filteredOutColor = getPreferredColor(FILTERED_COLOR_PREF,
                                        "i18n.filtered",
                                        0xa999999, true);
    }

    /**
     * A convenience redefinition of {@link Status#PASSED Status.PASSED}.
     */
    public static final int PASSED = Status.PASSED;

    /**
     * A convenience redefinition of {@link Status#FAILED Status.FAILED}.
     */
    public static final int FAILED = Status.FAILED;

    /**
     * A convenience redefinition of {@link Status#ERROR Status.ERROR}.
     */
    public static final int ERROR  = Status.ERROR;

    /**
     * A convenience redefinition of {@link Status#NOT_RUN Status.NOT_RUN}.
     */
    public static final int NOT_RUN = Status.NOT_RUN;

    /**
     * A constant indicating that an icon should be represented as "filtered out".
     */
    public static final int FILTERED_OUT = Status.NUM_STATES;

    /**
     * A constant indicating the number of different value "state" values.
     */
    public static final int NUM_STATES = FILTERED_OUT + 1;

    /**
     * Localized status strings.
     */
    private static String[] STATUS_STRINGS;
}
