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

import java.util.ArrayList;
import java.util.List;

import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.MeasureUnit;
import com.ibm.icu.util.NoUnit;
import com.ibm.icu.util.ULocale;

/**
 * A MicroPropsGenerator that multiplexes between different LongNameHandlers,
 * depending on the outputUnit.
 *
 * See processQuantity() for the input requirements.
 */
public class LongNameMultiplexer implements MicroPropsGenerator {
    /**
     * LongNameMultiplexer calls the parent MicroPropsGenerator itself,
     * receiving the MicroProps instance in use for this formatting pipeline.
     * Next it multiplexes between name handlers (fHandlers) which are not given
     * access to a parent. Consequently LongNameMultiplexer must give these
     * handlers the MicroProps instance.
     */
    public static interface ParentlessMicroPropsGenerator {
        public MicroProps processQuantityWithMicros(DecimalQuantity quantity, MicroProps micros);
    }

    private final MicroPropsGenerator fParent;

    private List<ParentlessMicroPropsGenerator> fHandlers;

    // Each MeasureUnit corresponds to the same-index MicroPropsGenerator
    // pointed to in fHandlers.
    private List<MeasureUnit> fMeasureUnits;

    public LongNameMultiplexer(MicroPropsGenerator fParent) {
        this.fParent = fParent;
    }

    // Produces a multiplexer for LongNameHandlers, one for each unit in
    // `units`. An individual unit might be a mixed unit.
    public static LongNameMultiplexer forMeasureUnits(ULocale locale,
                                                      List<MeasureUnit> units,
                                                      NumberFormatter.UnitWidth width,
                                                      PluralRules rules,
                                                      MicroPropsGenerator parent) {
        LongNameMultiplexer result = new LongNameMultiplexer(parent);

        assert (units.size() > 0);

        result.fMeasureUnits = new ArrayList<>();
        result.fHandlers = new ArrayList<>();


        for (int i = 0; i < units.size(); i++) {
            MeasureUnit unit = units.get(i);
            result.fMeasureUnits.add(unit);
            if (unit.getComplexity() == MeasureUnit.Complexity.MIXED) {
                MixedUnitLongNameHandler mlnh = MixedUnitLongNameHandler
                        .forMeasureUnit(locale, unit, width, rules, null);
                result.fHandlers.add(mlnh);
            } else {
                LongNameHandler lnh = LongNameHandler
                        .forMeasureUnit(locale, unit, NoUnit.BASE, width, rules, null);
                result.fHandlers.add(lnh);
            }
        }

        return result;
    }

    // The output unit must be provided via `micros.outputUnit`, it must match
    // one of the units provided to the factory function.
    @Override
    public MicroProps processQuantity(DecimalQuantity quantity) {
        // We call parent->processQuantity() from the Multiplexer, instead of
        // letting LongNameHandler handle it: we don't know which LongNameHandler to
        // call until we've called the parent!
        MicroProps micros = this.fParent.processQuantity(quantity);

        // Call the correct LongNameHandler based on outputUnit
        for (int i = 0; i < this.fHandlers.size(); i++) {
            if (fMeasureUnits.get(i).equals(micros.outputUnit)) {
                ParentlessMicroPropsGenerator handler = fHandlers.get(i);
                return handler.processQuantityWithMicros(quantity, micros);
            }
        }
        throw new AssertionError
                (" We shouldn't receive any outputUnit for which we haven't already got a LongNameHandler");
    }
}
