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

DetailAssistant = Class.create({
	initialize: function(params){
		this.focusWindow = params.focusWindow;
		//Mojo.Log.info("DetailAssistant.initialize: params are %j ", params);
		Mojo.require(params.personId || params.contact, "Details was pushed on with no person ID or contact to render.");
		this.isPseudoCard = params.isPseudoCard;
		this.launchType = params.launchType; //Used to tell if you were launched from the launcher, so we know if there's a problem we can delete the icon
		var targetId = params.personId;
		var contact = params.contact;
		if (contact) {
			this.contact = new Contact(contact);
		}
		
		this.targetId = targetId;
		this.suppressMenuItems = params.suppressMenuItems;
		this.person = {};
		this.isFirstLinking = true;
		
	},
	
	updateId:function(personId){
		if(this.targetId == personId){
			return;
		}
		this.targetId = personId;
		this.controller.stageController.swapScene({
			name: "detail"
		}, {personId: this.targetId, contact:this.contact})
	},
	
	moreDetailNames: ["birthday", "spouse", "children", "nickname"],
	
	renderContactHideEditUntilSaveSuccessful: function(response) {
		this.renderContact(response);
		
		this.cmdMenuModel.items = [];
		this.controller.modelChanged(this.cmdMenuModel);		
	},
	
	renderContact: function(response){
		if(response.list.length < 1){
			Mojo.Log.info("No actual linked contacts, you must have deleted the last one.")
			this.controller.stageController.popScenesTo('list')
			return;
		}
		response.list.each(function(c){
			//This might be a little costly, but we need the contact to be the same object that is in the linkedContacts arrays
			Object.extend(c, new Contact(c));
		});
		this.linkedContacts = response.list;
		if (this.linkedContactsWithAccountIDs) 
			this.giveContactsAccountIds();
		this.renderLinked(response.list);
		this.person = new Contact(response.person);
		this.cmdMenuModel.items = this.commandMenuModel;
		this.controller.modelChanged(this.cmdMenuModel);
		//We're going to use this to hold more details entries that we've already used so we don't show dupes
		var moreDetailsEntries = {
			"nickname": [],
			"birthday": [],
			"spouse": [],
			"children": []
		}
		
		if (response.list.length > 1) {
			this.person.isClipped = "clipped";
		}
		this.renderHeader(this.person, this.linkedContacts);
		
		var that = this;
		
		var moreDetails = [];
		var notes = [];
		["emailAddresses", "phoneNumbers", "imNames", "urls", "addresses"].each(function(property){
			that.person[property] = [];
		});
		
		this.extendAndCollapseAssociates(this.person, response.list);
		
		response.list.each(function(linked){
			for (var i = 0; i < that.moreDetailNames.length; i++) {
				var property = that.moreDetailNames[i];
				var value;
				if (property == "birthday" && linked[property]) {
					if (linked[property] != "0") {
						value = Utilities.parseBirthday(linked[property]);
					}
				}
				else {
					value = linked[property];
				}
				if (value && moreDetailsEntries[property].indexOf(value) == -1) {
					moreDetailsEntries[property].push(value)
					var localizedProperty;
					switch(property){
						case "birthday":
							localizedProperty = $L("birthday");
							break;
						case "nickname":
							localizedProperty = $L("nickname");
							break;
						case "children":
							localizedProperty = $L("children");
							break;
						case "spouse":
							localizedProperty = $L("spouse");
							break;
					}
					moreDetails.push({
						key: localizedProperty,
						value: value
					});
				}
			}
			if (linked.notes) 
				notes.push({
					notes: linked.notes.gsub("\n", "<br />")
				});
		});
		
		this.notesModel.items = notes;
		this.notesUpdate(this.notesModel.items);
		this.moredetailsModel.items = moreDetails;
		this.moredetailsUpdate(this.moredetailsModel.items);
		this.renderContactPoints(this.person);
		this.showDeferrals();
		
		
						
		if((this.person.reminder && this.person.reminder.length > 0)){
			this.reminderContainer.update(Mojo.View.render({
				template:'detail/reminder',
				object:this.person
			}))
			this.reminderContainer.show();
		} else {
			this.reminderContainer.hide();
		}
		               
		if (!this.person.companyName) {
			this.controller.select('div.company').each(Element.hide);
		}
		
		if (this.person.companyName && !(this.person.jobTitle)) {
			this.controller.select('span.comma').each(Element.hide);
		}
		this.checkForLaunchpoint();
		
		this.controller.instantiateChildWidgets(this.controller.get('lists'));
		if (this.aboutToActivateCallback) {
			Mojo.Log.info("calling aboutToActivateCallback")
			this.aboutToActivateCallback();
		}
		this.checkForSIM();
	},
	
	renderHeader: function(person, linkedContacts){
		person.fullName = person.getFullName() || person.displayText || "";

		person.workInfoLine = this.getWorkInfoLine(person);
		if (linkedContacts) {
			person.workInfoLine = this.getWorkInfoLines(linkedContacts);
			person.contactCount = linkedContacts.length;
		}
		
		//If there's no name and there is a company, let's promote that up to the name field.
		if(person.fullName.blank() && person.workInfoLine && !person.workInfoLine.blank()){
			person.fullName = person.workInfoLine;
			person.workInfoLine = "";
		}
		var headerParams = {
			isClipped: person.isClipped,
			pic: person.pic || Contact.DEFAULT_DETAILS_AVATAR,
			contactCount: person.contactCount,
			fullName: person.fullName,
			nickname: person.nickname,
			workInfoLine: person.workInfoLine
		}
		this.header.update(Mojo.View.render({
			template: 'detail/header',
			object: headerParams
		}));
	},
	
	renderContactPoints: function(){
		this.renderPhones({
			list: this.person.phoneNumbers
		});
		this.renderIMNames({
			list: this.person.imNames
		});
		this.renderEmails({
			list: this.person.emailAddresses
		});
		this.renderAddresses({
			list: this.person.addresses
		});
		this.renderUrls({
			list: this.person.urls
		});
	},
	
	updateOrCreateListWidget: function(model, divId, attrs){
		var flag = divId + 'Setup';
		if (!this[flag]) {
		
			if (model.items.length == 0) {
				Mojo.Log.info("skipping init of widget with no data: " + divId);
			}
			else {
				Mojo.Log.info("initializing widget: " + divId);
				
				var div = this.controller.get(divId);
				div.setAttribute('x-mojo-element', div.getAttribute('lazy-mojo-element'));
				this.controller.setupWidget(divId, attrs, model);
				this[flag] = true;
			}
		}
		else {
			this.controller.modelChanged(model);
		}
	},
	
	showDeferrals: function(){
		if (this.shown) 
			return;
		var deferrals = this.controller.select(".new-defer");
		for (var i = 0; i < deferrals.length; i++) {
			deferrals[i].setStyle({
				"display": "inherit"
			});
			this.controller.showWidgetContainer(deferrals[i]);
		}
		this.shown = true;
	},
	
	renderPhones: function(response){
		this.formattedPhones = response.list.collect(ContactPointDecorator.phoneFormatter);
		this.phoneModel.items = this.formattedPhones;
		
		this.phoneUpdate(this.phoneModel.items);
		
		if(this.formattedPhones.length < 1){
			this.sendContactItem.items = this.sendContactWithoutPhones;
		} else {
			this.sendContactItem.items = this.sendContactWithPhones;
		}
	},
	
	renderEmails: function(response){
		this.formattedEmails = [];
		for (var i = 0; i < response.list.length; i++) {
			this.formattedEmails.push(ContactPointDecorator.emailFormatter(response.list[i]));
		}
		this.emailModel.items = this.formattedEmails;
		
		this.emailUpdate(this.emailModel.items);
	},
	
	renderIMNames: function(response){
		this.formattedIms = [];
		
		for (var i = 0; i < response.list.length; i++) {
			this.formattedIms.push(ContactPointDecorator.messagingFormatter(response.list[i]));
		}
		this.messagingModel.items = this.formattedIms;
		this.messagingUpdate(this.messagingModel.items);
	},
	
	renderAddresses: function(response){
		this.formattedAddresses = [];
		for (var i = 0; i < response.list.length; i++) {
			this.formattedAddresses.push(ContactPointDecorator.addressFormatter(response.list[i]));
		}
		this.addressModel.items = this.formattedAddresses;
		this.addressUpdate(this.addressModel.items);
	},
	
	renderUrls: function(response){
		this.formattedUrls = [];
		for (var i = 0; i < response.list.length; i++) {
			this.formattedUrls.push(ContactPointDecorator.urlFormatter(response.list[i]));
		}
		this.urlModel.items = this.formattedUrls;
		this.urlUpdate(this.urlModel.items);
	},
	
	getWorkInfoLines: function(contacts){
		var that = this;
		var ret = "";
		contacts.each(function(c){
			var workline = that.getWorkInfoLine(c);
			if (!workline.blank()) {
				ret = workline;
				throw $break;
			}
		});
		return ret;
	},
	
	getWorkInfoLine: function(c){
		var workItems = ['jobTitle', 'deptName', 'companyName'];
		var workInfoLine = workItems.inject('', function(acc, n){
			if (c[n]) {
				acc += c[n] + ', ';
			}
			return acc;
		});

		workInfoLine = workInfoLine.replace(/(?:<br\s*\/?>)|\n|\r/gi, " ");
		return workInfoLine.substr(0, workInfoLine.length - 2); // remove the last comma
	},
	
	renderLinked: function(contacts){
		contacts.each(function(c, index){
			if(0 === index){
				c.primary = "primary";
			} else {
				c.primary = "";
			}
			c.fullName = c.getFullName() || c.displayText || "";
			c.workInfoLine = this.getWorkInfoLine(c);
			
			if (c.fullName.blank() && c.workInfoLine && !c.workInfoLine.blank()) {
				c.fullName = c.workInfoLine;
				c.workInfoLine = "";
			}
			
			if (c.workInfoLine && !c.workInfoLine.blank()) {
				c.showWorkInfo = "show-work-info";
			}
		}.bind(this));
		
		this.clippedModel.items = contacts;
		if (this.clippedModel.items.length == 1) 
			this.clippedModel.items[0].singleItem = "x";
		else 
			this.clippedModel.items[0].singleItem = "";
		this.linking = false;
		
		this.clippedUpdate(this.clippedModel.items);
	},
	
	_clippedUpdate: function(items){
		TinyList.renderData(this.clippedDiv, this.clippedList, items);
		
		// remove the 'last' class on the last item so a divider line appears underneath it (NOV-38890)
		var lastDiv = this.clippedDiv.down('.last');
		if (lastDiv) {
			lastDiv.removeClassName('last');
		}
	},
	
	extendAndCollapseAssociates: function(person, linkedContacts){
		// extend each of the linked contacts' associates according to the type as indicated by their JSON property
		var prototypes = [["phoneNumbers", new PhoneNumber()], ["imNames", new IMName()], ["addresses", new Address()], ["emailAddresses", new Email()], ["urls", new URLModel()]];
		
		prototypes.each(function(proto){
			var uniques = new Hash();
			
			linkedContacts.each(function(linked){
				linked[proto[0]].each(function(item){
					item.iconPath = linked.iconPath;
					// extend each one
					Object.extend(item, proto[1]);
					
					// and remove duplicates when copying to person
					
					var key = (item.getHashKey && item.getHashKey()) || item.value || "";
					key = key.strip().toLowerCase();
					
					var existing = uniques.get(key);
					
					// If model.getRating is implemented, use that to find the "best quality" item when removing dupes.
					// Otherwise, just use the first item found.
					if (item.getRating) {
						item.rating = item.getRating(linked);
						if ((!existing || item.rating > existing.rating)&& !item.toDelete) {
							// overwrite existing best
							uniques.set(key, item);
						}
					}
					else {
						if (!existing && !item.toDelete) {
							uniques.set(key, item);
						}
					}
					
				});
			});
			
			// copy unique items into person
			uniques.values().each(function(unique){
				person[proto[0]].push(unique);
			});
			
		});
		
	},
	
	setupContact: function(){
		if (this.isPseudoCard) {
			//TODO: This probably won't work for notes/birthdays/etc
			this.renderHeader(this.contact);
			Object.extend(this.person, this.contact);
			delete this.person.id;
			
			this.renderContactPoints(this.person);
			this.showDeferrals();
			
			if (this.aboutToActivateCallback) {
				Mojo.Log.info("calling aboutToActivateCallback")
				this.aboutToActivateCallback();
			}
			return;
		}
		if (this.linkedRequest) {
			this.linkedRequest.cancel();
		}
		var fail = this.serviceFailed.bind(this);
		this.linkedRequest = AppAssistant.contactsService.getLinkedContacts(this.targetId, this.getLinkedContactsCallback.bind(this), this.controller, fail);
	},
	
	serviceFailed: function(resp){
		this.linkedRequest.cancel();
		this.detailRequest.cancel();
		Mojo.Log.error("Getting contact details failed for id" + this.targetId + " with launch type " + this.launchType)
		if(this.launchType == "launcher") {
			this.controller.showAlertDialog({
				message:$L("The launcher link to this contact is broken"),
				choices:[{label:$L("OK"), command:"ok"}],
				onChoose:function(){
					this.controller.stageController.popScenesTo("list");
				}.bind(this),
				onCancel:function(){
					this.controller.stageController.popScenesTo("list");
				}.bind(this)
			});
		} else {
			this.controller.stageController.popScenesTo("list");
		}
	},
	
	getLinkedContactsCallback: function(resp){
		resp.list.each(function(c){
			c.iconClass = AppAssistant.getIconClass(c.accountId)
			c.iconPath = Utilities.iconJSONToIconPath(c.accountIcons)
		})
		this.linkedContactsWithAccountIDs = resp.list;
		if (this.linkedContacts) 
			this.giveContactsAccountIds();
		this.detailRequest = AppAssistant.contactsService.details(this.targetId, this.renderContact.bind(this), this.controller, {
			collapse: false
		}, this.serviceFailed.bind(this));
		this.isFirstLinking = false;
	},
	
	giveContactsAccountIds: function(){
		var that = this;
		//Go through each contact, attatch its id to the matching contact in this.linkedContacts
		for (var i = 0; i < this.linkedContactsWithAccountIDs.length; i++) {
			var linkedC = this.linkedContactsWithAccountIDs[i];
			for (var j = 0; j < this.linkedContacts.length; j++) {
				var c = this.linkedContacts[j];
				if (c.id == linkedC.id) {
					if (linkedC.readOnly) {
						c.disabled = true;
					}
					//TODO We need a better way to specify the diff between google contacts, and gtalk contacts
					///We should be putting better class names in the accounts.xml
					if (linkedC.accountName == "Google") {
						linkedC.accountDomain = "google";
					}
					c.accountDomain = linkedC.accountDomain;
					
					c.command = "c," + c.id + "," + linkedC.accountDomain;
					c.label = c.getFullName() || $L("No name available");
					c.accountName = linkedC.accountName;
					c.readOnly = linkedC.readOnly;
					c.iconPath = linkedC.iconPath;
				}
			}
		}
		this.clippedUpdate(this.clippedModel.items);
	},
	
	populateAccounts: function(resp){
		if (resp.list.length == 0) 
			return;
		this.accounts = resp.list;
		var redoHeader = false;
		var that = this;
		this.accounts.each(function(acct){
			if (that.isNewScene && (acct.accountId == that.contact.accountId)) {
				that.contact.accountDomain = acct.domain;
				that.contact.iconClass = Utilities.iconClassFromAccountName(that.contact.accountName);
				that.nameSyncPicker.className = "palm-account-icon " + that.contact.accountDomain;
			}
			acct.label = acct.accountDisplayName;
			acct.command = "a," + acct.accountId + "," + acct.domain;
			acct.icon = acct.domain;
		});
	},
	
	toggleClipped: function(e){
		if (this.targetId) //Don't open if it's a pseudo card
			this.headerDiv.mojo.setOpenState(!this.headerDiv.mojo.getOpenState())
	},

	handleDelete: function(){
		var deleteName = this.person.fullName ||
		$L({
			value: "Person",
			key: "default_value_for_deletion_when_no_name"
		});
		var choices;
		
		var deleteContactChoice = {
			label: $L('Delete All Profiles'),
			value: 'DELETE_ALL',
			type: 'negative'
		}
		var deleteOtherContactsChoice = {
			label: $L('Delete other profiles'),
			value: 'DELETE_OTHERS',
			type: 'negative'
		}
		var cancelChoice = {
			label: $L('Cancel'),
			value: 'CANCEL',
		}
		choices = [];
		var hasReadOnlyContact = false;
		var hasWritableContact = false;
		var readOnlyType = "";
		this.linkedContacts.each(function(contact){
			if (contact.readOnly) {
				readOnlyType = contact.accountName;
				hasReadOnlyContact = true;
			} else {
				hasWritableContact = true;
			}
		});
		
		var messageTemplate;
		if (hasReadOnlyContact && hasWritableContact) {
			
			messageTemplate = new Template($L('#{type} profiles cannot be deleted.  Delete all other profiles?'));
			choices.push(deleteOtherContactsChoice);
			choices.push(cancelChoice);
		} else if (hasReadOnlyContact && !hasWritableContact) {
			messageTemplate = new Template($L('#{type} profiles can not be deleted'));
			choices.push(cancelChoice);
		} else {
			messageTemplate = new Template($L('Delete #{deleteName}?'));
			choices.push(deleteContactChoice);
			choices.push(cancelChoice);
		}
		var message = messageTemplate.evaluate({
			deleteName: deleteName,
			type:readOnlyType
		});
		this.controller.showAlertDialog({
			onChoose: function(value){
				if (value == 'DELETE_ALL' || value == 'DELETE_OTHERS') {
					var that = this;
					this.linkedContacts.each(function(c){
						if (!c.readOnly) {
							AppAssistant.contactsService.doAsyncDelete(c.id);
						}
					})
					if (AppAssistant.listResetCallback) 
						AppAssistant.listResetCallback();
					if (value == 'DELETE_ALL') {
						if(this.person.launcherId){
							new Mojo.Service.Request("palm://com.palm.applicationManager/removeLaunchPoint", {
								parameters: {
									launchPointId:this.person.launcherId,
									id:"com.palm.app.contacts"
								}
							})
						}
						this.controller.stageController.popScenesTo('list');
					}
				}
			}.bind(this)            ,
			message: message,
			choices: choices
		});
	},
	
	_reconstitute: function(params){
		this.initialize(params);
		this.setupContact();
	},
	
	setup: function(){
		if (this.controller.crossLaunchPush == true) {
			this.isCrossLaunch = true;
		}
		if (!this.targetId) {
			this.isPseudoCard = true;
		}
		
		this.simMenuItem = {
					label:$L("Copy To SIM Card"),
					enabled:true,
					command:MenuHandler.addToSIM
				}
		
		this.isInitialSetup = true;
		this.menuItems = [];
		this.setupAppMenu();
		this.personDetailElement = this.controller.get('contact_detail');
		this.header = this.controller.get('detail-header');
		this.headerContainer = this.controller.get('detail-header-container');
		this.handleListClickBound = this.handleListClick.bindAsEventListener(this);
		this.toggleClippedBound = this.toggleClipped.bindAsEventListener(this);
		this.handleClippedClickBound = this.handleClippedClick.bindAsEventListener(this);
		this.handleClipMoreBound = this.handleClipMore.bindAsEventListener(this);
		this.reminderContainer = this.controller.get("reminderContainer");
		this.headerDrawer = this.controller.get("clipped-info-drawer");
		this.controller.setupWidget("clipped-info-drawer", {
			open: false
		}, {});
		this.headerDiv = this.controller.get("clipped-info-drawer");
		this.editHandler = this.handleEdit.bind(this);
		this.commandMenuModel = [{
			label: $L('Edit'),
			command: MenuHandler.editContact
		}];
		
		if (this.contact) {
			this.renderHeader(this.contact);
		}
		
		//If you've passed in a contact with no id change the edit button to a save button
		this.cmdMenuModel = {
			visible: true,
			items: []
		};
		//If it's cross launched to another app, but is an existing contact, don't show the save button
		if ((this.isCrossLaunch && !this.isPseudoCard) || this.suppressMenuItems) {
			this.cmdMenuModel.visible = false;
		}
		if (this.isPseudoCard) {
			this.commandMenuModel[0] = {
				label: $L('Add To Contacts'),
				command: this.savePseudo.bind(this),
			}
		}
		this.controller.setupWidget(Mojo.Menu.commandMenu, undefined, this.cmdMenuModel);
		this.controller.setupWidget('clipped-list', {
			itemTemplate: 'detail/clipped-item',
		}, this.clippedModel);
		
		this.clippedList = {
			itemTemplate: 'detail/clipped-item'
		};
		this.clippedModel = {
			items: []
		};
		this.clippedDiv = this.controller.get('clipped-list');
		TinyList.setupTapHandler(this.clippedDiv, this.clippedModel, this.handleClippedClickBound);
		
		// this business is here to delay the initial rendering of the clipped list a little
		// this speeds up scene load a bit
		// updates after the initial load will still trigger an immediate refresh
		var updateClippedNow = function(){
			this.waitingToUpdateClipped = false;
			this._clippedUpdate(this.currClippedItems);
			this.currClippedItems = undefined;
		}.bind(this);
		
		this.clippedUpdate = function(items){
			if (!this.updatedClippedList) {
				this.updatedClippedList = true;
				this.waitingToUpdateClipped = true;
				this.currClippedItems = items;
				updateClippedNow.delay(1);
			}
			else 
				if (this.waitingToUpdateClipped) {
					this.currClippedItems = items;
				}
				else {
					this._clippedUpdate(items);
				}
		}.bind(this);
		
		var that = this;
		['phone', 'email', 'messaging', 'address', 'url', 'notes', 'moredetails'].each(function(n){
			var attrs = {
				itemTemplate: 'detail/' + n + '-item',
				listTemplate: 'common/group-container-unlabeled',
			};
			that[n + 'List'] = attrs;
			
			var model = {
				items: []
			};
			that[n + 'Model'] = model;
			
			var div = that.controller.get(n + 'List');
			that[n + 'Div'] = div;
			
			TinyList.setupTapHandler(div, model, that.handleListClickBound);
			that[n + 'Update'] = TinyList.renderData.curry(div, attrs);
			
		});
		
		// Open with object for handling opening
		// specific applications for the addresss'
		// of a contact.
		this.openWith = new OpenWithMenu({
			isMimeType: false,
			urlOrMime: "mapto:",
			controller: this.controller,
			listId: "addressList"
		});
		// set up behaviors once and don't bother to deactivate them, unnecessary and adds complexity
		
		this.headerContainer.observe(Mojo.Event.tap, this.toggleClippedBound);
		
		this.moreDetailsModel = {
			items: []
		};
		this.moreDetailsAttributes = {
			itemTemplate: 'detail/moredetails-item',
			listTemplate: 'common/group-container-unlabeled',
		}
		
		var that = this;
		this.notesAttrs = {
			listTemplate: 'common/group-container-unlabeled',
			itemTemplate: 'detail/notes-item'
		}
		this.notesModel = {
			items: []
		}
		
		this.observeListForDetails();
		
		this.controller.get('clip-button').observe(Mojo.Event.tap, this.handleClipMore.bind(this));
		this.controller.listen("reminderContainer", Mojo.Event.tap, this.handleReminder.bind(this));
	},
	
	setupAppMenu: function(){
        var editItem = {
            "label": $L("Edit"),
            "items": [{
                "label": $L("Cut"),
                "command": "palm-cut-cmd",
                "shortcut": "x",
                "checkEnabled": true
            }, {
                "label": $L("Copy All"),
                "command": MenuHandler.copyContact,
                "shortcut": "c",
            }, {
                "label": $L("Paste"),
                "command": "palm-paste-cmd",
                "shortcut": "v",
                "checkEnabled": true
            }]
        }
		
		this.menuItems = $A([editItem]);
		var addLabel = $L("Add to Launcher");
		 if(this.person.launcherId)
		   addLabel = $L("Remove from Launcher");
		this.launcherItem = {
			label: addLabel,
			enabled: true,
			command: MenuHandler.addToLauncher
		}
		if (!this.isCrossLaunch) {
			this.menuItems.push({
				label: $L("Set speed dial"),
				enabled: true,
				command: MenuHandler.setSpeedDial
			});
			
			this.menuItems.push({
				label:$L("Add Contact Reminder"),
				enabled:true,
				command:MenuHandler.addReminder
			})
			this.menuItems.push(this.launcherItem);
			this.menuItems.push({
				label: $L("Delete Contact"),
				command: MenuHandler.deleteContact
			});
		}
		
		
		this.sendContactWithPhones = [
				{
					label:$L("Send to Car Kit"),
					command:"sendbluetooth"
				},
				{
					label:$L("Send via MMS"),
					command:"sendmms"
				},
				{
					label:$L("Send via Email"),
					command:"sendemail"
				}
			];
		this.sendContactWithoutPhones = [
				{
					label:$L("Send via MMS"),
					command:"sendmms"
				},
				{
					label:$L("Send via Email"),
					command:"sendemail"
				}
			]
			
		this.sendContactItem = {
			label:$L("Send Contact"),
			items: this.sendContactWithPhones
		},
		
		this.menuItems.push(this.sendContactItem)
		this.menuItems.push(MenuHandler.prefsItem);
        this.menuItems.push({
            label: $LL('Help'),
            command: Mojo.Menu.helpCmd
        });
		this.appMenuModel = {
            visible: true,
            label: 'Contacts Menu',
            items: this.menuItems
        };
        this.controller.setupWidget(Mojo.Menu.appMenu, {
            omitDefaultItems: true
        }, this.appMenuModel);
	},
	
	checkForLaunchpoint: function(){
		var oldLabel = this.launcherItem.label;
		if (this.person.launcherId){
			this.launcherItem.label = $L("Remove from Launcher");
		}
		else {
			this.launcherItem.label = $L("Add to Launcher");
		}
		if (oldLabel != this.launcherItem.label) {
			this.controller.modelChanged(this.menuItems);
		}
	},
	
	checkForSIM:function(){
		AppAssistant.accountsService.getAllAccountsForService(this.controller, "CONTACTS", true, this.checkForSimResponse.bind(this));
	},
	
	checkForSimResponse:function(resp){
		var simID = -1;
		resp.list.each(function(acct){
			if(acct.domain == "simphonebook"){
				simID = acct.accountId;
			}
		})
		this.simID = simID;
		if(this.simID != -1 && this.phoneModel.items.length > 0){
			if (this.appMenuModel.items.indexOf(this.simMenuItem) == -1) {
				this.appMenuModel.items.splice(3, 0, this.simMenuItem);
			}
		} else {
			this.appMenuModel.items = Utilities.removeFromArray(this.appMenuModel.items, this.simMenuItem);
		}
		this.controller.modelChanged(this.appMenuModel);
	},
	
	handleAddToLauncher: function(){
		if (!this.person.launcherId){
			var contact = this.linkedContacts[0];
			var firstName = contact.firstName || "";
			var lastName = contact.lastName || "";
			this.controller.showDialog({
				template: 'detail/add-to-launcher',
				pic: this.person.pictureLoc || Contact.DEFAULT_DETAILS_AVATAR,
				firstName: firstName,
				lastName: lastName,
				isClipped: this.person.isClipped,
				assistant: new AddToLauncherAssistant(this.controller, this.doAddToLauncher.bind(this), {
					firstName: firstName,
					lastName: lastName
				}),
			});
		}
		else {
			var callParams = {
				launchPointId: this.person.launcherId
			}
			this.controller.serviceRequest('palm://com.palm.applicationManager/removeLaunchPoint', {
				parameters: callParams,
				onSuccess: this.removeLaunchIconSuccess.bind(this, this.targetId),
				onFailure: this.removeLaunchIconFailure.bind(this, this.targetId)
			});
		}
	},
	
	copyDetailsToClipboard:function(){
		var details = [];
		details.push(this.person.getFullName() || this.person.displayText);
		if(this.person.workInfoLine) details.push(this.person.workInfoLine);
		var that = this;
		this.person.phoneNumbers.each(function(ph){
			details.push((ph.displayValue + " " + ph.displayLabel).strip());
		})
		this.person.emailAddresses.each(function(item){
			details.push((item.displayValue + " " + item.displayLabel).strip());
		})
		this.person.imNames.each(function(item){
			details.push(($L("IM: ") + item.displayValue + " " + item.displayLabel).strip());
		})
		this.person.urls.each(function(item){
			details.push(item.url.strip())
		})
		
		this.person.addresses.each(function(item){
			details.push((item.displayLabel && (item.displayLabel + ": ")) + (item.getOneLine()).strip());
		})
		this.moredetailsModel.items.each(function(item){
			details.push((item.key.capitalize() + ": " + item.value).strip())
		});
		this.notesModel.items.each(function(item){
			var note = item.notes.gsub("<br />", "\n");
			details.push(note)
		})
		this.controller.stageController.setClipboard(details.join("\n"), true)
		
	},
	
	doAddToLauncher: function(firstName, lastName){
		var title = firstName + " " + lastName;
		title.strip();
		var appParams = {
			id: this.targetId,
			launchType: 'launcher'
		};
		
		var callParams = {
			id: 'com.palm.app.contacts',
			'icon': this.person.pictureLoc || Contact.DEFAULT_DETAILS_AVATAR,
			'title': title,
			'params': appParams
		};
		this.controller.serviceRequest('palm://com.palm.applicationManager/addLaunchPoint', {
			parameters: callParams,
			onSuccess: this.addLaunchIconSuccess.bind(this),
			onFailure: this.addLaunchIconFailure.bind(this)
		});
	},
	
	addLaunchIconSuccess: function(resp){
		var launchId = resp.launchPointId;
		AppAssistant.contactsService.setPersonDetails(this.controller, this.targetId, {
			launcherId: launchId
		});
		Mojo.Controller.appController.showBanner($L("Added contact to Launcher"), null, "contacts-banner")
	},
	
	addLaunchIconFailure: function(resp){
		Mojo.Controller.errorDialog($L("Unable to add to Launcher"), this.controller.window);
		Mojo.Controller.appController.showBanner($L("Couldn't add contact to Launcher"), null, "contacts-banner")
	},
	
	removeLaunchIconSuccess: function(personId, resp){
		AppAssistant.contactsService.setPersonDetails(this.controller, this.targetId, {
			launcherId: null
		});
		Mojo.Controller.appController.showBanner($L("Removed contact from Launcher"), null, "contacts-banner")
	},
	
	removeLaunchIconFailure: function(personId, resp){
		Mojo.Controller.appController.showBanner($L("Couldn't remove contact from Launcher"), null, "contacts-banner")
		
	},
	
	observeListForDetails: function(){
		this.lists = this.lists || ['phoneList', 'messagingList', 'emailList', 'addressList', 'urlList'];
		for (var i = 0; i < this.lists.length; i++) {
			this.controller.listen(this.lists[i], Mojo.Event.listTap, this.handleListClickBound);
		}
		
	},
	
	stopEditing: function(){
		this.controller.select("*:focus").each(function(el){
			el.blur();
		})
	},
	
	activate: function(params){
		if(this.focusWindow){
			this.controller.stageController.activate();
			this.focusWindow = false;
		}
		if (params && params.personId) {
		
		
			if (this.isPseudoCard) {
				if (c.getFullName().blank()) {
					c.setFullName(params.fullName);
				}
				AppAssistant.contactsService.save(this.c, function(newContactInfo){
					AppAssistant.contactsService.linkContacts(null, personToClipTo, [newContactInfo.personId]);
				}.bind(this));
			}
			else {
				// this.clipExisting(params);
			}
		}
		if (params && params.update) {
			this.setupContact();
		}
		this.isInitialSetup = false;
		this.blurStageBound = this.blurStageBound || this.blurStage.bind(this);
		this.controller.listen(this.controller.document, Mojo.Event.deactivate, this.blurStageBound);
		
	},
	
	blurStage: function(){
		if (this.isCrossLaunch) 
			this.controller.stageController.popScene();
	},
	
	aboutToActivate: function(callback){
		if (this.isInitialSetup) {
			Mojo.Log.info("remembering aboutToActivateCallback")
			this.aboutToActivateCallback = callback;
			Mojo.Log.info("calling setupContact")
			this.setupContact();
		}
		else {
			callback();
		}
	},
	
	deactivate: function(){
		this.controller.stopListening(this.controller.stageController.document, Mojo.Event.deactivate, this.blurStageBound);
	},
	
	onSynchronousChange: function(){
		// a synchronous change is one triggered by the UI
		// the detail scene is subscribed on linked contacts, but only in the 'new' way so updates may not fire immediately.
		// Refresh the detail scene manually when the source of the change was the UI (it's more important than refreshing immediately for backend changes).
		
		// this scene should also notify the list scene so it picks up the change immediately
		// (as opposed to treating it as a backend-initiated change, which could cause it to delay the UI refresh)
		this.setupContact();
		if (AppAssistant.listResetCallback) {
			AppAssistant.listResetCallback();
		}
	},
	
	onClippedChoose: function(id, node, selection){
		var cb = this.onSynchronousChange.bind(this);
		
		if (selection == 'UNCLIP') {
			AppAssistant.contactsService.unlinkContact(this.controller, id, cb);
		}
		else 
			if (selection == 'MAKE_PRIMARY') {
				AppAssistant.contactsService.setPrimary(this.controller, id, cb);
			}
			else 
				if (selection == 'DELETE_PROFILE') {
					node.addClassName("deleting")
					AppAssistant.contactsService.doDelete(id, cb, this.controller);
				}
	},
	
	handleReminder:function(){
		this.controller.stageController.pushScene("reminder", {personId: this.targetId, fullName:this.person.fullName, reminderText:(this.person.reminder ||""), focusField:true})
	},
	
	clipExisting: function(contact){
		this.linking = true;
		contact.fullName = $L("Linking...");
		this.clippedModel.items.push(contact);
		this._clippedUpdate(this.clippedModel.items);
		window.startLinkTime = new Date().getTime();
		
		AppAssistant.contactsService.linkContacts(this.controller, this.targetId, [contact.personId], this.onSynchronousChange.bind(this));
	},
	
	handleClipMore: function(){
	
		var headerMessage = "";
		if (this.isPseudoCard) {
			headerMessage = $L("Link To Existing Contact");
		}
		else {
			headerMessage = $L("Link to #{name}");
		}
		headerMessage = headerMessage.interpolate({
			name: this.person.fullName
		});
		
		this.controller.stageController.pushScene("list", {
			mode: 'picker',
			exclusions: [this.person.id],
			message: headerMessage,
			callback: this.clipExisting.bind(this)
		});
	},
	
	handleClippedClick: function(e){
		if (this.linkedContacts.length < 2) 
			return;
		var itemNode = e.originalEvent.target;
		if(!itemNode.hasClassName("palm-row")){
			itemNode = itemNode.up(".palm-row")
		}
		
		var choices = [{
				label: $L('Unlink this profile'),
				value: 'UNCLIP'
			}, {
				label: $L('Set as primary profile'),
				value: 'MAKE_PRIMARY'
			}, {
				label: $L('Delete this profile'),
				value: 'DELETE_PROFILE',
				type:'negative'
			}, {
				label: $L('Cancel'),
				value: 'CANCEL',
				type: 'dismiss'
			}];
		if(e.item.readOnly){
			choices = [{
				label: $L('Unlink this profile'),
				value: 'UNCLIP'
			}, {
				label: $L('Set as primary profile'),
				value: 'MAKE_PRIMARY'
			}, {
				label: $L('Cancel'),
				value: 'CANCEL',
				type: 'dismiss'
			}]
		}
		this.controller.showAlertDialog({
			onChoose: this.onClippedChoose.bind(this, e.item.id, itemNode),
			title: e.item.fullName,
			choices: choices
		});
	},
	
	handleListClick: function(e){
		var elt = this.controller.get(e.originalEvent.target);
		if (elt.hasClassName('messaging') || elt.up('div.messaging')) {
			var useMessaging = true;
		}
		
		//Messaging
		if (e.item.isImAddress || useMessaging) {
			var msgingParams = {
				personId: this.person.id,
				contactPointId: e.item.id,
				address: e.item.value
			};
			if (e.item.serviceName) {
				msgingParams.serviceName = e.item.serviceName;
			}
			
			if (e.item.type) {
				msgingParams.type = e.item.type;
			}
			return this.controller.serviceRequest("palm://com.palm.applicationManager", {
				method: 'open',
				parameters: {
					id: "com.palm.app.messaging",
					params: msgingParams
				}
			});
			
			//Phone Number
		}
		else 
			if (e.item.isPhoneNumber) {
				
				var openParams = {
					number: e.item.value,
					personId:this.targetId,
					label: e.item.displayLabel
				};
				return this.controller.serviceRequest("palm://com.palm.applicationManager", {
					method: 'open',
					parameters: {
						id: 'com.palm.app.phone',
						params: openParams
					}
				});
				
			//Street Address
			}
			else 
				if (e.item.isAddress) {
					this.openWith.shouldOpenOpenWith(e);
					/*
					var q = e.item.freeformAddress.gsub("\n", " ");
					q = q.gsub("\r", " ");
					var params = {
						query: q
					};
					return this.controller.serviceRequest("palm://com.palm.applicationManager", {
						method: 'open',
						parameters: {
							id: 'com.palm.app.maps',
							params: params
						}
					});*/
				//Email
				}
				else 
					if (e.item.isEmailAddress) {
						return this.controller.serviceRequest("palm://com.palm.applicationManager", {
							method: 'open',
							parameters: {
								id: 'com.palm.app.email',
								params: {
									uri: "mailto:" + e.item.value
								}
							}
						})
					}
					// URL
					else 
						if (e.item.isUrl) {
							var openParams = {
								scene: 'page',
								url: e.item.url
							};
							return this.controller.serviceRequest('palm://com.palm.applicationManager', {
								method: 'open',
								parameters: {
									id: 'com.palm.app.browser',
									params: openParams
								}
							});
						}
	},
	
	handleEdit: function(){
		if(this.linking) return;
		var updateDetailID = function(id){
			if ((id == this.targetId) || !id) 
				return;
			this.targetId = id;
			this.setupContact();
		}.bind(this);
		var detailSceneCallback = this.renderContactHideEditUntilSaveSuccessful.bind(this);
		this.controller.stageController.pushScene('edit', {
			person: this.person,
			linkedContacts: this.linkedContacts,
			updateDetailCallback: updateDetailID,
			detailSceneCallback: detailSceneCallback
		});
	},
	
	savePseudo: function(){
		this.controller.showAlertDialog({
			onChoose: this.doHandleSave.bind(this),
			choices: [{
				label: $L('Save as new'),
				value: 'NEW'
			}, {
				label: $L('Add to existing'),
				value: 'EXISTING'
			}, {
				label: $L('Cancel'),
				value: 'CANCEL'
			}]
		});
		
	},
	
	doHandleSave: function(choice){
		if (choice === 'CANCEL') 
			return;
		if (choice === 'NEW') {
			//AppAssistant.contactsService.save(this.contact, this.switchToEdit.bind(this), this.controller);
			this.switchToEdit(false);
		}
		if (choice === 'EXISTING') {
			this.switchToEdit(true);
		}
	},
	
	
	switchToEdit: function(existing){
		var launchType = "newContact";
		if (existing) {
			launchType = "addToExisting";
		}
		
		this.controller.serviceRequest("palm://com.palm.applicationManager", {
			method: "open",
			parameters: {
				id: "com.palm.app.contacts",
				params: {
					contact: this.contact,
					launchType: launchType
				}
			}
		});
		return;
		
	},
	
	setSpeedDial: function() {
		var cb = this.setupContact.bind(this);
		
		if (this.person.phoneNumbers.length == 1) {
			this.controller.stageController.pushScene("speeddial", {phone:this.person.phoneNumbers[0], person:this.person, cb:cb, popto:'detail'});
		} else {
			this.controller.stageController.pushScene("speeddialchoice", {
				numbers: this.phoneModel.items,
				person: this.person,
				cb: cb,
				popto: 'detail'
			});
			return;
		}
	},
	
	sendContactMMS:function(){
		AppAssistant.contactsService.makeVCard(this.controller, {
				personId: this.targetId
			}, this.gotVCard.bind(this, "mms"));
	},
	
	sendContactEmail:function(){
		AppAssistant.contactsService.makeVCard(this.controller, {
				personId: this.targetId
			}, this.gotVCard.bind(this, "email"));
	},
	
	sendContactBluetooth:function(){
		var params = {"type":"bluetooth", "onSuccess":this.bluetoothResponse.bind(this)}; 
		ConnectionWidget.connect(params, this.controller.stageController);
	},
	
	bluetoothResponse:function(response){
		if (response == "BT-On" || response == "BT-StartingUp") {
			AppAssistant.contactsService.makeVCard(this.controller, {
				personId: this.targetId,
				stripPhoneNumbers:true
			}, this.gotVCard.bind(this, "bluetooth"));
		}
	},
	
	gotVCard:function(type, resp){
		if (type == "bluetooth") {
			var appArgs = {
				appId: "com.palm.app.bluetooth",
				name: "btopp"
			}
			var sceneArgs = {
				file: resp.file
			}
			this.controller.stageController.pushScene(appArgs, sceneArgs);
		} else if(type == "mms"){
			this.controller.serviceRequest('palm://com.palm.applicationManager', {
			    method: 'open',
			    parameters: {
			        id: 'com.palm.app.messaging',
			        params: {
			            attachment:resp.file
			        }
			    }
			});
		} else if(type =="email"){
			this.controller.serviceRequest('palm://com.palm.applicationManager', {
			    method: 'open',
			    parameters: {
			        id: 'com.palm.app.email',
			        params: {
			            summary: 'vCard: ' + this.person.fullName,
			            attachments: [{
			                fullPath:resp.file,
			                mimeType:'text/x-vcard'
			            }]
			        }
			    }
			});			
		}
		
	},
	
	addToSIM: function(){
		if(this.phoneModel.items.length > 1){
			this.controller.stageController.pushScene("simchoice", {
				numbers:this.phoneModel.items,
				person:this.person,
				cb:this.doAddToSim.bind(this)
			})
		} else {
			this.doAddToSim(this.phoneModel.items[0]);
		}
	},
	
	doAddToSim:function(number){
		if(!AppAssistant.simNameLength || !AppAssistant.simPhoneLength){
			this.simLengthRequest = new Mojo.Service.Request("palm://com.palm.telephony", {
				method:"simbookEntryMaxLengthQuery",
				parameters:{
					type:"adn"
				},
				onSuccess:function(resp){
					this.simLengthRequest = null;
					if(resp.extended){
						AppAssistant.simNameLength = resp.extended.name;
						AppAssistant.simPhoneLength = resp.extended.number;
						this.finalizeAddToSim(number);
					} else {
						this.saveSimFailure(resp);
					}
					
				}.bind(this),
				onFailure:function(resp){
					this.saveSimFailure(resp);
				}.bind(this)
			});
		} else {
			this.finalizeAddToSim(number);
		}
	},
	
	finalizeAddToSim:function(number){
		var c = new Contact();
		c.setFullName(this.person.getFullName().substring(0, AppAssistant.simNameLength));
		c.accountId = this.simID;
		if(number.value.length > AppAssistant.simPhoneLength){
			this.controller.showAlertDialog({
				title:$L("Unable to copy"),
				message:$L("This number is too long to copy to the SIM card. Try again with a different number."),
				choices:[{label:$L("OK")}]
			});
			return;
		}
		var p = new PhoneNumber(number.value.substring(0, AppAssistant.simPhoneLength));
		p.label = 8;
		c.phoneNumbers.push(p);
		AppAssistant.contactsService.save(c, this.saveSimSuccess.bind(this), this.saveSimFailure.bind(this));		
	},
	
	simError:function(err){
		
	},
	
	saveSimSuccess:function(resp){
		AppAssistant.contactsService.linkContacts(this.controller, this.targetId, [resp.personId]);
		Mojo.Controller.appController.showBanner($L("Contact copied to SIM card"), null, "contacts-banner");
	},
	
	saveSimFailure:function(resp){
		var message = $L("There was an error copying to the sim");
		if(resp.errorCode == "tel:109"){
			message = $L("The SIM card is full.");
		}
		Mojo.Controller.appController.showBanner(message, null, "contacts-banner");
	},
	
	handleCommand:function(event){
 		 if (event.type == Mojo.Event.command) {
            if (event.command == Mojo.Menu.prefsCmd) {
                event.stop();
                this.goToPrefs();
                return;
            }
            if (event.command === MenuHandler.deleteContact) {
                event.stop();
                this.handleDelete();
                return;
            }
			 if (event.command == "sendbluetooth") {
                event.stop();
                this.sendContactBluetooth();
                return;
            }
			if(event.command == "sendemail"){
				event.stop();
                this.sendContactEmail();
                return;
			}
			if(event.command == "sendmms"){
				event.stop();
                this.sendContactMMS();
                return;
			}
            if (event.command === MenuHandler.addToLauncher) {
                event.stop();
                this.handleAddToLauncher();
                return;
            }
            if (event.command === MenuHandler.noop) {
                event.stop();
                return;
            }
            
            if (event.command === MenuHandler.linkProfiles) {
                event.stop();
                this.handleClipMore();
                return;
            }
            if ((typeof event.command) === "function") {
                event.command();
                event.stop();
            }
            
            if (event.command === MenuHandler.editContact) {
                this.handleEdit();
                event.stop();
                return;
            }
            
            if (event.command === MenuHandler.setSpeedDial) {
                this.setSpeedDial();
                event.stop();
                return;
            }
            
            if (event.command === MenuHandler.addReminder) {
                this.handleReminder();
                event.stop();
                return;
            }
			
			if(event.command === MenuHandler.addToSIM){
				this.addToSIM();
				event.stop();
				return;
			}
			
			if(event.command === MenuHandler.copyContact){
				this.copyDetailsToClipboard();
				event.stop();
				return;
			}
        }		
	},
	
	goToPrefs: function(){
        this.controller.stageController.pushScene('prefs');
    },
	
	detailViewIsUpWithPersonId: function(personID){
		return this.targetId == personID;
	}
});

DetailAssistant.setSpeedDial = "setSpeedDial";
DetailAssistant.addReminder = "addReminder";
