/* Copyright 2009 Palm, Inc.  All rights reserved. */

var BtoppAssistant = Class.create({
	initialize: function(params) {
		// Get the Bluetooth state from the app assistant
		this.bt = Mojo.Controller.getAppController().assistant.bt;
		
		// Register this assistant as being interested in BT events
		this.btEventHandler = this.handleBtEvent.bind(this);
		this.bt.registerAssistant(this.btEventHandler);
		
		// Messages that can be shown to the user
		var selectMsg = $L("1#Select a car kit to send the contact to:|#Select a car kit to send the contacts to:");
		var sendingMsg = $L("1#Sending contact to #{name}...|#Sending contacts to #{name}...");
		var successMsg = $L("1#Send contact complete|#Send contacts complete");
		var errorMsg = $L("1#Unable to send contacts|#Unable to send contacts");

		// Initialize the parameters
		this.params = params || {};
		if (!this.params.num)
			this.params.num = 1;
		// Create the messages, if they weren't provided
		if (!this.params.selectMsg)
			this.params.selectMsg = Mojo.Format.formatChoice(this.params.num, selectMsg, {name: "#{name}"});
		if (!this.params.sendingMsg)
			this.params.sendingMsg = Mojo.Format.formatChoice(this.params.num, sendingMsg, {name: "#{name}"});
		if (!this.params.successMsg)
			this.params.successMsg = Mojo.Format.formatChoice(this.params.num, successMsg, {name: "#{name}"});
		if (!this.params.errorMsg)
			this.params.errorMsg = Mojo.Format.formatChoice(this.params.num, errorMsg, {name: "#{name}"});
		if (!this.params.type)
			this.params.type = "text/x-vcard";
		if (this.params.deleteWhenDone == undefined)
			this.params.deleteWhenDone = true;
		if (!this.params.file)
			this.params.file = "/var/tmp/bt/multiple.vcf";
		Mojo.Log.info("@@@@@@@ params=" + Object.toJSON(this.params));
			
		// Initialize the variables
		this.trustedDeviceList = [];
		this.defaultMessageTimer = null;
		this.setupComplete = false;
		this.setupCompleteCallback = null;
		this.btStarted = false;
		this.isFullScreen = true;
		this.scenePopped = false;
	},
	
	setup: function() {
		Mojo.Log.info("@@@@@@@ setup @@@@@@@");
		this.oppTrustedListWidget = this.controller.get('oppTrustedList');
		this.oppMsgArea = this.controller.get('oppMsgArea');
		this.oppAreaSpinner = this.controller.get('bt_opp_spinner');
		this.oppSpinnerWidget = this.controller.get('oppspinner');
		this.oppSendingView = this.controller.get('oppSendingView');
		this.oppDevicesView = this.controller.get('oppDevicesView');
		this.actionButton = this.controller.get('action');

		if (this.bt.uiDebugMode)
			this.frameworkDebug();

		this.progressAttributes = {
		        modelProperty: "progress"
		};
		this.progressModel = {
		    progress: 0.0,
			percentDisplayed: 0,
			percent: 0,
		};
		this.controller.setupWidget('oppTransferProgress', this.progressAttributes, this.progressModel);

		this.trustedListModel = {
			listTitle: $L('Car kits'),
            items: this.trustedDeviceList
        };
        this.template = {
            itemTemplate: 'btopp/listitem',
            listTemplate: 'btopp/listcontainer',
            addItemLabel: $L("Add car kit"),
			swipeToDelete: false,
        };
		this.controller.setupWidget('oppTrustedList', this.template, this.trustedListModel);
		this.controller.setupWidget('oppSpinner', {property: 'oppSpinning'}, this.trustedListModel);
		
		// App Menu - Remove default items and add only Edit & Help items.
		var appMenuModel = {
	        visible: true,
            items: [Mojo.Menu.editItem, {label:$L('Help'), command:Mojo.Menu.helpCmd}]
        };
		this.controller.setupWidget(Mojo.Menu.appMenu, {omitDefaultItems:true}, appMenuModel);
		
		Mojo.Event.listen(this.oppTrustedListWidget, Mojo.Event.listAdd, this.listAddHandler.bindAsEventListener(this), false);
		Mojo.Event.listen(this.oppTrustedListWidget, Mojo.Event.listTap, this.listTapHandler.bindAsEventListener(this), false);
		
		this.btAppHasFocusHandler = this.btAppHasFocus.bind(this);
		this.btAppHasLostFocusHandler = this.btAppHasLostFocus.bind(this);
		Mojo.Event.listen(this.controller.document, Mojo.Event.activate, this.btAppHasFocusHandler, false);
		Mojo.Event.listen(this.controller.document, Mojo.Event.deactivate, this.btAppHasLostFocusHandler, false);
		
		// Listen for taps on the button
		Mojo.Event.listen(this.actionButton, Mojo.Event.tap, this.handleButtonTap.bind(this), false);
		
		// Connect to the service, register for notifications and get current status
		this.btStarted = this.bt.startBT();
		
		// Add the app icon
		this.controller.get('headerIcon').addClassName('contacts');
		
		// If BT has already started then don't delay scene activation
		if (this.btStarted)
			this.setupComplete = true;
			
		// If Bluetooth is turning on, then show the "BT turning on" message
		if (this.bt.isRadioTurningOn)
			this.handleBtEvent(Bluetooth.radioTurningOn);
	},
	
	activate: function(params) {		
		Mojo.Log.info("@@@@@@@ BT activate @@@@@@@ ");
        // If BT was already started, get the status now
        if (this.btStarted) {
            this.handleBtEvent(Bluetooth.trustedDevices);
            this.btStarted = false;
        }
	},
	
	// Handle Bluetooth events
	handleBtEvent: function(event, data) {
		Mojo.Log.info("@@@@@@@ handleBtEvent: " + event);
		switch (event) {
			case Bluetooth.serviceDown:
				// The service has gone down so pop this scene
				if (!this.bt.uiDebugMode)
					this.popScene();
				break;
				
			case Bluetooth.delayActivation:
				this.setupComplete = false;
				break;
				
			case Bluetooth.allowActivation:
				// Is this scene going straight to the Inquiry scene?
				if (this.preventActivation) {
					Mojo.Log.info("@@@@@@@ allowActivation has been prevented")
					this.preventActivation = false;
					break;
				}
				// Allow the UI to draw if it hasn't already
				this.setupComplete = true;
				if (this.setupCompleteCallback) {
					Mojo.Log.info("@@@@@@@ Calling setupCompleteCallback ...")
					this.setupCompleteCallback();
					this.setupCompleteCallback = null;
				}
				break;
				
			case Bluetooth.radioTurningOn:
				this.showStatusMessage($L("Bluetooth is turning on ..."), false, true, 0);
				this.oppDevicesView.hide();
				break;
				
			case Bluetooth.radioTurningOff:
				// The radio is turning off
				this.popScene();
				break;
			
			case Bluetooth.radioOnAndReady:
				this.showDefaultMessage();
				this.oppDevicesView.show();
				break;
				
			case Bluetooth.trustedDevices:
				// Empty the list first
				this.trustedDeviceList.clear();
				for (var i = 0; i < this.bt.trustedDevices.length; i++)
					this.addTrustedDevice(this.bt.trustedDevices[i]);
				// Update the Model to refresh the UI
				Mojo.Log.info("@@@@@@@ Bluetooth.trustedDevices: " + Object.toJSON(this.trustedListModel));
				this.controller.modelChanged(this.trustedListModel, this);
				
				// If there are no trusted car kits then start inquiry now
				if (this.trustedDeviceList.length == 0) {
					var activeScene = this.controller.stageController.activeScene();
					if (this.bt.isRadioOn && (!activeScene || (activeScene && activeScene.sceneName != 'inquiry'))) {
						if (!activeScene)
							this.preventActivation = true;
						this.listAddHandler();
					}
				}
				else {
					// Is there a newly paired car kit?
					if (this.newlyPairedCarkit) {
						// Wait 10 seconds before sending data to the car kit
						if (this.connectToNewlyPairedCarkitTimer)
							clearTimeout(this.connectToNewlyPairedCarkitTimer);
						this.connectToNewlyPairedCarkitTimer = setTimeout(this.connectToNewlyPairedCarkit.bind(this), 10000);
					}
				}
				break;
				
			case Bluetooth.pairedDevice:
				// A device pairing has just been completed.  
				// Send once the trusted list has been returned if this app is full screen
				if (this.isFullScreen)
					this.newlyPairedCarkit = data;
				break;
				
			case Bluetooth.deviceDeleted:
				var i;
				for (i = 0; i< this.trustedDeviceList.length; i++) {
					if (this.trustedDeviceList[i].address == data)
						break;
				}
				if (i < this.trustedDeviceList.length) {
					// Remove the device from the model
					this.trustedDeviceList.splice(i, 1);
					
					// Update the UI too
					this.oppTrustedListWidget.mojo.noticeRemovedItems(i, 1);
				}
				break;
				
			case Bluetooth.deviceRenamed:
				// A device has been renamed
				this.updateTrustedDeviceDisplay(data);
				break;
				
			case Bluetooth.radioOff:
				// If the radio has turned off then pop this scene
				if (!this.bt.uiDebugMode)
					this.popScene();
				break;
			
			case Bluetooth.updateTrustedStatus:
				// Is the device connected?
				var device = this.getDeviceFromTrustedList(data);
				if (device && device.CONNECTSTATE_OPP === 'connected' && !this.progressTimer) {
					// Hide the list of trusted devices
					this.oppDevicesView.hide();
					// Show the OPP progress meter
					this.actionButton.hide();
					this.oppSendingView.show();
					this.controller.showWidgetContainer(this.oppSendingView);
					this.actionButton.show();
					
					// Start a timer to smoothly update the progress bar
					this.progressTimer = this.controller.window.setInterval(this.updateProgressBar.bind(this), 40);
					
					// Show the "sending ..." message
					this.showStatusMessage(this.params.sendingMsg.interpolate(device), false, false, 0);
					
					// Set the button to "Cancel"
					this.actionButton.innerHTML = $L('Cancel');
				}
				// Show a spinner if we're waiting to connect to this car kit
				if (device && data == this.newlyPairedCarkit)
					device.oppSpinning = true;
				this.updateTrustedDeviceDisplay(data);
				break;
				
			case Bluetooth.cannotConnectOPP:
				this.showStatusMessage($L("Unable to connect."), true, false, 5000);
				break;
				
			case Bluetooth.oppProgressPercent:
				Mojo.Log.info("@@@@@@@ Bluetooth.oppProgressPercent = " + data);
				this.progressModel.percent = data;
				break;
				
			case Bluetooth.oppError:
				// There was an error sending the data
				this.showStatusMessage(this.params.errorMsg, true, false, 0);
				// Set the button to "OK"
				this.actionButton.innerHTML = $L('OK');
				// Stop the timer
				if (this.progressTimer) {
					this.controller.window.clearInterval(this.progressTimer);
					this.progressTimer = null;
				}
				break;
				
			case Bluetooth.oppNotSupported:
				this.controller.showAlertDialog({
					title: $L("Unable to send"),
					message: $L("Make sure the destination device supports Object Push Profile (OPP) and is in receive mode."),
					choices:[{label:$L('OK'), type:'dismiss'}]
				});
				break;
		}
	},
	
	// Cleanup because this scene is no longer running
	cleanup: function() {	
		// BT OPP scene is no longer running
		Mojo.Log.info("@@@@@@@ BT Cleanup @@@@@@@");
		
		// Cancel the transfer
		this.bt.cancelOPP();
		
		// Unregister this assistant from interesting BT events
		this.bt.unregisterAssistant(this.btEventHandler);

		// Stop listening for window focus events
		Mojo.Event.stopListening(this.controller.document, Mojo.Event.activate, this.btAppHasFocusHandler, false);
		Mojo.Event.stopListening(this.controller.document, Mojo.Event.deactivate, this.btAppHasLostFocusHandler, false);
	},
	
	// Main Bluetooth scene is no longer top scene	
	deactivate: function() {
		Mojo.Log.info("@@@@@@@ BT deactivate @@@@@@@");
	},
	
	// Pop this scene
	popScene: function() {
		if (!this.scenePopped) {
			this.controller.stageController.popScene();
			this.scenePopped = true;
		}
	},
	
	listTapHandler: function(event) {
		// User tapped on a trusted device
		Mojo.Log.info("@@@@@@@ listTapHandler: " + event.item.address);
		this.handleTapOnTrustedList(event.item.address);
	},
	
	listAddHandler: function(event) {
		// User tapped on "Add device"
		Mojo.Log.info("@@@@@@@ listAddHandler @@@@@@@");
		// Tell other scenes to abort inquiry
		this.bt.popInquiryScene({relaunchingInquiry:true});
		this.controller.stageController.pushScene('inquiry', {carkitsOnly:true, hideOnOffToggle:true, title:$L("Send via Bluetooth")});		
	},

	// The user either tapped 'Cancel' or 'Done'
	handleButtonTap: function() {
		// Pop this scene
		this.popScene();
	},
	
	// BT App is running full-screen 
	btAppHasFocus: function(event) {
		// Make sure the event is for this scene
		if (event.target != this.controller.document)
			return
		Mojo.Log.info("@@@@@@@ btOppHasFocus @@@@@@@");
		this.isFullScreen = true;
	
	    // Allow pairing requests 
		this.bt.allowPairing(true);
	},

	// BT App is not running full-screen anymore 
	btAppHasLostFocus: function(event) { 
		// Make sure the event is for this scene
		if (event.target != this.controller.document)
			return
		Mojo.Log.info("@@@@@@@ btAppHasLostFocus @@@@@@@");
		this.isFullScreen = false;
	
	    // Pairing is no longer allowed 
		this.bt.allowPairing(false);

		// If currently pairing, then the Inquiry scene should pop itself
		this.bt.popInquirySceneIfPairing();

		// Close the pairing dialog box if it is still showing
		this.bt.closePairingDialogBox();
	},

	// Connect to the newly paired car kit once the timer fires
	connectToNewlyPairedCarkit: function(addr) {
		// The timer fired.  Clear it
		this.connectToNewlyPairedCarkitTimer = null;
		
		// Connect to the car kit now
		this.handleTapOnTrustedList(this.newlyPairedCarkit);
		
		// Clear the newely paired car kit 
		this.newlyPairedCarkit = null;
	},
	
	// Show the default message in the message box
	showDefaultMessage: function() {
		this.showStatusMessage(this.params.selectMsg, false, false, 0);
	},

	// Show the status message in the message area
	showStatusMessage: function(msg, error, spinner, timeToDisplayMsg) {
		Mojo.Log.info("@@@@@@@ showStatusMessage: msg=" + msg + " error=" + error + " time=" + timeToDisplayMsg);
		// Display the message
		this.oppMsgArea.innerHTML = msg;
		
		// Is this an error message?
		if (error)
			this.oppMsgArea.addClassName('error');
		else
			this.oppMsgArea.removeClassName('error');
	
		// Should the spinner be displayed?
		if (spinner) {
			this.oppAreaSpinner.addClassName('active');
			this.oppSpinnerWidget.mojo.start();
		}
		else {
			this.oppAreaSpinner.removeClassName('active');
			this.oppSpinnerWidget.mojo.stop();		
		}

		// If there is currently a timer to display the default message then clear it now
		if (this.defaultMessageTimer) {
			clearTimeout(this.defaultMessageTimer);
			this.defaultMessageTimer = null;
		}
		
		// Should the default message be displayed after a while?
		if (timeToDisplayMsg)
			this.defaultMessageTimer = setTimeout(this.showDefaultMessage.bind(this), timeToDisplayMsg);	
	},

	// Add a trusted device to the list of trusted devices
	addTrustedDevice: function(payload) {
		Mojo.Log.info("@@@@@@@ addTrustedDevice: " + Object.toJSON(payload));
		
		// Only show devices that support OPP
		if (!isOPPSupported(payload.cod))
			return;
				
		// Insert CoD icon
		payload.ICON = getIconFile(payload.cod, payload.name);
		
		// The car kit is an audio device
		payload.DEVICETYPE = 'Audio';
			
		payload.HIGHLIGHT = 'x-mojo-tap-highlight="momentary"';
		if (!isOPPSupported(payload.cod)) {
			payload.DISABLED = 'disabled ';
			payload.HIGHLIGHT = '';
		}
		
		// Add it to the list array
		this.trustedDeviceList.push(payload);
	
		// Add the elements that show connected status
		this.bt.updateDeviceConnectedState(payload.address);
	},

	getDeviceFromTrustedList: function(addr) {
		if (!addr)
			return undefined;
		for (var i = 0; i< this.trustedDeviceList.length; i++) {
			if (this.trustedDeviceList[i].address == addr) {
				return this.trustedDeviceList[i];
			}									
		}
		return undefined;	
	},

	updateTrustedDeviceDisplay: function(addr) {
		for (var i = 0; i< this.trustedDeviceList.length; i++) {
			if (this.trustedDeviceList[i].address == addr) {
				this.oppTrustedListWidget.mojo.noticeUpdatedItems(i, [this.trustedDeviceList[i]]);
				break;
			}									
		}
	},
	
	// Connect or disconnect the device that the user tapped on
	handleTapOnTrustedList: function(addr) {
		Mojo.Log.info("@@@@@@@ handleTapOnTrustedList: " + addr);
		if (this.bt.uiDebugMode) {
			// Find the device that was tapped on
			var device = this.getDeviceFromTrustedList(addr);
			if (!device)
				return;
			
			if (device.oppSpinning)
				device.oppSpinning = false;
			else
				device.oppSpinning = true;
			this.updateTrustedDeviceDisplay(addr);
			return;
		}
		
		var device = this.getDeviceFromTrustedList(addr);
		if (!device)
			return;
		if (device.DISABLED)
			return;

		// If currently connecting, disconnect OPP - otherwise initiate a connection
		if (device.CONNECTSTATE_OPP === 'connecting') {
			device.CONNECTSTATE_OPP = 'disconnected';
			device.oppSpinning = false;
			this.bt.cancelOPP();
		}
		else {
			// Show that this device is connecting
			device.CONNECTSTATE_OPP = 'connecting';
			device.oppSpinning = true;
			this.bt.sendOppData(addr, this.params.file, this.params.type, this.params.deleteWhenDone);
		}
		// Show the new state immediately
		this.updateTrustedDeviceDisplay(addr);
	},
	
	// Update the progress frequently to smooth the animation
	updateProgressBar: function() {
		Mojo.Log.info("@@@@@@@ updateProgressBar: percent=" +this.progressModel.percent + " displayed = " + this.progressModel.percentDisplayed);

		// Is the progress complete?
		if (this.progressModel.percentDisplayed == 100) {
			// Stop the timer
			if (this.progressTimer) {
				this.controller.window.clearInterval(this.progressTimer);
				this.progressTimer = null;
			}
				
			// Display the success message
			this.showStatusMessage(this.params.successMsg, false, false, 0);
			
			// Set the button to "OK"
			this.actionButton.innerHTML = $L('OK');
		}

		// Move the progress bar by 2 percent
		if (this.progressModel.percent > this.progressModel.percentDisplayed) {
			this.progressModel.percentDisplayed+= 2;
			this.progressModel.progress = this.progressModel.percentDisplayed / 100.0;
			this.controller.modelChanged(this.progressModel);
		}
	},

	handleCommand: function(event) {
		if (event.type == Mojo.Event.command) {
			if (event.command == Mojo.Menu.helpCmd) {
				 this.bt.btMojoService("palm://com.palm.applicationManager/open",
                                  {'id': 'com.palm.app.help','params': {'target': 'http://help.palm.com/bluetooth/index.html'}});       
			}
		}
	},	
	
	frameworkDebug: function() {
		// Populate the lists with dummy data so that framework and UI can be debugged
		this.trustedDeviceList = [
			{address:"00:11:22:33:44:55", cod:"1024", ICON:'headset', name:"HFG is now connected", DEVICETYPE:'Audio', DISABLED:'', CONNECTSTATE:'connected', CONNECTSTATE_HFG:'connected', SHOW_PAN_ICON:'', oppSpinning:false},
			{address:"00:11:22:33:44:56", cod:"1024", ICON:'headset_a2dp', name:"HFG and A2DP both up!", DEVICETYPE:'Audio', DISABLED:'', CONNECTSTATE:'connected', CONNECTSTATE_HFG:'connected', CONNECTSTATE_A2DP:'connected', SHOW_PAN_ICON:'', oppSpinning:false},
			{address:"01:11:22:33:44:57", cod:"1024", ICON:'carkit', name:"Carkit is spinning", DEVICETYPE:'Audio', DISABLED:'', CONNECTSTATE:'connecting', SHOW_PAN_ICON:'', oppSpinning:true},
			{address:"02:11:22:33:44:58", cod:"1024", ICON:'a2dp', name:"Disconnected Audio device", DEVICETYPE:'Audio', DISABLED:'', CONNECTSTATE:'disconnected', SHOW_PAN_ICON:'', oppSpinning:false},
			{address:"03:11:22:33:44:59", cod:"1024", ICON:'phone', name:"Phone not enabled today", DEVICETYPE:'', DISABLED:'disabled', CONNECTSTATE:'disconnected', SHOW_PAN_ICON:'', oppSpinning:false},
			{address:"04:11:22:33:44:60", cod:"1024", ICON:'computer', name:"Laptop with PAN", DEVICETYPE:'', DISABLED:'disabled ', CONNECTSTATE:'connected', SHOW_PAN_ICON:'enabled', oppSpinning:false},
			{address:"05:11:22:33:44:61", cod:"1024", ICON:'computer', name:"Laptop Spin", DEVICETYPE:'', DISABLED:'disabled ', CONNECTSTATE:'connecting', SHOW_PAN_ICON:'', oppSpinning:true},
		];
	},
});
