import is from '@pilotlab/is';
import Numbers from '@pilotlab/numbers';
import {
    Animation,
    IAnimation,
    IAnimationEventArgs,
    IAnimationEaseFunction,
    ISpeed
} from '@pilotlab/animation';
import { NodeType } from '@pilotlab/nodes';
import Attribute from '../attribute';
import AttributeBase from '../attributeBase';
import Attributes from '../attributes';
import AttributeRoot from '../attributeRoot';
import { AttributeChangeActions, DataType } from '../attributeEnums';
import AttributeChangeOptions from '../attributeChangeOptions';
import IAttributeChangeOptions from '../interfaces/iAttributeChangeOptions';
import IAttributeSetReturn from '../interfaces/iAttributeSetReturn';


export abstract class AttributeNumberBase
extends AttributeBase<number, AttributeNumberBase, Attributes, AttributeRoot> {
    protected constructor(
        value?:any,
        dataType?:(DataType | string),
        label?:string,
        key?:string,
        isInitialize:boolean = true
    ) {
        super(Attribute.create, value, dataType, label, key, NodeType.BASIC, isInitialize);
        this.p_formatter = (value:number) => { return Numbers.round(value, 4); };
    }


    /*====================================================================*
    START: Methods
    *====================================================================*/
    protected p_set(value:any, changeOptions:IAttributeChangeOptions, returnValue:IAttributeSetReturn):void {
        /// If we're running an animation, interrupt it.
        if (is.notEmpty(this.p_animation)) {
            this.p_animation.interrupt();
            this.p_animation = null;
        }

        /// Run this function where we simply need to set the value and update the returnValue.
        const setValue = ():void => {
            this.p_value = value;
            this.p_valueTarget = value;
            returnValue.isChanged = true;
            this.doChanged(changeOptions.action);
        };

        if (is.notEmpty(changeOptions.durationSpeed) && changeOptions.durationSpeed !== 0) {
            /// A duration or speed value was passed,
            /// so we may need to set up an animation.
            let duration:number = Animation.getDuration(changeOptions.durationSpeed);

            if (duration > 0) {
                if (this.p_value !== value) {
                    let easeFinal:IAnimationEaseFunction = Animation.validateSpeed(changeOptions.durationSpeed).ease;

                    this.p_valueTarget = value;
                    returnValue.animation.animations.clear();
                    returnValue.isChanged = true;

                    /// We need to animate
                    const animation:IAnimation = Animation.go(this.p_value, value, duration, easeFinal, changeOptions.repeatCount, this.animationKey);
                    animation.ticked.listen((args:IAnimationEventArgs) => {
                        this.p_value = args.values[0].current;
                        const action:AttributeChangeActions = changeOptions.action === AttributeChangeActions.SAVE
                            ? AttributeChangeActions.SIGNAL_CHANGE
                            : changeOptions.action;
                        this.doChanged(action);
                    }, this);
                    animation.completed.listenOnce(() => {
                        this.p_value = value;
                        this.doChanged(changeOptions.action);
                    });
                    returnValue.animation.animations.add(animation);
                    this.p_animation = returnValue.animation;
                } else if (changeOptions.action === AttributeChangeActions.SAVE) {
                    this.doChanged(changeOptions.action);
                }
            } else setValue();
        } else setValue();
    }


    go(
        target:number,
        durationSpeed?:(number | ISpeed),
        ease?:IAnimationEaseFunction,
        repeatCount:number = 0
    ):IAttributeSetReturn {
        const options:IAttributeChangeOptions = new AttributeChangeOptions(AttributeChangeActions.SIGNAL_CHANGE, durationSpeed, ease);
        options.repeatCount = repeatCount;
        return this.set(target, options);
    }
} // End class


export default AttributeNumberBase;
