/*******************************************************************************
 * @license
 * Copyright (c) 2011, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials are made 
 * available under the terms of the Eclipse Public License v1.0 
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
 * 
 * Contributors: IBM Corporation - initial API and implementation
 ******************************************************************************/
/*global define navigator Worker console*/


define(["orion/assert", "orion/serviceregistry", "orion/pluginregistry", "orion/Deferred"], function(assert, mServiceregistry, mPluginregistry, Deferred) {
	var tests = {};
	
	tests["test init/stop empty registry"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		assert.equal(pluginRegistry.getState(), "installed");
		pluginRegistry.init();
		assert.equal(pluginRegistry.getState(), "starting");
		assert.equal(pluginRegistry.getPlugins().length, 0);
		return pluginRegistry.stop().then(function() {
			assert.equal(pluginRegistry.getState(), "resolved");
		});
	};

	tests["test start/stop empty registry"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		assert.equal(pluginRegistry.getState(), "installed");
		return pluginRegistry.start().then(function() {
				assert.equal(pluginRegistry.getState(), "active");
		}).then(function() {
			return pluginRegistry.stop().then(function() {
				assert.equal(pluginRegistry.getState(), "resolved");
			});
		});
	};

	tests["test install/uninstall plugin"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		pluginRegistry.init();
		var promise = pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
			assert.equal(pluginRegistry.getPlugins().length, 1);
			assert.equal(serviceRegistry.getServiceReferences().length, 0);		
			assert.equal(plugin.getState(), "installed");
			return plugin.uninstall().then(function() {
				assert.equal(plugin.getState(), "uninstalled");
				assert.equal(pluginRegistry.getPlugins().length, 0);
			});
		});
		return promise;
	};
	
	tests["test install/uninstall initial plugin"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {plugins:{"testPlugin.html": true}, storage:{}});
		pluginRegistry.init();
		assert.equal(pluginRegistry.getPlugins().length, 1);
		assert.equal(serviceRegistry.getServiceReferences().length, 0);
		var plugin = pluginRegistry.getPlugin("testPlugin.html");
		assert.ok(plugin);
		assert.equal(plugin.getState(), "installed");
		return plugin.uninstall().then(function() {
			assert.equal(pluginRegistry.getPlugins().length, 0);
		});
	};

	tests["test start/stop plugin"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		return pluginRegistry.start().then(function() {
			return pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				assert.equal(pluginRegistry.getPlugins().length, 1);
				assert.equal(serviceRegistry.getServiceReferences().length, 0);		
				assert.equal(plugin.getState(), "installed");
				pluginRegistry.resolvePlugins();
				assert.equal(plugin.getState(), "resolved");
				assert.equal(serviceRegistry.getServiceReferences().length, 0);	
				return plugin.start({"lazy":true}).then(function() {
					assert.equal(plugin.getState(), "starting");
					assert.equal(plugin._getAutostart(), "lazy");
					assert.equal(serviceRegistry.getServiceReferences().length, 1);	
					return plugin.stop();
				}).then(function() {
					assert.equal(plugin.getState(), "resolved");
					assert.equal(plugin._getAutostart(), "stopped");
					assert.equal(serviceRegistry.getServiceReferences().length, 0);	
					return plugin.uninstall();
				}).then(function() {
					assert.equal(plugin.getState(), "uninstalled");
					assert.equal(pluginRegistry.getPlugins().length, 0);
					return pluginRegistry.stop();
				});
			});
		}).then(function() {
			assert.equal("resolved", pluginRegistry.getState());
		});
	};
	
	tests["test start/stop initial plugin"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {plugins:{"testPlugin.html": true}, storage:{}});
		return pluginRegistry.start().then(function() {
			assert.equal(pluginRegistry.getPlugins().length, 1);
			assert.equal(serviceRegistry.getServiceReferences().length, 1);
			var plugin = pluginRegistry.getPlugin("testPlugin.html");
			assert.ok(plugin);
			assert.equal(plugin.getState(), "starting");
			assert.equal(plugin._getAutostart(), "lazy");
			assert.equal(serviceRegistry.getServiceReferences().length, 1);	
			return plugin.stop().then(function() {
				assert.equal(plugin.getState(), "resolved");
				assert.equal(plugin._getAutostart(), "stopped");
				assert.equal(serviceRegistry.getServiceReferences().length, 0);	
				return plugin.uninstall();
			}).then(function() {
				assert.equal(plugin.getState(), "uninstalled");
				assert.equal(pluginRegistry.getPlugins().length, 0);
				return pluginRegistry.stop();
			});
		}).then(function() {
			assert.equal("resolved", pluginRegistry.getState());
		});
	};	

	tests["test install same plugin URL"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		pluginRegistry.init();

		var promise1 = pluginRegistry.installPlugin("testPlugin.html");
		var promise2 = pluginRegistry.installPlugin("testPlugin.html");
		return promise1.then(function(plugin1) {
			return promise2.then(function(plugin2) {
				assert.equal(plugin1, plugin2, "Got the same Plugin instance");
				return pluginRegistry.stop();
			});
		}).then(function() {
			assert.equal("resolved", pluginRegistry.getState());
		});
	};
	
	tests["test install worker plugin"] = function() {
		if (typeof(Worker) === "undefined") {
			return;
		}
		
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		pluginRegistry.start().then(function() {		
			assert.equal(pluginRegistry.getPlugins().length, 0);
			assert.equal(serviceRegistry.getServiceReferences().length, 0);			
			return pluginRegistry.installPlugin("testPlugin.js").then(function(plugin) {
				return plugin.start().then(function() {
					assert.equal(pluginRegistry.getPlugins().length, 1);
					assert.equal(serviceRegistry.getServiceReferences().length, 1);		
					return plugin.uninstall();
				}).then(function() {
					assert.equal(pluginRegistry.getPlugins().length, 0);
					assert.equal(serviceRegistry.getServiceReferences().length, 0);
					return pluginRegistry.stop();			
				});
			}).then(function() {
				assert.equal("resolved", pluginRegistry.getState());
			});
		});
	};
	
	tests["test reload installed plugin"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var storage = {};
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:storage});
		
		return pluginRegistry.start().then(function() {
			var promise = pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				var pluginInfo = {
					location: plugin.getLocation(),
					data: JSON.parse(storage["plugin." + plugin.getLocation()])
				};
				
				return plugin.start().then(function() {
					assert.equal(pluginRegistry.getPlugins().length, 1);
					assert.equal(serviceRegistry.getServiceReferences().length, 1);
					return plugin.uninstall();
				}).then(function() {
					assert.equal(pluginRegistry.getPlugins().length, 0);
					assert.equal(serviceRegistry.getServiceReferences().length, 0);
					return pluginInfo;
				});
			}).then(function(pluginInfo) {
				return pluginRegistry.installPlugin(pluginInfo.location, pluginInfo.data);
			}).then(function(plugin) {
				return plugin.start().then(function() {
					assert.equal(pluginRegistry.getPlugins().length, 1);
					assert.equal(serviceRegistry.getServiceReferences().length, 1);
					return plugin.uninstall();
				}).then(function() {
					assert.equal(pluginRegistry.getPlugins().length, 0);
					assert.equal(serviceRegistry.getServiceReferences().length, 0);
					return pluginRegistry.stop();
				});
			}).then(function() {
				assert.equal("resolved", pluginRegistry.getState());
			});
			return promise;		
		});

	};
	
	
	tests["test plugin service call"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		
		return pluginRegistry.start().then(function() {
			assert.equal(pluginRegistry.getPlugins().length, 0);
			assert.equal(serviceRegistry.getServiceReferences().length, 0);	
			var promise = pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				return plugin.start({"lazy":true}).then(function() {
					return serviceRegistry.getService("test").test("echo");
				});
			}).then(function(result) {
				assert.equal(result, "echo");
				return pluginRegistry.stop();
			}).then(function() {
				assert.equal("resolved", pluginRegistry.getState());
			});
			return promise;
		});
	};
	
	tests["test plugin service call promise"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		
		var progress = false;
		return pluginRegistry.start().then(function() {
			assert.equal(pluginRegistry.getPlugins().length, 0);
			assert.equal(serviceRegistry.getServiceReferences().length, 0);		
			
			var promise = pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				return plugin.start({"lazy":true}).then(function() {
					return serviceRegistry.getService("test").testPromise("echo");
				});
			}).then(function(result) {
				assert.equal(result, "echo");
				assert.ok(progress);
				return pluginRegistry.stop();
			}, function(error) {
				assert.ok(false);
			}, function (update) {
				assert.equal(update, "progress");
				progress = true;
			});
			return promise;
		});
	};
	
	tests["test plugin service call promise cancel"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		
		return pluginRegistry.start().then(function() {
			assert.equal(pluginRegistry.getPlugins().length, 0);
			assert.equal(serviceRegistry.getServiceReferences().length, 0);	
			var promise = pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				return plugin.start({"lazy":true}).then(function() {
					var cancelPromise = serviceRegistry.getService("test").testCancel();
					cancelPromise.cancel();
					cancelPromise.then(function(result) {
						assert.ok(false);
					}, function(error) {
						assert.ok(cancelPromise.isCanceled());
					}).then(function() {
						return serviceRegistry.getService("test").testCancel(true);
					}).then(function(result) {
						assert.equal(result, "test");
						return pluginRegistry.stop();
					});
				});
			});
			return promise;
		});
	};
		
	
	tests["test plugin event"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		
		return pluginRegistry.start().then(function() {
			assert.equal(pluginRegistry.getPlugins().length, 0);
			assert.equal(serviceRegistry.getServiceReferences().length, 0);
			var eventListenerCalls = 0;
			function eventListener(event) {
				if (event.result === "echotest") {
					eventListenerCalls++;
				}
			}
			
			var promise = pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				return plugin.start({"lazy":true}).then(function() {
					var service = serviceRegistry.getService("test");
					service.addEventListener("echo", eventListener);
					return service.testEvent("echo").then(function() {
						service.removeEventListener("echo", eventListener);
						return service.testEvent("echo");
					});
				});
			}).then(function(result) {
				assert.equal(eventListenerCalls, 1);
				pluginRegistry.stop();
			});
			return promise;
		});
	};
