/* Copyright 2009 Palm, Inc.  All rights reserved.
 *
 * Depends on an existing application using the mojo framework and prototype.
 * Beware teh globals.
 *
 * Supported scenes need to support the scene parameter "applicationRoot"
 * as a prefix to loading any additional app resources instead of the default
 * application path.
 *
 * Usage:
 * var loader = new PhotosLoader();
 *
 * function loadedOkay() {
 * 	// scenes and js from target application are loaded here.
 * 	loader.push("fullscreen", {
 * 		model: new Photos.Model.Fullscreen(...)
 * 	});
 * }
 *
 * function failed() {
 * 	// cancel push here...
 * }
 *
 * loader.open(loadedOkay.bind(this), failed, this.controller);
 */

try {
	if (PhotosLoader) {
		Mojo.Log.error("PhotosLoader is defined");
	}

} catch(e) {

PhotosLoader = Class.create({
	initialize: function(id, sceneController) {
		this.appId = id || "com.palm.app.photos";
		this.controller = sceneController;
	},

	// major will be for new features, major changes, api breaks
	// minor will be for api breaks, but similar functionality
	// release is for bug fixes.
	version: function() {
		var ret = {major: 1, minor: 0, release: 2};
		ret.value = ret.major + "." + ret.minor + "." + ret.release;
		return ret;
	},

	// TODO: loads everything for now, could be more selective about
	// specific scenes.
	open: function(okay, fail, sceneController, appId) {
		var localApp = appId || this.appId;
		var localController = sceneController || this.controller;

		if (!localController) {
			throw "You must specify a sceneController during " +
				"initialize or during open()";
		}

		function gotSources(response) {
			var f = new PhotosLoader.GFUNKERA();
			var scripts = [];
			var sources = response.responseJSON;
			for (var s = 0; s < sources.length; s++) {
				var source = sources[s].source;
				var sheet = sources[s].stylesheet;

				if (sheet) {
					this.sheet = sheet;
					continue;
				}

				if (!source ||
					source.indexOf("app-assistant") != -1 ||
					source.indexOf("stage-assistant") != -1) {
					continue;
				}
				// want to put scripts in the root window
				var script = document.createElement('script');
				scripts[scripts.length] = script;
				script.onload = f.add();
				script.onerror = fail;
				script.src = this.root + source;
				Mojo.Log.info("child " + script.src);
			}


			Mojo.Log.info("Adding all children " + scripts.length);
			// want to put scripts in the root window
			var fragment = document.createDocumentFragment();
			for (var as = 0; as < scripts.length; as++) {
				fragment.appendChild(scripts[as]);
			}
                        var strings = Mojo.Locale.readStringTable(
				null, null,
				'file://' + this.root + '/resources');
			Mojo.Locale.strings =
				Mojo.Locale.mergeObjectStringTables(
					[Mojo.Locale.strings, strings]);

			var stageController = localController &&
				localController.stageController;
			if (stageController &&
				!stageController.PhotosLoaderLoadedStyles) {
				stageController.PhotosLoaderLoadedStyles = true;
				stageController.loadStylesheet(
					this.root + this.sheet);
			}
			document.body.appendChild(fragment);
			Mojo.Log.info("waiting for loads: ");
			f.wait(okay);
		}

		function noSources() {
			Mojo.Log.error("Failed to load sources.json");
			fail();
		}

		function loadSources() {
			new Ajax.Request(this.root + 'sources.json', {
				method: 'get',
				sanitizeJSON: true,
				evalJSON: "force",
				onSuccess: gotSources.bind(this),
				onFailure: noSources
			});
		}

		function gotApps(response) {
			for (var a = 0; a < response.apps.length; a++) {
				var app = response.apps[a];
				if (app.id === localApp) {
					var path = app.main;
					if (path.startsWith('file://')) {
						path = path.substr(
							'file://'.length);
					}
					this.root = path.substr(
						0, path.lastIndexOf('/') + 1);
					loadSources.bind(this)();
					return;
				}
			}
			Mojo.Log.error("appId was never found in app list!");
			fail();
		}

		function noApps(response) {
			Mojo.Log.error("Failed to get applications!");
			fail();
		}
		
		return localController.serviceRequest(
			'palm://com.palm.applicationManager', {
				method: "listApps",
				onSuccess: gotApps.bind(this),
				onFailure: noApps.bind(this)
			}
		);
	},

	push: function(scene, params, stageController) {
		if (!this.root) {
			throw "You must have a valid root to push!";
			return;
		}

		var stage = stageController || this.controller.stageController;
		if (!stage) {
			throw "No stageController found for loader!";
			return;
		}

		var sceneParams;
		var fullParams = params;
		if (typeof scene === "object") {
			sceneParams = scene;
		} else if (typeof scene === "string") {
			sceneParams = {name: scene};
		} else {
			throw "PhotosLoader.push() has invalid params.";
			return;
		}
		sceneParams.assistantConstructor =
			Photos.Scene[sceneParams.name.capitalize()];
		sceneParams.sceneTemplate =
			this.root + "app/views/" + sceneParams.name + '/' +
			sceneParams.name + '-scene';
		fullParams.applicationRoot = this.root;

		Mojo.Log.info("Template dir: " + sceneParams.sceneTemplate);
		if (this.sheet && !stage.PhotosLoaderLoadedStyles) {
			// want to put the stylesheet in the
			// specific window document
			stage.PhotosLoaderLoadedStyles = true;
			stage.loadStylesheet(this.root + this.sheet);
		}
		stage.pushScene(sceneParams, fullParams);
	}
});

PhotosLoader.GFUNKERA = function() {
	var dependencies = 0;
	var sofar = 0;
	var that = this;
	var waiting = false;
	var ready = null;

	function wrapper(proceed) {
		// proceed functions have additional parameters passed to them
		// apply them to the real function
		args = $A(arguments);
		args.shift();
		proceed.apply(null, args);
		if (++sofar === dependencies && waiting && ready) {
			ready();
		}
	}

	function empty() {}

	return {
		/*
		 * Optionally takes a callback function to be called
		 * pending completion of subTask
		 */
		add: function(subDone) {
			dependencies++;
			return (subDone || empty).wrap(wrapper);
		},

		wait: function(func) {
			var done = func || empty;
			waiting = true;
			if (sofar === dependencies) {
				// incase our deps finished first.
				done();
			} else {
				ready = done;
			}
		}
	};
};

// TODO: deprecated.
NewHotnessLoader = PhotosLoader;

}

