import { DataUtilities } from "../../Common/DataUtilities";
class ListenerRegistration {
    constructor() {
        this.components = [];
        this.functions = [];
    }
}
// todo, keep copy of old object, only send out updates if actual changes were made
export class StateManager {
    static addObserver(observable, observer, properties = []) {
        //        console.log('adding observer ', observable)
        let observers = Reflect.get(observable, this.OBSERVERS_VALUE_NAME);
        if (!observers) {
            observers = new ListenerRegistration();
            Reflect.set(observable, this.OBSERVERS_VALUE_NAME, observers);
        }
        if (observer instanceof Function) {
            observers.functions.push({ function: observer, properties: properties });
        }
        else {
            observers.components.push({ component: observer, properties: properties });
        }
    }
    static removeObserver(observable, observer) {
        let observers = Reflect.get(observable, this.OBSERVERS_VALUE_NAME);
        if (observers) {
            if (observer instanceof Function) {
                let index = -1;
                for (let l of observers.functions) {
                    index++;
                    if (l.function === observer)
                        break;
                }
                if (index !== -1)
                    observers.functions.splice(index, 1);
            }
            else {
                let index = -1;
                for (let l of observers.components) {
                    index++;
                    if (l.component === observer)
                        break;
                }
                if (index !== -1)
                    observers.components.splice(index, 1);
            }
        }
    }
    static isListening(listeningToProperties, changedProperties) {
        for (let prop of listeningToProperties) {
            if (changedProperties.indexOf(prop) !== -1)
                return true;
        }
        return false;
    }
    // todo.  stop double forcing.
    static internalTriggerUpdate(observable, forProperties = undefined) {
        let observers = Reflect.get(observable, this.OBSERVERS_VALUE_NAME);
        let oldStates = Reflect.get(observable, this.OLD_STATES_VALUE_NAME);
        if (!oldStates) {
            oldStates = [];
            Reflect.set(observable, this.OLD_STATES_VALUE_NAME, oldStates);
        }
        oldStates.push(DataUtilities.clone(observable));
        // keep only last 20
        if (oldStates.length > 20)
            oldStates.shift();
        if (observers) {
            for (let l of observers.components) {
                // checking property listeners.
                if (forProperties && l.properties && l.properties.length !== 0 && this.isListening(l.properties, forProperties)) {
                    //                    console.log('calling state change', l.component)
                    l.component.forceUpdate();
                }
                // call general observers, always.
                if (!l.properties || l.properties.length === 0) {
                    //                    console.log('calling general state change - based on property change', l.component)
                    l.component.forceUpdate();
                }
            }
            for (let l of observers.functions) {
                // checking property listeners.
                if (forProperties && l.properties && l.properties.length !== 0 && this.isListening(l.properties, forProperties)) {
                    //                  console.log('calling state change - based on property change : function')
                    l.function();
                }
                // call general observers, always.
                if (!l.properties || l.properties.length === 0) {
                    //                    console.log('calling general state change - based on property change : function')
                    l.function();
                }
            }
        }
    }
    static isChanged(observable) {
        // let oldStates = Reflect.get(observable, this.OLD_STATES_VALUE_NAME) as object[] ;
        //
        // if (!oldStates || oldStates.length === 0) {
        //     return true;
        // }
        //
        // return DataUtilities.compare(oldStates[oldStates.length -1], observable);
        //
        //
        // return true;
        return true;
    }
    static forceChange(observable) {
        StateManager.internalTriggerUpdate(observable);
    }
    static changed(observable) {
        if (this.isChanged(observable))
            StateManager.internalTriggerUpdate(observable);
    }
    static propertyChanged(observable, property) {
        if (this.isChanged(observable))
            StateManager.internalTriggerUpdate(observable, [property]);
    }
    static propertiesChanged(observable, properties) {
        if (this.isChanged(observable)) {
            StateManager.internalTriggerUpdate(observable, properties);
        }
    }
}
StateManager.OBSERVERS_VALUE_NAME = '_____observers';
StateManager.OLD_STATES_VALUE_NAME = '_____oldstates';
// class ObserverHandler<T extends object> {
//     set(obj: object, prop: string, value: any) : boolean{
//         if (prop === 'age') {
//             if (!Number.isInteger(value)) {
//                 throw new TypeError('The age is not an integer');
//             }
//             if (value > 200) {
//                 throw new RangeError('The age seems invalid');
//             }
//         }
//
//         // The default behavior to store the value
//         Reflect.set(obj, prop, value);
//
//
//         // Indicate success
//         return true;
//     }
// };
//
// export class ObserverProxyFactory  {
//
//     static getProxy<T extends object>(target: T) : T {
//         return new Proxy<T>(target, new ObserverHandler<T>());
//     }
//
// }
export const action = (target, propertyKey, descriptor) => {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args) {
        let retVal = originalMethod.apply(...args);
        StateManager.changed(target);
        return retVal;
    };
};
//# sourceMappingURL=StateManager.js.map