(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();
	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 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);
		},
		
		NS_NOINTERFACE: 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);
		},
		
		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 = contentAPI;

		if (appWin.document.location.href.indexOf("resource://livemargins/apps/search/search.html") == 0) {
			Cu.evalInSandbox(jsm.utils.getContentFromURL("resource://livemargins/3rd/jquery.js"),                sandbox, "1.8");	
			Cu.evalInSandbox(jsm.utils.getContentFromURL("resource://livemargins/apps/search/search-detail.js"), sandbox, "1.8");
			Cu.evalInSandbox(jsm.utils.getContentFromURL("resource://livemargins/3rd/jquery.selectboxes.js"),    sandbox, "1.8");
			Cu.evalInSandbox(jsm.utils.getContentFromURL("resource://livemargins/3rd/template.js"),              sandbox, "1.8");
			Cu.evalInSandbox(jsm.utils.getContentFromURL("resource://livemargins/apps/search/search.js"),        sandbox, "1.8");
			sandbox.init();
		} else if (appWin.document.location.href.indexOf("resource://livemargins/apps/search/search-config.html") == 0) {
			Cu.evalInSandbox(jsm.utils.getContentFromURL("resource://livemargins/3rd/jquery.js"),                sandbox, "1.8");	
			Cu.evalInSandbox(jsm.utils.getContentFromURL("resource://livemargins/3rd/template.js"),              sandbox, "1.8");
			Cu.evalInSandbox(jsm.utils.getContentFromURL("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.
	window.addEventListener("load", function() {
		document.getElementById("player_browser").addProgressListener(_progListener, Ci.nsIWebProgress.NOTIFY_ALL | Ci.nsIWebProgress.NOTIFY_STATE_ALL);
	}, false);
	
	window.addEventListener("unload", function() {
		document.getElementById("player_browser").removeProgressListener(_progListener, Ci.nsIWebProgress.NOTIFY_ALL | Ci.nsIWebProgress.NOTIFY_STATE_ALL);	
	}, false);
})();