import { ClassPropertyObserver } from './classPropertyObserver';
import { Cache } from '../../utils/exported';
import { IBindingContext } from '../../interface/exported';

// helper class (its faster this way)
const emptyObject = class {
    // nothing
};


/*
 * creates a observer for property on class/object
 * classRef.__observer[this.keyBlock]
 *
 */
export class ClassPropertyObserverCreator {
    // private static curClass: any;
    private static classRef: any;
    private static keyParts: any;
    private static keyNo: any;
    private static keyBlock: any;



    /**
     * create
     *
     */
    public static create(_class: IBindingContext, observerKey: string, caller: any) {

        this.classRef = _class && _class.$context;
        this.keyParts = Cache.keyMaps.getCreate(observerKey);

        this.keyNo = 0;
        this.keyBlock = this.keyParts[this.keyNo];


        ClassPropertyObserverCreator.processKeys(caller);
        ClassPropertyObserverCreator.clear();

    }



    /**
     * remove observer
     *
     */
    public static remove(_class: IBindingContext, observerKey: string, caller: any) {

        this.classRef = _class && _class.$context;
        this.keyParts = Cache.keyMaps.getCreate(observerKey);
        this.keyNo = 0;
        this.keyBlock = this.keyParts && this.keyParts[this.keyNo];

        ClassPropertyObserverCreator.removeKeys(caller);
        ClassPropertyObserverCreator.clear();

    }



    /**
     * clear internal
     *
     */
    private static clear() {
        this.classRef = null;
        this.keyParts = null;
        this.keyNo = null;
        this.keyBlock = null;
    }



    /**
     * set next key
     *
     */
    private static nextKey() {
        this.keyNo++;
        this.keyBlock = this.keyParts[this.keyNo];

    }



    /**
     * process key
     *
     */
    private static processKeys(caller: any) {

        // add "__obeserver" prop if it does not exist
        if (!this.classRef.__observer) {
            Object.defineProperty(this.classRef, '__observer', {
                writable: true,
                configurable: true,
                value: new emptyObject()
            });
        }

        // create observer
        if (!this.classRef.__observer[this.keyBlock]) {
            this.classRef.__observer[this.keyBlock] = new ClassPropertyObserver(this.classRef, this.keyBlock);
            this.classRef.__observer[this.keyBlock].subscribe(caller);
        } else {
            this.classRef.__observer[this.keyBlock].subscribe(caller);
            this.classRef.__observer[this.keyBlock].observe();

        }

        // if not next
        if (this.keyNo !== this.keyParts.length - 1 && this.keyParts.length > 1) {

            if (this.classRef) {
                this.classRef = this.classRef[this.keyBlock];
            }

            this.nextKey();
            if (this.classRef) {
                this.processKeys(caller);
            }
        }
    }



    /**
     * remove key
     *
     */
    private static removeKeys(caller: any) {


        // unsubscribe
        if (this.classRef && this.classRef.__observer) {
            if (this.classRef.__observer[this.keyBlock]) {
                this.classRef.__observer[this.keyBlock].unsubscribe(caller);
            }
        }


        // if not next
        if (this.keyNo !== this.keyParts.length - 1 && this.keyParts.length > 1) {

            if (this.classRef) {
                this.classRef = this.classRef[this.keyBlock];
            }

            this.nextKey();
            if (this.classRef) {
                this.removeKeys(caller);
            }
        }
    }



}
