/* Copyright 2013 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/**
 * @fileoverview
 * The sandbox side of the application/sandbox WCS interface, used by the
 * sandbox to exchange messages with the application.
 */

'use strict';

/** @suppress {duplicate} */
var remoting = remoting || {};

/** @constructor */
remoting.WcsSandboxContent = function() {
  /**
   * @type {Window}
   * @private
   */
  this.parentWindow_ = null;
  /**
   * @type {number}
   * @private
   */
  this.nextXhrId_ = 0;
  /**
   * @type {Object.<number, XMLHttpRequest>}
   * @private
   */
  this.pendingXhrs_ = {};

  window.addEventListener('message', this.onMessage_.bind(this), false);
};

/**
 * Event handler to process messages from the application.
 *
 * @param {Event} event
 */
remoting.WcsSandboxContent.prototype.onMessage_ = function(event) {
  this.parentWindow_ = event.source;

  switch (event.data['command']) {

    case 'proxyXhrs':
      // Since the WCS driver code constructs XHRs directly, the only
      // mechanism for proxying them is to replace the XMLHttpRequest
      // constructor.
      XMLHttpRequest = remoting.XMLHttpRequestProxy;
      break;

    case 'sendIq':
      /** @type {string} */
      var stanza = event.data['stanza'];
      if (stanza === undefined) {
        console.error('sendIq: missing IQ stanza.');
        break;
      }
      if (remoting.wcs) {
        remoting.wcs.sendIq(stanza);
      } else {
        console.error('Dropping IQ stanza:', stanza);
      }
      break;

    case 'setAccessToken':
      /** @type {string} */
      var token = event.data['token'];
      if (token === undefined) {
        console.error('setAccessToken: missing access token.');
        break;
      }
      // The WCS driver JS requires that remoting.wcsLoader be a global
      // variable, so it can't be a member of this class.
      // TODO(jamiewalch): remoting.wcs doesn't need to be global and should
      // be made a member (http://crbug.com/172348).
      if (remoting.wcs) {
        remoting.wcs.updateAccessToken(token);
      } else if (!remoting.wcsLoader) {
        remoting.wcsLoader = new remoting.WcsLoader();
        remoting.wcsLoader.start(token,
                                 this.onLocalJid_.bind(this),
                                 this.onError_.bind(this));
      }
      break;

    case 'xhrStateChange':
      /** @type {number} */
      var id = event.data['id'];
      if (id === undefined) {
        console.error('xhrStateChange: missing id.');
        break;
      }
      var pendingXhr = this.pendingXhrs_[id];
      if (!pendingXhr) {
        console.error('xhrStateChange: unrecognized id:', id);
        break;
      }
      /** @type {XMLHttpRequest} */
      var xhr = event.data['xhr'];
      if (xhr === undefined) {
        console.error('xhrStateChange: missing xhr');
        break;
      }
      for (var member in xhr) {
        pendingXhr[member] = xhr[member];
      }
      if (xhr.readyState == 4) {
        delete this.pendingXhrs_[id];
      }
      if (pendingXhr.onreadystatechange) {
        pendingXhr.onreadystatechange();
      }
      break;

    default:
      console.error('Unexpected message:', event.data['command'], event.data);
  }
};

/**
 * Callback method to indicate that the WCS driver has loaded and provide the
 * full JID of the client.
 *
 * @param {string} localJid The full JID of the WCS client.
 * @private
 */
remoting.WcsSandboxContent.prototype.onLocalJid_ = function(localJid) {
  remoting.wcs.setOnIq(this.onIq_.bind(this));
  var message = {
    'command': 'onLocalJid',
    'localJid': localJid
  };
  this.parentWindow_.postMessage(message, '*');
};

/**
 * Callback method to indicate that something went wrong loading the WCS driver.
 *
 * @param {remoting.Error} error Details of the error.
 * @private
 */
remoting.WcsSandboxContent.prototype.onError_ = function(error) {
  var message = {
    'command': 'onError',
    'error': error
  };
  this.parentWindow_.postMessage(message, '*');
};

/**
 * Forward an XHR to the container process to send. This is analogous to XHR's
 * send method.
 *
 * @param {remoting.XMLHttpRequestProxy} xhr The XHR to send.
 * @return {number} The unique ID allocated to the XHR. Used to abort it.
 */
remoting.WcsSandboxContent.prototype.sendXhr = function(xhr) {
  var id = this.nextXhrId_++;
  this.pendingXhrs_[id] = xhr;
  var message = {
    'command': 'sendXhr',
    'id': id,
    'parameters': xhr.sandbox_ipc
  };
  this.parentWindow_.postMessage(message, '*');
  delete xhr.sandbox_ipc;
  return id;
};

/**
 * Abort a forwarded XHR. This is analogous to XHR's abort method.
 *
 * @param {number} id The unique ID of the XHR to abort, as returned by sendXhr.
 */
remoting.WcsSandboxContent.prototype.abortXhr = function(id) {
  if (!this.pendingXhrs_[id]) {
    // The XHR is removed when it reaches the "ready" state. Calling abort
    // subsequently is unusual, but legal, so just silently ignore the request
    // in this case.
    return;
  }
  var message = {
    'command': 'abortXhr',
    'id': id
  };
  this.parentWindow_.postMessage(message, '*');
};

/**
 * Callback to indicate than an IQ stanza has been received from the WCS
 * driver, and should be forwarded to the main process.
 *
 * @param {string} stanza
 * @private
 */
remoting.WcsSandboxContent.prototype.onIq_ = function(stanza) {
  remoting.wcs.setOnIq(this.onIq_.bind(this));
  var message = {
    'command': 'onIq',
    'stanza': stanza
  };
  this.parentWindow_.postMessage(message, '*');
};

/**
 * Entry point for the WCS sandbox process.
 */
function onSandboxInit() {
  // The WCS code registers for a couple of events that aren't supported in
  // Apps V2, so ignore those for now.
  var oldAEL = window.addEventListener;
  window.addEventListener = function(type, listener, useCapture) {
    if (type == 'beforeunload' || type == 'unload') {
      return;
    }
    oldAEL(type, listener, useCapture);
  };

  remoting.settings = new remoting.Settings();
  remoting.sandboxContent = new remoting.WcsSandboxContent();
}

window.addEventListener('load', onSandboxInit, false);

/** @type {remoting.WcsSandboxContent} */
remoting.sandboxContent = null;