import {Observable} from "./observable";

export class ValueSubject extends Observable{
    _observers = [];
    constructor(data){
        super(observer=>{
            this._observers.push(observer);

            if(this._timestamp!==undefined){
                // Already have a value
                observer.next(this._value);
            }else if(this._request === undefined){
                // No request has been made yet
                this.refresh();
            }

            // Return a cleanup function which will unsubscribe the observer
            return ()=>{
                this._observers = this._observers.filter(obs=>obs!==observer);
            }
        });

        if(typeof(data)==="function"){
            this._refreshFn = data;
            this._value = undefined;
            this._timestamp = undefined;
        }else if(data!==undefined){
            this._refreshFn = undefined;
            this._value = data;
            this._timestamp = new Date();
        }
    }

    refresh(){
        if(this._refreshFn){
            let req = this._request = Promise.resolve(this._refreshFn()).then(result=>{
                this.update(result);
                this._request = undefined;
                return this._value;
            }).catch(err=>{
                for(let obs of this._observers){
                    obs.error(err);
                }
            });
            return req;
        }else{
            return new Promise((resolve)=>resolve());// NO-OP
        }
    }

    close(){
        for(let obs of this._observers){
            obs.complete();
        }
    }

    update(newValue){
        if(newValue!==this._value){
            this._value = newValue;
            this._timestamp = new Date();
            for(let obs of this._observers){
                obs.next(newValue);
            }
        }
    }

    getObservable({passive=false}= {}){
        return new Observable(observer=>{
            this._observers.push(observer);

            if(!passive){
                if(this._timestamp!==undefined){
                    // Already have a value
                    observer.next(this._value);
                }else if(this._request === undefined){
                    // No request has been made yet
                    this.refresh();
                }
            }

            // Return a cleanup function which will unsubscribe the observer
            return ()=>{
                this._observers = this._observers.filter(obs=>obs!==observer);
            }
        })
    }

    // Properties
    get timestamp(){
        return this._timestamp;
    }

    get value(){
        return this._value;
    }
    set value(value){
        this.update(value);
    }

    get updates(){
        return this.getObservable({passive: true});
    }
}

//if(window && !window.ValueSubject) window.ValueSubject = ValueSubject;