import { RepeatAttribute } from './repeatAttribute';
import { BindingEngine } from '../binding/exported';
import { IListener } from '../interface/exported';
import { ArrayObserverHandler } from '../binding/array/arrayObserverHandler';
import { PropertyObserverHandler } from '../binding/property/propertyObserverHandler';


/***************************************************************
 *  Helpers for repeat for attribute when array changes
 *  ArrayMethodCallHandler & ArrayPropertyChange & PropertyChangeSimple
 ***************************************************************/



/**
 *  Handles events/methods on array, like push/splice etc
 *
 */
export class ArrayMethodCallHandler implements IListener {
    public name: string;
    public caller: ArrayObserverHandler | PropertyObserverHandler;



    constructor(
        private repeat: RepeatAttribute
    ) {
        this.name = 'repeat' + this.repeat.value;
    }



    public call(events: any) {

        let sort = true;
        events.forEach((event: any) => {
            switch (event.event) {
                case 'push':
                    let i = 0;
                    while (i < event.args.length) {
                        this.repeat.push(event.args[i]);
                        i++;
                    }
                    this.repeat.updateInternals();
                    break;
                case 'reverse':
                case 'sort':
                    if (sort) {
                        sort = false;
                        const array = this.repeat.$array; // BindingEngine.evaluateExpression(this.repeat.arrayExpression, this.repeat.$bindingContext);
                        if (array) {
                            array.forEach((ctx: any, i: number) => {
                                this.repeat.templateArray[i].ctx.$context[this.repeat.rowInstanceName] = ctx;
                            });
                        }
                    }
                    break;
                case 'shift':
                    this.repeat.shift();
                    break;
                case 'pop':
                    this.repeat.pop();
                    break;
                case 'splice':
                    this.repeat.splice(event.args);
                    break;
                default:
            }
        });



    }
}



/**
 *  Handles changes to array when its replaced
 *
 */
export class ArrayPropertyChange implements IListener {
    public name: string;
    public caller: ArrayObserverHandler | PropertyObserverHandler;

    constructor(
        private repeat: RepeatAttribute,
        public expression?: boolean
    ) {
        this.name = 'repeat' + this.repeat.value;
    }



    public call() {
        if (this.repeat.isAttached) {

            const array = BindingEngine.evaluateExpression(this.repeat.arrayExpression, this.repeat.$bindingContext);

            if (Array.isArray(array) && this.repeat.templateArray.length !== array.length) {
                this.repeat.$array = array;
                if (this.repeat.templateArray.length !== 0 && array.length !== 0) {

                    if (this.repeat.templateArray.length > array.length) {

                        this.repeat.loopBinded(true, this.repeat.templateArray.length - array.length, 0);
                    } else {
                        this.repeat.loopBinded(true, 0, array.length - this.repeat.templateArray.length);
                    }
                    BindingEngine.unSubscribeClassArray(this.repeat.$bindingContext, this.repeat.arrayMethodCallHandler);
                    this.repeat.subscribeArray();


                } else {
                    this.repeat.clearTemplateArray();
                    let array = BindingEngine.evaluateExpression(this.repeat.arrayExpression, this.repeat.$bindingContext);
                    if (Array.isArray(array)) {
                        array.forEach((ctx: any) => {
                            this.repeat.push(ctx);
                        });

                        BindingEngine.unSubscribeClassArray(this.repeat.$bindingContext, this.repeat.arrayMethodCallHandler);
                        this.repeat.subscribeArray();

                        array = null;
                    }
                }
            } else {
                if (Array.isArray(array)) {
                    // array is same size, we just need to replace data
                    this.repeat.$array = array;
                    this.repeat.loopBinded();
                    BindingEngine.unSubscribeClassArray(this.repeat.$bindingContext, this.repeat.arrayMethodCallHandler);
                    if (array) {
                        this.repeat.subscribeArray();
                    }
                } else {
                    // array is null/undefined, we need to clear array
                    this.repeat.$array = Array.isArray(array) ? array : [];
                    this.repeat.clearTemplateArray();
                    BindingEngine.unSubscribeClassArray(this.repeat.$bindingContext, this.repeat.arrayMethodCallHandler);
                    if (array) {
                        this.repeat.subscribeArray();
                    }
                }

            }
        }
    }
}



/**
 *  Handles changes to property(not array when this is used) when its replaced  (simple array, not object)
 *
 */
export class PropertyChangeSimple implements IListener {
    public name: string;
    public caller: ArrayObserverHandler | PropertyObserverHandler;



    constructor(private repeat: RepeatAttribute) {
        this.name = 'repeat' + this.repeat.value;
    }



    public call(newValue: any) {
        if (this.repeat.isAttached) {
            if (this.repeat.arrayType === 'string') {
                // TODO: this is slow if sub router and many
                // I think this will ever be used..much
                // need to think about implementing this better like numbers
                const stringLength = typeof newValue === 'string' ? newValue.length : 0;
                if (this.repeat.templateArray.length !== stringLength) {
                    this.repeat.clearTemplateArray();
                    for (let i = 0; i < stringLength; i++) {
                        this.repeat.push(newValue[i]);
                    }
                }
            }
            if (this.repeat.arrayType === 'number') {

                if (typeof newValue !== 'number') {
                    console.warn('repeat not number:', newValue);
                    newValue = 0;
                }
                if (this.repeat.templateArray.length !== newValue) {
                    if (this.repeat.templateArray.length < newValue) {
                        this.repeat.loopArrayNumber(true, 0, newValue - this.repeat.templateArray.length);
                    } else {
                        this.repeat.loopArrayNumber(true, this.repeat.templateArray.length - newValue, 0);
                    }
                }
            }
        }
    }
}

