/*global window, rJS, RSVP */
/*jslint indent: 2, maxerr: 3 */
(function(window, rJS, RSVP, jIO) {
    "use strict";

  /**
     * Send request with XHR and return a promise. xhr.onload: The promise is
     * resolved when the status code is lower than 400 with the xhr object as
     * first parameter. xhr.onerror: reject with xhr object as first
     * parameter. xhr.onprogress: notifies the xhr object.
     *
     * @param  {Object} param The parameters
     * @param  {String} [param.type="GET"] The request method
     * @param  {String} [param.dataType=""] The data type to retrieve
     * @param  {String} param.url The url
     * @param  {Any} [param.data] The data to send
     * @param  {Function} [param.beforeSend] A function called just before the
     *    send request. The first parameter of this function is the XHR object.
     * @return {Promise} The promise
     */
  function ajax(param) {
    var xhr = new XMLHttpRequest();
    return new RSVP.Promise(function(resolve, reject, notify) {
      var k;
      xhr.open(param.type || "GET", param.url, true);
      xhr.addEventListener("load", function(e) {
        var answer = {};
        if (e.target.status >= 400) {
          return reject(e);
        }
        answer.responseText = this.responseText;
        answer.responseType = this.getResponseHeader("content-type");
        answer.responseURL = param.url;
        resolve(answer);
      });
      xhr.addEventListener("error", reject);
      xhr.addEventListener("progress", notify);
      if (typeof param.xhrFields === 'object' && param.xhrFields !== null) {
        for (k in param.xhrFields) {
          if (param.xhrFields.hasOwnProperty(k)) {
            xhr[k] = param.xhrFields[k];
          }
        }
      }
      xhr.send();
    }, function() {
      xhr.abort();
    });
  }
  
  
  function sendMessage(message) {
    // This wraps the message posting/response in a promise, which will resolve if the response doesn't
    // contain an error, and reject with the error if it does. If you'd prefer, it's possible to call
    // controller.postMessage() and set up the onmessage handler independently of a promise, but this is
    // a convenient wrapper.
    return new RSVP.Promise(function(resolve, reject, notify) {
      var messageChannel = new MessageChannel();
      messageChannel.port1.onmessage = function(event) {
        console.log(event);
        if (event.data.error) {
          reject(event.data.error);
        } else {
          resolve(event.data);
        }
      };
      
      // This sends the message data as well as transferring messageChannel.port2 to the service worker.
      // The service worker can then use the transferred port to reply via postMessage(), which
      // will in turn trigger the onmessage handler on messageChannel.port1.
      // See https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
      return navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);
    });
  }
  
  function setStatus(statusMessage) {
    console.log(statusMessage);
  }
  
  rJS(window)
    .ready(function (g) {
      g.props = {};
      return g.getElement()
        .push(function (element) {
          g.props.element = element;
         });
    })  
    .ready(function(gadget) {
      // Initialize the gadget local parameters
      gadget.state_parameter_dict = {};
      if ('serviceWorker' in navigator) {
	    // XXX Hack to not add a new service worker when one is already declared
        if (!navigator.serviceWorker.controller) {
          return new RSVP.Promise(function(resolve, reject, notify) {
            navigator.serviceWorker.register('/sw.js', {scope: '/'}).then(
              function(){
                if (navigator.serviceWorker.controller) {
                  resolve();
                } else {
                  reject("Please reload this page to allow Service Worker to control this page");
                }
            }).catch(function(error) {
              reject(error);
            });
          });
        }
      } else {
        throw "Service Worker are not available in your browser";
      }
    })
    
    /**
     * allDocs return the list of document in the cache
     *
     * @params {Object} Not taken into account 
     * @return {} Return the data url of the document
    */
    .declareMethod('allDocs', function(params) {
      if (params && params.cached_only) {
        return new RSVP.Queue()
          .push(function() {
          return sendMessage({
            command: 'keys'
          });
        });
      }
      return new RSVP.Queue()
        .push(function() {
        return sendMessage({
          command: 'allDocs'
        });
      });      
    })
    
    /**
     * get Return a data url. Cannot return a blob as the data
     * is transmitted through an iFrame
     *
     * @url  {string} url of the document to retrieve 
     * @return {data_url} Return the data url of the document
    */
    .declareMethod('get', function(url) {
      return new RSVP.Queue()
      .push(function () {
        return ajax({
          url: url
        });
      })
      .push(function (result) {
        return new Blob([result.responseText], {type: result.responseType})
      })
      .push(function(result) {
        return jIO.util.readBlobAsDataURL(result);
      })
      .push(function(e) {
        return e.target.result;
      });
    })
    
    /**
     * put Return a data url. Cannot provide a blob as the data
     * is transmitted through an iFrame
     *
     * @url  {string} url of the document to update 
     * @parameter {data_url} data url of the document to put, it will be transformed in a blob 
     * @return {data_url} Return the data url of the document
    */
    .declareMethod('put', function(url, parameter) {
      return new RSVP.Queue()
      .push(function() {
        return sendMessage({
          command: 'add',
          url: url,
          information: jIO.util.dataURItoBlob(parameter)
        });
      }).push(function() {
        // If the promise resolves, just display a success message.
        console.log("Done adding "+ url);
        return 'Added to cache:  ' + url + ' at ' + Date();
      }).fail(setStatus);
    })
    
    /**
     * Remove an url from the cache
     *
     * @url  {string} url of the document to remove 
     * @return {}
    */
    .declareMethod('remove', function(url) {
      return new RSVP.Queue()
        .push(function() {
          return sendMessage({
            command: 'delete',
            url: url
          });
        });
    });

}(window, rJS, RSVP, jIO));