 /*
  * RHQ Management Platform
  * Copyright (C) 2005-2008 Red Hat, Inc.
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, version 2, as
  * published by the Free Software Foundation, and/or the GNU Lesser
  * General Public License, version 2.1, also as published by the Free
  * Software Foundation.
  *
  * This program 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 and the GNU Lesser General Public License
  * for more details.
  *
  * You should have received a copy of the GNU General Public License
  * and the GNU Lesser General Public License along with this program;
  * if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
package org.rhq.core.clientapi.util.units;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Locale;
import org.rhq.core.clientapi.util.ArrayUtil;
import static org.rhq.core.clientapi.util.units.ScaleConstants.SCALE_NONE;
import static org.rhq.core.clientapi.util.units.UnitsConstants.UNIT_BITS;
import static org.rhq.core.clientapi.util.units.UnitsConstants.UNIT_BYTES;
import static org.rhq.core.clientapi.util.units.UnitsConstants.UNIT_DATE;
import static org.rhq.core.clientapi.util.units.UnitsConstants.UNIT_DURATION;
import static org.rhq.core.clientapi.util.units.UnitsConstants.UNIT_NONE;
import static org.rhq.core.clientapi.util.units.UnitsConstants.UNIT_PERCENTAGE;
import static org.rhq.core.clientapi.util.units.UnitsConstants.UNIT_TEMPERATURE;

class UnitsUtil {
    static final BigDecimal FACT_NONE = new BigDecimal(1);

    // Binary scaling factors
    static final BigDecimal FACT_KILO_BIN = new BigDecimal(1 << 10);
    static final BigDecimal FACT_MEGA_BIN = FACT_KILO_BIN.multiply(FACT_KILO_BIN);
    static final BigDecimal FACT_GIGA_BIN = FACT_MEGA_BIN.multiply(FACT_KILO_BIN);
    static final BigDecimal FACT_TERA_BIN = FACT_GIGA_BIN.multiply(FACT_KILO_BIN);
    static final BigDecimal FACT_PETA_BIN = FACT_TERA_BIN.multiply(FACT_KILO_BIN);

    // The following factors are how to convert a number in FACT_* to
    // nanoseconds.
    static final BigDecimal FACT_NANOS = new BigDecimal(1);
    static final BigDecimal FACT_MICROS = new BigDecimal(1000);
    static final BigDecimal FACT_MILLIS = new BigDecimal(1000000);
    static final BigDecimal FACT_JIFFYS = new BigDecimal(10000000);
    static final BigDecimal FACT_SECS = FACT_MILLIS.multiply(new BigDecimal(1000));
    static final BigDecimal FACT_MINS = FACT_SECS.multiply(new BigDecimal(60));
    static final BigDecimal FACT_HOURS = FACT_MINS.multiply(new BigDecimal(60));
    static final BigDecimal FACT_DAYS = FACT_HOURS.multiply(new BigDecimal(24));
    static final BigDecimal FACT_WEEKS = FACT_DAYS.multiply(new BigDecimal(7));
    static final BigDecimal FACT_YEARS = FACT_DAYS.multiply(new BigDecimal(365));

    static void checkValidScaleForUnits(UnitsConstants unit, ScaleConstants scale) {
        EnumSet<ScaleConstants> weightSet = ScaleConstants.getBinaryScaleSet();
        EnumSet<ScaleConstants> timeSet = ScaleConstants.getTimeSet();
        EnumSet<ScaleConstants> temperatureSet = ScaleConstants.getTemperatureSet();

        //       if(!(scale == SCALE_NONE ||
        //                ((unit == UNIT_BYTES || unit == UNIT_BITS) &&
        //                 scale >= SCALE_KILO && scale <= SCALE_PETA)
        //              ||
        //                ((unit == UNIT_DURATION || unit == UNIT_DATE) &&
        //                 scale >= SCALE_YEAR && scale <= SCALE_NANO)
        //              ||
        //                (unit == UNIT_PERCENTAGE && scale != SCALE_NONE) ||
        //                (unit == UNIT_NONE && scale != SCALE_NONE)))

        if (!((scale == SCALE_NONE) || (((unit == UNIT_BYTES) || (unit == UNIT_BITS)) && weightSet.contains(scale))
            || (((unit == UNIT_DURATION) || (unit == UNIT_DATE)) && timeSet.contains(scale))
            || (((unit == UNIT_TEMPERATURE)) && temperatureSet.contains(scale))
            || ((unit == UNIT_PERCENTAGE) && (scale != SCALE_NONE)) || ((unit == UNIT_NONE) && (scale != SCALE_NONE)))) {
            throw new IllegalArgumentException("Scale is not valid for the " + "specified units");
        }
    }

    /**
     * Find the index of the first character which is not a number.
     *
     * @return The index of the first character which is not a number. If the entire value is a number, then -1 is
     *         returned
     */
    static int findNonNumberIdx(String val, NumberFormat fmt) {
        ParsePosition pos = new ParsePosition(0);

        fmt.parse(val, pos);
        if (pos.getIndex() == val.length()) {
            return -1;
        }

        return pos.getIndex();
    }

    /**
     * Get a NumberFormat which shows enough significant figures, such that each value passed in is distinguishable from
     * the next. (if they are not the same value)
     */
    public static NumberFormat getNumberFormat(double[] inValues, Locale locale) {
        NumberFormat res;
        String[] sValues;
        double[] values;
        int numDigits;

        res = NumberFormat.getInstance(locale);
        values = new double[inValues.length];
        System.arraycopy(inValues, 0, values, 0, inValues.length);
        Arrays.sort(values);
        values = ArrayUtil.uniq(values);

        if (values.length < 2) {
            res.setMinimumFractionDigits(1);
            res.setMaximumFractionDigits(1);
            return res;
        }

        /**
         * Find a NumberFormat, such that the numbers can all be formatted
         * uniquely.  We have to do this iteratively, checking all the
         * values, since the NumberFormat rounding can cause multiple
         * unique values to the same one.  This kinda sux, since it's
         * slow, but not much we can do here.
         */
        numDigits = 0;
        sValues = new String[values.length];
        while (numDigits < 9) {
            res.setMinimumFractionDigits(numDigits);
            res.setMaximumFractionDigits(numDigits);

            for (int i = 0; i < sValues.length; i++) {
                sValues[i] = res.format(values[i]);
            }

            Arrays.sort(sValues);
            if (ArrayUtil.isUniq(sValues) == false) {
                numDigits++;
            } else {
                break;
            }
        }

        return res;
    }

    public static int getUniqueDigits(double[] inValues, Locale locale) {
        return getNumberFormat(inValues, locale).getMaximumFractionDigits();
    }
}