/*	
	tests["test pluginregistry event pluginLoaded - lazy"] = function() {
		var storage = {};
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:storage});
		
		return pluginRegistry.start().then(function() {
			assert.equal(pluginRegistry.getPlugins().length, 0);
			assert.equal(serviceRegistry.getServiceReferences().length, 0);	
		
			var promise = new Deferred();
			pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				return plugin.start({"lazy":true}).then(function() {
				// Dance required to trigger a lazy load: shutdown, recreate (reuse plugin storage), startup
				pluginRegistry.shutdown();
				serviceRegistry = new mServiceregistry.ServiceRegistry();
				pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, storage);
				pluginRegistry.addEventListener("pluginLoaded", function(event) {
					var plugin = event.plugin;
					try {
						assert.ok(!!plugin, "plugin not null");
						assert.equal(plugin.getServiceReferences().length, 1);
						assert.equal(plugin.getServiceReferences()[0].getProperty("name"), "echotest");
						promise.resolve();
					} catch(e) {
						promise.reject(e);
					}
				});
				pluginRegistry.startup(["testPlugin.html"]).then(function() {
					// This service call should trigger pluginLoaded listener
					serviceRegistry.getService("test").test().then(function() {
						plugin.uninstall();
						pluginRegistry.shutdown();
					});
				});
			});
			return promise;
		});
	};

	tests["test pluginregistry event pluginLoaded - non-lazy"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});

		assert.equal(pluginRegistry.getPlugins().length, 0);
		assert.equal(serviceRegistry.getServiceReferences().length, 0);		

		var listenerCalled = false;
		pluginRegistry.addEventListener("pluginLoaded", function() {
			listenerCalled = true;
		});
		return pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
			// Plugin already loaded here, so this is not lazy
			return serviceRegistry.getService("test").test().then(function() {
				assert.ok(listenerCalled, "Listener called");
				pluginRegistry.shutdown();
			});
		});
	};

	// Test ordering guarantee:
	// The testOrdering1() service call injected by our pluginLoaded listener should call back before the original 
	// testOrdering2() service call that triggered the listener.
	tests["test pluginregistry event pluginLoaded service call ordering"] = function() {
		var storage = {};
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:storage});

		assert.equal(pluginRegistry.getPlugins().length, 0);
		assert.equal(serviceRegistry.getServiceReferences().length, 0);		

		return pluginRegistry.installPlugin("testPlugin2.html").then(function(plugin) {
			// To trigger lazy load: shutdown, recreate (reuse plugin storage), startup
			pluginRegistry.shutdown();
			serviceRegistry = new mServiceregistry.ServiceRegistry();
			pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, storage);
			var listenerCalls = 0;
			pluginRegistry.addEventListener("pluginLoaded", function() {
				assert.equal(++listenerCalls, 1);
				// Our loading handler invokes testOrdering2 method of the plugin that is loading
				serviceRegistry.getService("testOrdering").testOrdering1();
			});
			return pluginRegistry.startup(["testPlugin2.html"]).then(function() {
				var service = serviceRegistry.getService("testOrdering");
				// Kicks off the lazy-load process:
				return service.testOrdering2().then(function() {
					// At this point both testOrdering1() (injected) and testOrdering2() should've called back.
					return service.getCallOrder().then(function(order) {
						assert.deepEqual(order, ["testOrdering1", "testOrdering2"], "Service method call order is as expected");
						plugin.uninstall();
						pluginRegistry.shutdown();
					});
				});
			});
		});
	};
*/
	tests["test pluginregistry events started"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		return pluginRegistry.start().then(function() {
			assert.equal(pluginRegistry.getPlugins().length, 0);
			assert.equal(serviceRegistry.getServiceReferences().length, 0);		
			
			var promise = new Deferred();
			pluginRegistry.addEventListener("started", function(event) {
				var plugin = event.plugin;
				try {
					assert.ok(!!plugin, "plugin not null");
						assert.equal(plugin.getServiceReferences().length, 1);
						assert.equal(plugin.getServiceReferences()[0].getProperty("name"), "echotest");
					promise.resolve();
				} catch(e) {
					promise.reject(e);
				}
			});
			pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				return plugin.start();
			});
			return promise;
		});
	};

	tests["test 404 plugin"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		pluginRegistry.init();
		assert.equal(pluginRegistry.getPlugins().length, 0);
		
		var promise = pluginRegistry.installPlugin("badURLPlugin.html").then(function() {
			throw new assert.AssertionError();
		}, function(e) {
			assert.ok(e.message.match(/Load timeout for plugin/));
			assert.equal(pluginRegistry.getPlugins().length, 0);
			return pluginRegistry.stop();
		});
		return promise;
	};
	
	if (navigator && navigator.userAgent && navigator.userAgent.indexOf("WebKit") !== -1) {
		tests["test iframe sandbox"] = function() {
			var serviceRegistry = new mServiceregistry.ServiceRegistry();
			var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
			pluginRegistry.init();
			var plugins = pluginRegistry.getPlugins();
			assert.equal(plugins.length, 0);
			
			var promise = pluginRegistry.installPlugin("testsandbox.html").then(function() {
				throw new assert.AssertionError();
			}, function(e) {
				assert.ok(e.message.match(/Load timeout for plugin/));
				plugins = pluginRegistry.getPlugins();
				assert.equal(plugins.length, 0);
				return pluginRegistry.stop();
			});
			return promise;
		};
	}

	tests["test __plugin__ property"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		return pluginRegistry.start().then(function() {
			return pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
				return plugin.start().then(function() {
					var serviceReferences = serviceRegistry.getServiceReferences("test");
					assert.equal(serviceReferences.length, 1);
					var __plugin__ = serviceReferences[0].getProperty("__plugin__");
					assert.equal(__plugin__, plugin.getLocation());
					return pluginRegistry.stop();
				});
			});
		});
	};
	
	tests["test plugin headers"] = function() {
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:{}});
		pluginRegistry.init();
		return pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
			var headers = plugin.getHeaders();
			assert.equal(headers.name, "test plugin");
			assert.equal(headers.description, "This is a test plugin");
			return pluginRegistry.stop();
		});
	};
	
/*
	tests["test plugin states"] = function() {
		var storage = {};
		var serviceRegistry = new mServiceregistry.ServiceRegistry();
		var pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:storage});
		// Eager-load case
		return pluginRegistry.installPlugin("testPlugin.html").then(function(plugin) {
			var pluginLocation = plugin.getLocation();
			assert.equal(plugin.getState(), "active", "Plugin loaded (eager)");
			pluginRegistry.shutdown();

			// Lazy-load case
			serviceRegistry = new mServiceregistry.ServiceRegistry();
			pluginRegistry = new mPluginregistry.PluginRegistry(serviceRegistry, {storage:storage});
			return pluginRegistry.startup(["testPlugin.html"]).then(function() {
				plugin = pluginRegistry.getPlugin(pluginLocation);
				assert.equal(plugin.getState(), "lazy", "Plugin installed");
				return serviceRegistry.getService("test").test().then(function() {
					assert.equal(plugin.getState(), "active", "Plugin loaded (lazy)");
					plugin.uninstall();
					assert.equal(plugin.getState(), "uninstall", "Plugin uninstalled");
					pluginRegistry.shutdown();
				});
			});
		});
	};
*/
	return tests;
});
