(function() {
  var Cu = Components.utils;
  var Ci = Components.interfaces;
  var Cc = Components.classes;

  var jsm = { };
  Cu["import"]("resource://livemargins/apps/search/searchService.js", jsm);
  Cu["import"]("resource://livemargins/webjsm.js", jsm);
  Cu["import"]("resource://livemargins/utils.jsm", jsm);

  var webjsm = new jsm.WebJsModule(window);
  webjsm.importScript("resource://livemargins/3rd/jquery.js");

  // Privileged api for appcenter page for concerning the security.
  // Make sure that all the api has limited privileges.
  function channelListener(onStartRequest, onDataAvailable, onStopRequest) {
    this._onStartRequest = onStartRequest;
    this._onDataAvailable = onDataAvailable;
    this._onStopRequest = onStopRequest;
  }

  channelListener.prototype = {
    onStartRequest: function (aRequest, aContext) {
      if (typeof this._onStartRequest == "function") {
        this._onStartRequest();
      }
    },

    onDataAvailable: function (aRequest, aContext,
                               aStream, aSourceOffset, aLength) {
      if (typeof this._onDataAvailable == "function") {
        this._onDataAvailable(aRequest, aContext,
                              aStream, aSourceOffset, aLength);
      }
    },

    onStopRequest: function (aRequest, aContext, aStatus) {
      if (typeof this._onStopRequest == "function") {
        this._onStopRequest(aRequest, aContext, aStatus);
      }
    },

    // nsIChannelEventSink
    onChannelRedirect: function (aOldChannel, aNewChannel, aFlags) { },

    // nsIInterfaceRequestor
    getInterface: function (aIID) {
      try {
        return this.QueryInterface(aIID);
      } catch (e) {
        throw Components.results.NS_NOINTERFACE;
      }
    },

    // nsIProgressEventSink (not implementing will cause annoying exceptions)
    onProgress : function (aRequest, aContext, aProgress, aProgressMax) { },
    onStatus : function (aRequest, aContext, aStatus, aStatusArg) { },

    // nsIHttpEventSink (not implementing will cause annoying exceptions)
    onRedirect : function (aOldChannel, aNewChannel) { },

    QueryInterface : function(aIID) {
      if (aIID.equals(Components.interfaces.nsISupports) ||
              aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
              aIID.equals(Components.interfaces.nsIChannelEventSink) ||
              aIID.equals(Components.interfaces.nsIProgressEventSink) ||
              aIID.equals(Components.interfaces.nsIHttpEventSink) ||
              aIID.equals(Components.interfaces.nsIStreamListener))
        return this;

      throw Components.results.NS_NOINTERFACE;
    }
  };

  var logger = new jsm.utils.logger("lm4.multisearch");
  var contentAPI = {
    searchService: jsm.utils.wrapAsContentAPI(jsm.SearchService),

    // Content apis for pref service
    getCharPref: function(prefName) {
      return Cc['@mozilla.org/preferences-service;1']
            .getService(Components.interfaces.nsIPrefService)
            .getBranch('extensions.livemargins.search.')
            .getCharPref(prefName);
    },

    getBoolPref: function(prefName) {
      return Cc['@mozilla.org/preferences-service;1']
            .getService(Components.interfaces.nsIPrefService)
            .getBranch('extensions.livemargins.search.')
            .getBoolPref(prefName);
    },

    setBoolPref: function(prefName, prefValue) {
      Cc['@mozilla.org/preferences-service;1']
            .getService(Components.interfaces.nsIPrefService)
            .getBranch('extensions.livemargins.search.')
            .setBoolPref(prefName, prefValue)
    },

    setCharPref: function(prefName, prefValue) {
      Cc['@mozilla.org/preferences-service;1']
            .getService(Components.interfaces.nsIPrefService)
            .getBranch('extensions.livemargins.search.')
            .setCharPref(prefName, prefValue)
    },

    // Content apis for search engines injection.
    get currentEngine () {
      return Cc["@mozilla.org/browser/search-service;1"].
            getService(Ci.nsIBrowserSearchService).
            currentEngine;
    },

    get visibleEngines () {
      return Cc["@mozilla.org/browser/search-service;1"].
            getService(Ci.nsIBrowserSearchService).
            getVisibleEngines({});
    },

    getSearchEngines: function(searchString) {
      var currentEngine = Cc["@mozilla.org/browser/search-service;1"]
                  .getService(Ci.nsIBrowserSearchService)
                  .currentEngine;
      var engines = Cc["@mozilla.org/browser/search-service;1"]
                  .getService(Ci.nsIBrowserSearchService)
                  .getVisibleEngines({});

      if (engines.length == 0) {
        return null;
      }

      if (currentEngine === null) {
        currentEngine = engines[0];
      }

      var result = {
        currentEngine: {},
        engines: []
      };

      try {
        result.currentEngine.title = currentEngine.name;
        result.currentEngine.thumb = currentEngine.iconURI.spec;
        result.currentEngine.link = currentEngine
                                      .getSubmission(searchString, null)
                                      .uri.spec;

        for (var i = 0; i < engines.length; i += 1) {
          if (engines[i] === currentEngine)
            continue;

          result.engines.push({
            title: engines[i].name,
            thumb: engines[i].iconURI.spec,
            link: engines[i].getSubmission(searchString, null).uri.spec
          });
        }
      } catch (e) { }

      return jsm.utils.exposeReadOnly(result);
    },

    // content apis for ioService
    newURI: function(aSpec, aOriginCharset, aBaseURI) {
      return Cc["@mozilla.org/network/io-service;1"]
               .getService(Components.interfaces.nsIIOService)
               .newURI(aSpec, aOriginCharset, aBaseURI);
    },

    getFullSpec: function(aSpec, aOriginCharset, aBaseURI) {
      return Cc["@mozilla.org/network/io-service;1"]
               .getService(Components.interfaces.nsIIOService)
               .newURI(aSpec, aOriginCharset, aBaseURI).spec;
    },

    newChannel: function(aSpec, aOriginCharset, aBaseURI) {
      var ioService = Cc["@mozilla.org/network/io-service;1"].
                getService(Components.interfaces.nsIIOService);
      return ioService.newChannelFromURI(ioService.newURI(aSpec, aOriginCharset, aBaseURI));
    },

    newChannelFromURI: function(aUri) {
      return Cc["@mozilla.org/network/io-service;1"].
            getService(Components.interfaces.nsIIOService).
            newChannelFromURI(aUri);
    },

    readDataFromStream: function(aStream, aLength) {
      var charset = "GBK";
      const replacementChar = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
      var is = Cc["@mozilla.org/intl/converter-input-stream;1"].
            createInstance(Ci.nsIConverterInputStream);
      is.init(aStream, charset, aLength, replacementChar);
      var str = {};
      var data = [];
      while (is.readString(4096, str) != 0) {
          data.push(str.value);
      }
      return data.join("");
    },

    isSuccessCode:  function(aStatus) {
      return Components.isSuccessCode(aStatus);
    },

    get NS_NOINTERFACE() {
      return Components.results.NS_NOINTERFACE
    },

    createChannelListener: function(onStartRequest, onDataAvailable,
                                    onStopRequest) {
      return new channelListener(onStartRequest, onDataAvailable, onStopRequest);
    },

    asyncOpenHttpChannel: function(aHttpChannel, listener)  {
      aHttpChannel.notificationCallbacks = listener;
      aHttpChannel.asyncOpen(listener, null);
    },

    // content apis for tabbrowser
    get currentTabLink() {
      var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                         .getService(Components.interfaces.nsIWindowMediator);
      var recentWindow = wm.getMostRecentWindow("navigator:browser");
      return recentWindow ? recentWindow.content.document.location : null;
    },

    log: function(msg) {
      logger.trace(msg);
    },

    ajax: function(options) {
      webjsm.jQuery.ajax(options);
    },

    makeObjAccessible: function(obj) {
      return jsm.utils.exposeReadOnly(obj);
    },

    setRequestHeader: function(request, header, value) {
      request.setRequestHeader(header, value);
    },

    $: function() {
      return webjsm.jQuery(arguments);
    },
  };

  function init() {
    var appWin = document.getElementById("player_browser").contentWindow;
    var sandbox = new Cu.Sandbox(appWin);
    sandbox.window = appWin;
    sandbox.document = appWin.document;
    sandbox.__proto__ = sandbox.window;
    sandbox.contentAPI = jsm.utils.exposeReadOnly(contentAPI);

    function evalInSandbox(url, sandboxObj, version) {
      Cu.evalInSandbox(jsm.utils.getContentFromURL(url), sandboxObj, version);
    }

    let configPageUrl = "resource://livemargins/apps/search/search-config.html";
    let searchPageUrl = "resource://livemargins/apps/search/search.html";
    let href = appWin.document.location.href;
    if (href.indexOf(searchPageUrl) == 0) {
      evalInSandbox("resource://livemargins/3rd/jquery.js", sandbox, "1.8");
      evalInSandbox("resource://livemargins/apps/search/search-detail.js",
                    sandbox, "1.8");
      evalInSandbox("resource://livemargins/3rd/jquery.selectboxes.js",
                    sandbox, "1.8");
      evalInSandbox("resource://livemargins/3rd/template.js", sandbox, "1.8");
      evalInSandbox("resource://livemargins/apps/search/search.js",
                    sandbox, "1.8");
    } else if (href.indexOf(configPageUrl) == 0) {
      evalInSandbox("resource://livemargins/3rd/jquery.js", sandbox, "1.8");
      evalInSandbox("resource://livemargins/3rd/template.js", sandbox, "1.8");
      evalInSandbox("resource://livemargins/apps/search/search-config.js",
                    sandbox, "1.8");
    }

    sandbox.init();
    appWin.addEventListener("unload", function() {
      delete sandbox.__proto__;
      for (var attr in sandbox) {
        delete sandbox[attr];
      }
    }, false);
  }

  var _progListener = new jsm.utils.browserLoadListener(null, init, null);
  // Can not listen to the "load" event, if xul is opened in dialog.
  // Don't know why, :-(
  // Using progress listener to capture the load event instead.
  let playerBrowser = document.getElementById("player_browser");
  window.addEventListener("load", function() {
    let flag = Ci.nsIWebProgress.NOTIFY_ALL | Ci.nsIWebProgress.NOTIFY_STATE_ALL;
    playerBrowser.addProgressListener(_progListener, flag);
  }, false);

  window.addEventListener("unload", function() {
    let flag = Ci.nsIWebProgress.NOTIFY_ALL | Ci.nsIWebProgress.NOTIFY_STATE_ALL;
    playerBrowser.removeProgressListener(_progListener, flag);
  }, false);
})();

