/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var Ci = Components.interfaces;
var Cu = Components.utils;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/DOMRequestHelper.jsm');

XPCOMUtils.defineLazyModuleGetter(this, 'GlobalSimulatorScreen',
                                  'resource://gre/modules/GlobalSimulatorScreen.jsm');

var DEBUG_PREFIX = 'SimulatorScreen.js - ';
function debug() {
  //dump(DEBUG_PREFIX + Array.slice(arguments) + '\n');
}

function fireOrientationEvent(window) {
  let e = new window.Event('mozorientationchange');
  window.screen.dispatchEvent(e);
}

function hookScreen(window) {
  let nodePrincipal = window.document.nodePrincipal;
  let origin = nodePrincipal.origin;
  if (nodePrincipal.appStatus == nodePrincipal.APP_STATUS_NOT_INSTALLED) {
    // Only inject screen mock for apps
    return;
  }

  let screen = window.wrappedJSObject.screen;

  screen.mozLockOrientation = function (orientation) {
    debug('mozLockOrientation:', orientation, 'from', origin);

    // Normalize and do some checks against orientation input
    if (typeof(orientation) == 'string') {
      orientation = [orientation];
    }

    function isInvalidOrientationString(str) {
      return typeof(str) != 'string' ||
        !str.match(/^default$|^(portrait|landscape)(-(primary|secondary))?$/);
    }
    if (!Array.isArray(orientation) ||
        orientation.some(isInvalidOrientationString)) {
      Cu.reportError('Invalid orientation "' + orientation + '"');
      return false;
    }

    GlobalSimulatorScreen.lock(orientation);

    return true;
  };

  screen.mozUnlockOrientation = function() {
    debug('mozOrientationUnlock from', origin);
    GlobalSimulatorScreen.unlock();
    return true;
  };

  Object.defineProperty(screen, 'width', {
    get: () => GlobalSimulatorScreen.width
  });
  Object.defineProperty(screen, 'height', {
    get: () => GlobalSimulatorScreen.height
  });
  Object.defineProperty(screen, 'mozOrientation', {
    get: () => GlobalSimulatorScreen.mozOrientation
  });
}

function SimulatorScreen() {}
SimulatorScreen.prototype = {
  classID:         Components.ID('{c83c02c0-5d43-4e3e-987f-9173b313e880}'),
  QueryInterface:  XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
  _windows: new Map(),

  observe: function (subject, topic, data) {
    let windows = this._windows;
    switch (topic) {
      case 'profile-after-change':
        Services.obs.addObserver(this, 'document-element-inserted', false);
        Services.obs.addObserver(this, 'simulator-orientation-change', false);
        Services.obs.addObserver(this, 'inner-window-destroyed', false);
        break;

      case 'document-element-inserted':
        let window = subject.defaultView;
        if (!window) {
          return;
        }

        hookScreen(window);

        var id = window.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils)
                       .currentInnerWindowID;
        windows.set(id, window);
        break;

      case 'inner-window-destroyed':
        var id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
        windows.delete(id);
        break;

      case 'simulator-orientation-change':
        windows.forEach(fireOrientationEvent);
        break;
    }
  }
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SimulatorScreen]);
