/*
 * Submenu for displaying the list of applications
 * that are capable of opening the type of data
 * specified.
 * 
 * Required parameters to constructor are
 * - isMimeType -> <bool> does this type of data
 * 				   require mime type - true
 * 				   does this type of data require
 * 				   a url type - false 
 * - urlOrMime	-> the url or mime type that this
 * 				   data could correspond to.
 * - controller -> the scene controller that contains
 * 				   the mojo element that this menu corresponds
 * 				   to.
 * - listId		-> the id of the list object the submenu
 * 				   should be attached to.
 * 
 * Assuming the mojo element exists at the time of creating
 * this menu object, all you need to do is create the object.
 * Otherwise you will need to add a listener to the object
 * that should get the open with menu. The method the event
 * should be binded to is shouldOpenOpenWith.
 */
OpenWithMenu = Class.create({
	initialize: function(params){
		this.isMimeType = params.isMimeType;
		this.urlOrMime = params.urlOrMime;
		this.controller = params.controller;
		this.listId = params.listId;
		
		this.pushLoading = false;
		this.loadingModel = [{label:$L("Copy"), command: "openwith-copyCommand"},{label:$L("Open With")}, {label: $L("Loading..."), command: "loading"}];
		this.handlers = [];
		this.bareData = "";
		this.data = null;
		this.originalEventForShowMenu = null;
		this.mimeSystemService = "palm://com.palm.applicationManager";
		this.mimeAppListMethod = "listAllHandlersForMime";
		this.urlAppListMethod = "listAllHandlersForUrl";
		this.launchDefaultFailed = $L("Launching Default Application Failed");
		this.openWithTitle = $L("Open With");
		
		this.controller.listen(this.listId, Mojo.Event.listTap, this.shouldOpenOpenWith.bindAsEventListener(this));
	},
	
	// Determine if the orange key is being pressed
	//
	// If it is we want to show the open with menu
	// If it is not we want to lauch the default app
	shouldOpenOpenWith: function(e) {
		// Pull out the data from the object that was
		// clicked on.
		this.bareData = e.item.freeformAddress;
		var data = {target: this.urlOrMime + e.item.freeformAddress.gsub("\n", " ").gsub("\r", " ")};
		// Was orange pressed
		if (e.originalEvent.down.altKey) {
			this.showMenu(e.originalEvent, data);
		} else {
			// Orange was not pressed so launch default
			this.launchDefault(data);
		}
	},
	
	// Show the open with menu
	//
	// Fetch the list of apps that can
	// handle the urlOrMime type specified
	showMenu: function(event, data){
		// Capture the original event that caused
		// this menu to be displayed. This
		// way we can have the open with menu
		// appear near the original event after
		// the list of apps loads
		this.originalEventForShowMenu = event;
		
		// Display the loading menu
		// until the list of open with 
		// apps is returned
		this.showLoadingMenu(event);
		
		// If any data was sent to this method
		// save it
		if (data) {
			this.data = data;
		}
		
		var params = {};
		var method = "";
		
		if (this.isMimeType) {
			params = {
				mime: this.urlOrMime
			};
			
			method = this.mimeAppListMethod;
			
			
		} else {
			params = {
				url: this.urlOrMime
			};
			
			method = this.urlAppListMethod;
		}
		
		// Get the list of apps that are capable
		// of handling this type of data
		new Mojo.Service.Request(this.mimeSystemService, {
				method: method,
				parameters: params,
				onSuccess: function(e){
					// Parse out the results and show
					// the list of apps in the submenu.
					// 
					// If there was an error it will display
					// an alert saying the mime system
					// failed.
					this.listAllHandlersSuccess(e, event);
				}.bind(this),
				onFailure: function(e){
					// Mime system is not reachable.
					// Display alert indicating this.
					this.displayMimeSystemFailed();
				}.bind(this)
			});
	},
	
	showLoadingMenu: function(event) {
		if (this.pushLoading) {
			// Hide any existing open with menus
			// if they exist
			if (this.popUpMenu) {
				this.popUpMenu.mojo.close();
			}
			
			// Pop up the menu that will hold the list of
			// apps to open with. First display the loading
			// menu. The list of apps will display
			// as soon as they are finished loading.
			this.popUpMenu = this.controller.popupSubmenu({
				onChoose: this.clickedMenu.bindAsEventListener(this),
				placeNear: event.target,
				items: this.loadingModel
			});
		}
	},
	
	// Parse and display the apps that are
	// able to open this data.
	listAllHandlersSuccess: function(result, originalEvent) {

		if (this.popUpMenu) {
			this.popUpMenu.mojo.close();
		}
		
		
		if (result.returnValue) {
			// Reset the handlers list
			// since we have new data
			this.pushHandlersInitialItems();
			
			// There are two possible handler
			// types that can be returned.
			// Check for either and assign
			// result as the valid handler.
			//
			// We don't need any of the data
			// above the handlers anymore
			// so we just write over result
			if (result.redirectHandlers) {
				result = result.redirectHandlers;
			} else if (result.resourceHandlers) {
				result = result.resourceHandlers;
			}

			// If for some reason an app does not
			// have an app name associated with 
			// it we will just use the appId.
			if (result.activeHandler.appName) {
				result.activeHandler.label = result.activeHandler.appName;
			} else {
				result.activeHandler.label = result.activeHandler.appId;
			}
			
			// Set the command of the handler to be equal to the appId.
			// This allows us to use the value that was clicked on the
			// open with menu.
			result.activeHandler.command = result.activeHandler.appId;
			this.handlers.push(result.activeHandler);
			
			// If there is more than one app that can handle this
			// data, those handlers will be set in alternates.
			if (result.alternates) {
				// Go through each alternate, set it up for
				// display in the open with menu, and add it
				// to the handlers array.
				result.alternates.each(function(handler){ 
					if (handler.appName) {
						handler.label = handler.appName;
					} else {
						handler.label = handler.appId;
					}
					handler.command = handler.appId;
					this.handlers.push(handler);
				}.bind(this));
			}
			
			// Display the list of apps that can open 
			// this type of data.
			this.popUpMenu = this.controller.popupSubmenu({
				onChoose: this.clickedMenu.bindAsEventListener(this),
				placeNear: originalEvent.target,
				items: this.handlers
			});
		}
		else {
			// The call to the mime system
			// failed because of an error in
			// the system. Display an alert
			// saying this.
			this.displayMimeSystemFailed();
		}
	},
	
	// Sets the initial items that need to
	// be in the Open With submenu
	pushHandlersInitialItems: function() {
		this.handlers = [];
		this.handlers.push({label:$L("Copy"), command: "openwith-copyCommand"});
		this.handlers.push({label: $L("Open With")});
	},
	
	// Get the app id of the default
	// application for this mime type or url
	getActiveHandlerForDefault: function(result) {
		if (result.returnValue) {
		
			// Return the appId of the default handler 
			// from the Mime systems response
			if (result.redirectHandlers) {
				return result.redirectHandlers.activeHandler.appId;
			} else if (result.resourceHandlers) {
				return result.resourceHandlers.activeHandler.appId;
			}
						
		} else {
			// The call to the mime system
			// failed because of an error in
			// the system. Display an alert
			// saying this.
			this.displayFailedMessage(this.launchDefaultFailed);
			return false;
		}
	},
	
	// Handle a click on the open with menu
	// 
	// If it was not a loading click,
	// launch the app chosen
	clickedMenu: function(appId){		
		if (appId) {
			if (appId == "openwith-copyCommand") {
				this.controller.stageController.setClipboard(this.bareData);
			}
			else if (appId != "loading") {
				this.launchApp(appId, this.data);
			} 
			else {
				this.showLoadingMenu(this.originalEventForShowMenu);
			}
		} 
	},
	
	// Open the default application with
	// the data specified
	launchDefault: function(data){
		this.data = data;
		
		var params = {};
		var method = "";
		
		if (this.isMimeType) {
			params = {
				mime: this.urlOrMime
			};
			
			method = this.mimeAppListMethod;
			
		} else {
			params = {
				url: this.urlOrMime
			};
			
			method = this.urlAppListMethod;
		}
		
		// Get the default application from the
		// mime system.
		new Mojo.Service.Request(this.mimeSystemService, {
				method: method,
				parameters: params,
				onSuccess: function(e){
					var defaultHandler = this.getActiveHandlerForDefault(e);
					if (defaultHandler) {
						this.launchApp(defaultHandler, this.data);
					} else {
						this.displayFailedMessage(this.launchDefaultFailed);
					}
				}.bind(this),
				onFailure: function(e){
					this.displayFailedMessage(this.launchDefaultFailed);
				}.bind(this)
			});
	},
	
	// Open the app with the appid
	// and data that was specified
	launchApp: function(appid, data){	
		
		// HACK for getting around mapto should be maploc in google maps
		if (appid === "com.palm.app.maps") {
			var tempData = data.target.substr(this.urlOrMime.length);
			data.target = "maploc:" + tempData;
		}
		///////////////////////////////////////////////////////////////
				
		new Mojo.Service.Request(this.mimeSystemService, {
			method: 'open',
			parameters: {
				id: appid,
				params: data
			},
			onFailure: function(e){
				this.displayAppLaunchFailed(appid);
			}.bind(this)
		});	
		
	},

	// Display in an alert dialog that
	// launching the specifed app failed
	displayAppLaunchFailed: function(appid) {
		var template = $L("#{appName} Failed To Launch");
		
		var appDisplay = this.getAppNameFromHandlers(appid);
		
		var message = template.interpolate({appName:appDisplay});
		this.displayFailedMessage(message);
	},
	
	// Display in an alert dialog that
	// the Mime system failed to 
	// return without an error
	displayMimeSystemFailed: function() {
		this.displayFailedMessage($L("Failed To Get Applications List"));
	},
	
	// Display in an alert dialog that
	// the Mime system failed because
	// of the reason given in message
	displayFailedMessage: function(message) {
		if (this.popUpMenu) {
			this.popUpMenu.mojo.close();
		}

		this.controller.showAlertDialog({
			message:message,
			title:this.openWithTitle,
			choices:[{label:$L("OK")}]
		})
	},
	
	// Pull out the app name from the 
	// handlers array.
	//
	// If the appid is not found or the
	// handler array is null, return
	// the appid as it was given to 
	// this method.
	getAppNameFromHandlers: function(appid) {
		if (!this.handlers) {
			return appid;
		}
		
		// Run through the handlers trying
		// to find the handler that corresponds
		// to the appid passed to the function.
		var toReturn = this.handlers.find(function(handler){ 
			if (handler.appId == appid) {
				return handler;
			}
		}.bind(this));
		
		// If there was no corresponding
		// handler, just return the appid
		// given to the function.
		if (!toReturn) {
			return appid;
		}
		
		// There is a possibilty that the mime
		// system will not return the label for
		// a handler. If this is the case
		// we must at least return the handler's appId
		if (toReturn.label) {
			return toReturn.label;
		} else {
			return toReturn.appId;
		}
	}
});