import is from '@pilotlab/is';
import { IAnimationEaseFunction } from '@pilotlab/animation';
import { Signal } from '@pilotlab/signals';
import RangeScale from './rangeScale';
import RangeScaleBase from './rangeScaleBase';


export class RangeScaleManager extends RangeScaleBase {
    /**
     * Provides methods to scale between an input range and an output range.
     * Multiple output ranges can be added and scaled to a single input range.
     * @param inputMin
     * @param inputMax
     * @param ease - An easing function can be provided to allow smooth modulated scaling from one range to the other.
     */
    constructor(inputMin:number = 0, inputMax:number = 1, ease?:IAnimationEaseFunction) { super(inputMin, inputMax, ease); }


    /*--------------------------------------------------------------------*
     START: Properties
     *--------------------------------------------------------------------*/
    get outputRanges():Map<string, RangeScale> { return this._outputRanges; }
    private _outputRanges:Map<string, RangeScale> = new Map<string, RangeScale>();


    /*--------------------------------------------------------------------*
     START: Signals
     *--------------------------------------------------------------------*/
    updated:Signal<number> = new Signal<number>();


    /*--------------------------------------------------------------------*
     START: Public Methods
     *--------------------------------------------------------------------*/
    addOutputRange(name:string, outputMin:number, outputMax:number):RangeScale {
        let interpolatorValue:RangeScale = new RangeScale(
            this.p_inputMin, this.p_inputMax, outputMin, outputMax, this.p_ease
        );
        this._outputRanges.set(name, interpolatorValue);
        return interpolatorValue;
    }


    update(inputValue:number, isValueNormalized:boolean = false):void {
        //----- Make sure we have a raw (not normalized or constrained) input value to work with.
        if (isValueNormalized) inputValue = this.unnormalizeInput(inputValue, false);
        if (inputValue === this.p_lastInputRaw) return;
        this.p_lastInputRaw = inputValue;
        this.p_lastInputNormalized = this.normalizeInput(inputValue);
        this.p_checkInputPreviousRaw(inputValue);

        //----- Update all the managed range scalers.
        this._outputRanges.forEach((scalerValues:RangeScale, name:string):boolean => {
            scalerValues.output(inputValue, false, false, false, false);
            return true;
        });

        this.updated.dispatch(this.progress);
    }


    output(
        name:string,
        isConstrain:boolean = true,
        isNormalizeReturnValue:boolean = false,
        isInvertReturnValue:boolean = false
    ):number {
        let scaler:RangeScale = this._outputRanges.get(name);
        if (is.empty(scaler)) return null;

        let returnValue:number = scaler.normalizeOutput(scaler.value, isConstrain, isInvertReturnValue);

        if (isNormalizeReturnValue) return returnValue;
        else return scaler.unnormalizeOutput(returnValue, false, false);
    }
} // End of class


export default RangeScaleManager;
