// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
package com.ibm.icu.number;

import java.util.Locale;

import com.ibm.icu.util.ULocale;

/**
 * The main entrypoint to the formatting of ranges of numbers, including currencies and other units of measurement.
 * <p>
 * Usage example:
 * <pre>
 * NumberRangeFormatter.with()
 *         .identityFallback(RangeIdentityFallback.APPROXIMATELY_OR_SINGLE_VALUE)
 *         .numberFormatterFirst(NumberFormatter.with().unit(MeasureUnit.METER))
 *         .numberFormatterSecond(NumberFormatter.with().unit(MeasureUnit.KILOMETER))
 *         .locale(ULocale.UK)
 *         .formatRange(750, 1.2)
 *         .toString();
 * // => "750 m - 1.2 km"
 * </pre>
 * <p>
 * Like NumberFormatter, NumberRangeFormatter instances (i.e., LocalizedNumberRangeFormatter
 * and UnlocalizedNumberRangeFormatter) are immutable and thread-safe. This API is based on the
 * <em>fluent</em> design pattern popularized by libraries such as Google's Guava.
 *
 * @author sffc
 * @stable ICU 63
 * @see NumberFormatter
 */
public abstract class NumberRangeFormatter {

    /**
     * Defines how to merge fields that are identical across the range sign.
     *
     * @stable ICU 63
     * @see NumberRangeFormatter
     */
    public enum RangeCollapse {
        /**
         * Use locale data and heuristics to determine how much of the string to collapse. Could end up collapsing none,
         * some, or all repeated pieces in a locale-sensitive way.
         * <p>
         * The heuristics used for this option are subject to change over time.
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        AUTO,

        /**
         * Do not collapse any part of the number. Example: "3.2 thousand kilograms – 5.3 thousand kilograms"
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        NONE,

        /**
         * Collapse the unit part of the number, but not the notation, if present. Example: "3.2 thousand – 5.3 thousand
         * kilograms"
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        UNIT,

        /**
         * Collapse any field that is equal across the range sign. May introduce ambiguity on the magnitude of the
         * number. Example: "3.2 – 5.3 thousand kilograms"
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        ALL
    }

    /**
     * Defines the behavior when the two numbers in the range are identical after rounding. To programmatically detect
     * when the identity fallback is used, compare the lower and upper BigDecimals via FormattedNumber.
     *
     * @stable ICU 63
     * @see NumberRangeFormatter
     */
    public static enum RangeIdentityFallback {
        /**
         * Show the number as a single value rather than a range. Example: "$5"
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        SINGLE_VALUE,

        /**
         * Show the number using a locale-sensitive approximation pattern. If the numbers were the same before rounding,
         * show the single value. Example: "~$5" or "$5"
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        APPROXIMATELY_OR_SINGLE_VALUE,

        /**
         * Show the number using a locale-sensitive approximation pattern. Use the range pattern always, even if the
         * inputs are the same. Example: "~$5"
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        APPROXIMATELY,

        /**
         * Show the number as the range of two equal values. Use the range pattern always, even if the inputs are the
         * same. Example (with RangeCollapse.NONE): "$5 – $5"
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        RANGE
    }

    /**
     * Used in the result class FormattedNumberRange to indicate to the user whether the numbers formatted in the range
     * were equal or not, and whether or not the identity fallback was applied.
     *
     * @stable ICU 63
     * @see NumberRangeFormatter
     */
    public static enum RangeIdentityResult {
        /**
         * Used to indicate that the two numbers in the range were equal, even before any rounding rules were applied.
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        EQUAL_BEFORE_ROUNDING,

        /**
         * Used to indicate that the two numbers in the range were equal, but only after rounding rules were applied.
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        EQUAL_AFTER_ROUNDING,

        /**
         * Used to indicate that the two numbers in the range were not equal, even after rounding rules were applied.
         *
         * @stable ICU 63
         * @see NumberRangeFormatter
         */
        NOT_EQUAL
    }

    private static final UnlocalizedNumberRangeFormatter BASE = new UnlocalizedNumberRangeFormatter();

    /**
     * Call this method at the beginning of a NumberRangeFormatter fluent chain in which the locale is not currently
     * known at the call site.
     *
     * @return An {@link UnlocalizedNumberRangeFormatter}, to be used for chaining.
     * @stable ICU 63
     */
    public static UnlocalizedNumberRangeFormatter with() {
        return BASE;
    }

    /**
     * Call this method at the beginning of a NumberRangeFormatter fluent chain in which the locale is known at the call
     * site.
     *
     * @param locale
     *            The locale from which to load formats and symbols for number range formatting.
     * @return A {@link LocalizedNumberRangeFormatter}, to be used for chaining.
     * @stable ICU 63
     */
    public static LocalizedNumberRangeFormatter withLocale(Locale locale) {
        return BASE.locale(locale);
    }

    /**
     * Call this method at the beginning of a NumberRangeFormatter fluent chain in which the locale is known at the call
     * site.
     *
     * @param locale
     *            The locale from which to load formats and symbols for number range formatting.
     * @return A {@link LocalizedNumberRangeFormatter}, to be used for chaining.
     * @stable ICU 63
     */
    public static LocalizedNumberRangeFormatter withLocale(ULocale locale) {
        return BASE.locale(locale);
    }

    /**
     * Private constructor - this class is not designed for instantiation
     */
    private NumberRangeFormatter() {
    }
}
