/**
 * @class Loader
 *
 * @description 
 *  Responsibilities
 *  > Being a facade for an ajax request of contents
 *
 *  @method load(url)
 *  @property {boolean} loading - state of the loader
 */
 export default class Loader {

    /**     
     * @param {*} onLoad( payload ) - called when the loading is complete 
     * @param {*} onLoading         - called when loading starts 
     * @param {*} onError( error )  - called when the loading fails
     */
    constructor( onLoad = null, onError = null, onLoading = null) {

        // state of the loader
        this.loading  = false;
        
        this.onLoad    = onLoad;
        this.onError   = onError;
        this.onLoading = onLoading;

        // instantiate the Web Worker (parallel thread)        
        this.loader = this._setWorker();
        
    }
   
    /* 
     * makes some adjustments to adapt the worker path
     * different dev environments
     */
    _setLoaderWorker() {
        try{
            if(window.Worker) return new Worker(this.worker);
        } catch(err) {
            console.log('_b| [loader] ',err.message);
        }        
    }

    _loading() {
        
        // sets the status of loading 
        this.loading = true;
        
        // launch the callback
        if(this.onLoading) this.onLoading();
    }

    _error(error) {

        // resets the state
        this.loading = false;
        
        // launch the callback  
        if(this.onError) this.onError(error);
    }

    _loaded( payload ) {
        
        // resets the state
        this.loading = false;

        // launch the callback
        if( this.onLoad ) this.onLoad( payload );
    }

    /**
     * Execute loading. In a new thread, if possible.
     * takes as a parameter the url to load from and three callbacks
     * to update the state
     */
    _parallelLoad(url) {
        
        if( this.loader ) {
            
            // dispatch of the incoming messages            
            this.loader.onmessage = msg => {
                switch( msg.data.type ) {
                    case 'loaded'  : this._loaded.call( this, msg.data.payload ); break;
                    case 'loading' : this._loading.call( this ); break;
                    case 'error'   : this._error.call( this, msg.data.payload ); break;
                }
            }

            // send the url to be loaded to the loader thread
            this.loader.postMessage({ url: url });

        } else {

            console.log('_b| [loader] not threaded')
            // IE9 version not parallel at all
            var oReq = new XMLHttpRequest();  
            oReq.onload  = response => { var payload = this.responseText;  this._loaded.call( this, payload );}
            oReq.onerror = () => { load_self._error.call( this ); }
            oReq.open("GET", url);
            oReq.send();
            
            this._loading.call( this );
        }
    }    

    /**
     * loads the content from the given url
     */
    load( url ) {
        
        if (url != null) this._parallelLoad(url);   
    }

    _setWorker() {        
        var content = [
            " onmessage = function(msg) { if(msg.data.url) load(msg.data.url); else postMessage({type : 'error', text : 'no url specified'}); }\n"+
            " load = function(url) { var oReq = new XMLHttpRequest(); oReq.onload = transferComplete; oReq.onerror = transferFailed; console.log('_b| loading...', url); oReq.open('GET', url); oReq.send(); postMessage({type : 'loading'}); }\n"+
            " function transferComplete() { console.log('_b| loaded'/*, this*/); postMessage({type: 'loaded', payload : this.response}); }\n"+
            " function transferFailed() { console.log('_b| load error', this.responseText); postMessage({type: 'error', text : this.response}); }\n"
        ];

        var blob = new Blob(content);
        return new Worker(window.URL.createObjectURL(blob));
    }

}