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

function Messaging() {
	Mojo.Log.info("Initializing Messaging Constants");
	this.helpUrl = "http://help.palm.com/messaging/index.html";
	this.requests = [];
	this.messagingPrefs = {};
	this.mmsPrefs = {};
	this.carrierSettings = {};
	this.buildName = '';
	this.smsSettings = {
		oneSegmentSize: 160,
		multipleSegmentSize: 151 // specific to sprint
	};
	this.debugMenuEnabled = false;
	this.Views = {
		BUDDY: 'viewBuddy',
		HISTORY: 'viewHistory',
		CHAT: 'viewChat',
		COMPOSE: 'viewCompose',
		PREFERENCES: 'viewPreferences'
	}
	this.listviewSceneAssistant;
	this.notificationTypes = {
		newMessage: 'newMessage',
		connectionFailure: 'connectionFailure',
		sendFailure: 'sendFailure'
	};
  
	this.Error = {
		getLoginErrorFromCode: function() {
			var data = {
				"AcctMgr_Network_Error" : $L("Could not connect to the server. Try again."),  
				"AcctMgr_Bad_Username": $L("Username is invalid. Try again."),
				"AcctMgr_Bad_Password": $L("Password is invalid. Try again."),
				"AcctMgr_Bad_Authentication": $L("Username or password is invalid. Try again."),
				"AcctMgr_Generic_Error": $L("Sign in failed. Try again."),
				"AcctMgr_Account_Locked": $L("This account has been locked. To unlock the account refer to the AOL website."),
				"AcctMgr_Acct_Already_Exists": $L("Account already exists."), // pop immediately
				"AcctMgr_Acct_Not_Created": $L("Sign in failed. Try again."), 
				"AcctMgr_Name_In_Use": $L("You have been signed out because you are signed in at another location."), 
				"AcctMgr_No_Setup_Service": $L("Sign in failed."), // pop immediately
				"AcctMgr_Single_Account_Only": $L("You can only create one messaging account of this type.")
	   		};
			return function(errorCode) {
				if(errorCode && data[errorCode])
					return data[errorCode];
				else
				 	return $L('Login failed');
			};
		}(),
		
		getMessageErrorFromCode: function() {
			var data = {
				"error: not connected to the til": $L("Could not send your message. Try again."),
				"error: no reply from til": $L("Could not send your message. Try again."),
				"error parsing json or generating response": $L("Could not send your message. Try again."),
				"error lsexception": $L("Could not send your message. Try again."),
				"success": $L("Success"),
				"phoneoff": $L("Phone off"), // this should never get displayed since we mark the message as pending until the radio is on
				"noservice": $L("No service"),  // this should never get displayed since we mark the message as pending until we have service
				"unknownscaddress": $L("Unknown service center address. Contact your carrier."),
				"badparam": $L("Could not send your message. Try again."),
				"smsnetworkerror": $L("Could not send your message due to a network error. Try again."),
				"fdnrestricted": $L(""), // TODO: this is for GSM only; we will need to add logic to handle this case for GSM
				"radionotresponding": $L("Could not send your message due to a network error. Try again."),
				"failure": $L("Could not send your message. Try again."),
				"Pending": $L("Pending"),
				"error: unable to message a non-buddy": $L("Unable to send messages to screen names that are not in the buddy list."),
				"timeout": $L("Could not send your message. Try again."),
				"routertimeout": $L("Could not send your message. Try again.")
			};
			return function(errorCode) {
				if(errorCode && data[errorCode])
					return data[errorCode];
				else
				 	return '';				
			};
		}()
	};

	var availabilityData = {
		raw: [{
			integer: 6,
			str: "NO_PRESENCE",
			displayStr: '',
			normalClass: "nopresence",
			popupClass: "status-nopresence"
		}, {
			integer: 5,
			str: "PENDING",
			displayStr: $L("Signing In..."),
			normalClass: "pending",
			popupClass: "status-pending"
		}, {
			integer: 4,
			str: "OFFLINE",
			displayStr: $L("Offline"),
			normalClass: "offline",
			popupClass: "status-offline"
		}, {
			integer: 3,
			str: "INVISIBLE",
			displayStr: $L("Invisible"),
			normalClass: "invisible",
			popupClass: "status-invisible"
		}, {
			integer: 2,
			str: "BUSY",
			displayStr: $L("Busy"),
			normalClass: "busy",
			popupClass: "status-busy"
		}, {
			integer: 1,
			str: "MOBILE",
			displayStr: $L("Mobile"),
			normalClass: "mobile",
			popupClass: "status-mobile"
		}, {
			integer: 0,
			str: "AVAILABLE",
			displayStr: $L("Available"),
			normalClass: "available",
			popupClass: "status-available"
		}],
		idHash: {},
		nameHash: {}
	};
	this.Availability = {};
	for(var i = 0; i<availabilityData.raw.length; i++) {
		var availability = availabilityData.raw[i];
		this.Availability[availability.str] = availability.integer;
		availabilityData.idHash[availability.integer] = availability;
		availabilityData.nameHash[availability.str] = availability;
	}
	
	// This returns a non-localized string that should never be displayed
	this.Availability.getAvailabilityAsConstText = function(availabilityInteger){
		if(availabilityInteger != undefined && availabilityData.idHash[availabilityInteger])
			return availabilityData.idHash[availabilityInteger].str;
		else
			return null;
	};
	this.Availability.getAvailabilityAsDisplayText = function(availabilityInteger){
		if(availabilityInteger != undefined && availabilityData.idHash[availabilityInteger])
			return availabilityData.idHash[availabilityInteger].displayStr;
		else
			return null;
	};	
	this.Availability.getAvailabilityAsInteger = function(availabilityStr){
		if(availabilityStr != undefined && availabilityData.nameHash[availabilityStr] != undefined)
			return availabilityData.nameHash[availabilityStr].integer;
		else
			return null;
	};
	this.Availability.getNormalAvailabilityClass = function(availabilityInteger) {
		if(availabilityInteger != undefined && availabilityData.idHash[availabilityInteger])
			return availabilityData.idHash[availabilityInteger].normalClass;
		else
			return null;		
	};
	this.Availability.getPopupAvailabilityClass = function(availabilityInteger) {
		if(availabilityInteger != undefined && availabilityData.idHash[availabilityInteger])
			return availabilityData.idHash[availabilityInteger].popupClass;
		else
			return "status-none";		
	};	

	/**
	 * Status constants: this.MessageStatus.SUCCESS, this.MessageStatus.PENDING... etc
	 * Methods: this.MessageStatus.getStatusClass(statusInteger);
	 */
	this.MessageStatus = function() {
		var _data = [{
			integer: 0,
			str: "SUCCESS",
			statusClass: "status-success"
		},{
			integer: 1,
			str: "PENDING",
			statusClass: "status-pending"
		},{
			integer: 2,
			str: "FAILED",
			statusClass: "status-failed"
		},{
			integer: 3,
			str: "UNDELIVERABLE",
			statusClass: "status-failed" // treat this the same as failed in the chat view message list
		},{
			integer: 4,
			str: "WAITING_FOR_DATA_TO_CONNECT",
			statusClass: "status-pending" // treat this the same as failed in the chat view message list
		},{
			integer: 5,
			str: "DELAYED_DELIVERY",
			statusClass: "status-not-yet-delivered"
		},{
			integer: 6,
			str: "RETRIEVING_CONTENT",
			statusClass: "status-retrieving"
		}];
		
		var _idHash = {};
		
		var result = {};
		for(var i = 0; i<_data.length; i++) {
			var status = _data[i];
			result[status.str] = status.integer;
			_idHash[status.integer] = status;
		}
		result.getStatusClass = function(statusInteger) {
			var statusClass = '';
			if(statusInteger != undefined) {
				var status = _idHash[statusInteger];
				if(status) {
					statusClass = status.statusClass;
				}
			}
			return statusClass;
		};
		
		return result;
	}();

  this.transports = {
    data: {
      'sms': {
	  	cannotBeAdded: true,
        className: 'sms',
        iconPath: 'images/transports/transport-sms-32x32.png',
        smallIcon: 'images/transports/sms_transport_splitter.png',
        largeIcon: 'images/transports/sms-logo.png',
		title: $L('Text'),
		displayName: $L('Text'),
		secondaryDisplayName: $L('Text')
      },
      'phone': {
	  	cannotBeAdded: true,
        className: 'sms',
        smallIcon: 'images/transports/sms_transport_splitter.png',
        largeIcon: 'images/transports/sms-logo.png',
		title: $L('Text'),
		displayName: $L('Text'),
		secondaryDisplayName: $L('Text')
      },
      'gmail': {
        className: 'gmail',
        iconPath: 'images/transports/transport-gmail-32x32.png',
        smallIcon: 'images/transports/gtalk_transport_splitter.png',
        largeIcon: 'images/transports/gtalk-logo.png',
		title: $L('Google Talk'),
		displayName: $L('Google Talk'),
		secondaryDisplayName: $L('Google')
      },
      'aol': {
        className: 'aol',
        iconPath: 'images/transports/transport-aol-32x32.png',
        smallIcon: 'images/transports/aim_transport_splitter.png',
        largeIcon: 'images/transports/aim-logo.png',
		title: $L('AIM'),
		displayName: $L('AIM'),
		secondaryDisplayName: $L('AIM')
      },
	  'yahoo': {
        className: 'yahoo',
        iconPath: 'images/transports/transport-yahoo-32x32.png',
        smallIcon: 'images/transports/yahoo_transport_splitter.png',
        largeIcon: 'images/transports/yahoo-logo.png',
		title: $L('Yahoo'),
		displayName: $L('Yahoo'),
		secondaryDisplayName: $L('Yahoo!')
      },
	  'icq': {
        className: 'icq',
        iconPath: 'images/transports/transport-icq-32x32.png',
        smallIcon: 'images/transports/icq_transport_splitter.png',
        largeIcon: 'images/transports/icq-logo.png',
		title: $L('ICQ'),
		displayName: $L('ICQ'),
		secondaryDisplayName: $L('ICQ')
      },
      'default': {  // TODO: get assets for this default case
	  	cannotBeAdded: true,
        className: 'default',
        smallIcon: 'images/transports/default_transport_splitter.png',
        largeIcon: 'images/transports/default-logo.png',
		displayName: '',
		secondaryDisplayName: ''      
      }
    },
    getTransportByName: function(name) {
      var result = this.data['default'];
      
      if(!name)
        result = this.data.phone;
      else {
        name = name.toLowerCase();
        if(this.data[name])
          result = this.data[name];
      }
      return result;
    }
  }

	/**
	 * Extend an object with this to make it observable 
	 */
	var Observable = function() {
		var observers = [];
		return {
			// This is potentially dangerous.  Whenever observe is called, stopObserving needs to be called when
			// the scene is cleaned up, or the headless app will still have a reference to the scene after it has
			// been closed
			observe: function(fn){
				observers.push(fn);
			},
			// method signature must be the same object that was passed into observe()
			stopObserving: function(fn){
				// iterate over observers and remove this function if a match is found
				var foundFn = false;
				for (var i = 0; i < observers.length && !foundFn; i++) {
				if (observers[i] === fn) {
						foundFn = true;
						observers.splice(i, 1);
					}
				}
			},
			notifyObservers: function(data) {
				for(var i=0; i<observers.length; i++) {
					observers[i](data);
				}					
			}
		}	
	};	

	/**
	 * Subscribed to Debug data.  This object is observable.
	 * this.DisplayState.observe(callback) passes the stored message queue
	 * to the specified callback
	 */

	this.Debug = function() {
		var hasInit = false;
		var dataRequest;
		var debugData = [];
		var queueSize = 100;
		var handleUpdate = function(data) {
			if(data.returnValue) // sent to us when the subscription is initially set up
				return;
				
			if(debugData.length > queueSize) {
				debugData.shift();
			}
			// add the timestamp to the data
			data.timestamp = new Date();
			
			debugData.push(data);
			this.notifyObservers(debugData);
		};
		
		return Object.extend({
			init: function() {
				if (!hasInit) {
					hasInit = true;
					dataRequest = MessagingMojoService.registerForDebugMessages(handleUpdate.bind(this));
				}
			},
			getDebugData: function() {
				return debugData;	
			},
			addDebugMessage: function(str) {
				handleUpdate.bind(this)({
					'debugMessage': str
				});
			}
		},new Observable());
	}();	

	/**
	 * Subscribed to IM Account data.  This object is observable.
	 */
	this.IMAccounts = function(d) {
		var Debug = d;
		var hasInit = false;
		var serviceRequest;
		var handleUpdate = function(data) {
			///////////////////////////
			// Temporary for Debugging
			var debugData = "";
			
			if(data.list.length == 0)
				debugData = "No Data!";
			for(var i=0; i<data.list.length; i++) {
				var account = data.list[i];
				debugData += '[' + account.username + ' : ' + account.availability + '] '; 
			}
			Debug.addDebugMessage('App - IMAccountData: ' + debugData);
			///////////////////////////
			
			// store the new data
			this.data = data;
			this.notifyObservers(data);
		};
		
		return Object.extend({
			data: [],
			init: function() { // create the IM account subscription
				if (!hasInit) {
					hasInit = true;
					serviceRequest = MessagingMojoService.getIMAccounts({
						onSuccess: handleUpdate.bind(this)
					});
				}
			},
			getAccountFromAccountTagOrServiceName: function(accountTag, serviceName) {
				var match = null;
				var account = null;
				// try to match on the accountTag first
				if(accountTag) {
					for(var i=0; i<this.data.list.length && !match; i++) {
						account = this.data.list[i];
						if (account.accountTag == accountTag) {
							match = account;
						}
					}
				}
				// if no match was found, look for an account with the same service name
				if(serviceName && !match) {
					for(var i=0; i<this.data.list.length; i++) {
						account = this.data.list[i];
						if (account.domain == serviceName) {
							// if we have multiple accounts with the same serviceName, choose the one with the lowest
							// availability value
							if (!match || (match && match.availability < account.availability)) {
								match = account;
							}
						}
					}
				}
				return match;
			},
			getMyAccountTypesHash: function() {
				var myAccountTypes = {};
				for(var i=0; i<this.data.list.length; i++) {
					var account = this.data.list[i];
					myAccountTypes[account.domain] = true;
				}
				return myAccountTypes;
			}		
		},new Observable());
	}.bind(this)(this.Debug);

	this.SupportedIMAccountTypes = function() {
		var hasInit = false;
		var getAccountTypesRequest;
		var handleAccountTypeData = function(result) {
			if (result) {
				this.data = result;
			}	
		};
		return {
			data: [],
			init: function() {
				if (!hasInit) {
					hasInit = true;
					getAccountTypesRequest = MessagingMojoService.getAccountTypes(null, handleAccountTypeData.bind(this));
				}
			},
			getAccountTypesHash: function() {
				var accountTypes = {};
				for(var i=0; i<this.data.list.length; i++) {
					var account = this.data.list[i];
					accountTypes[account.domain] = true;
				}
				return accountTypes;				
			}
		}
	}();

	this.isIMAccountTypeSupported = function(serviceName) {
		var supportedAccountsHash = this.SupportedIMAccountTypes.getAccountTypesHash();
		var myAccountsHash = this.IMAccounts.getMyAccountTypesHash();
		var result = {
			returnValue: true,
			isSupportedType: true,
			hasAccount: true
		};
	
		// we do not support this account type
		if (!supportedAccountsHash[serviceName]) {
			result.isSupportedType = false;
			result.returnValue = false;
			//result.errorText = $L("The specified IM type is not supported.");
		// the user has not created an account of this type
		} else if (!myAccountsHash[serviceName]) {
			result.hasAccount = false;
			result.returnValue = false;
			//result.errorText = $L("You have not set up an IM account of this type.");						
		}
		
		return result;		
	};

	/**
	 * The pending timeout is 30 seconds.  The list view will force this to complete early if list updates have calmed down
	 * @param {Object} data
	 * @param {Object} timeoutDuration
	 */
	this.pendingManager = function(messaging,timeoutDuration){
	    var pendingTimeout;
	    var pendingCompleteFunction;
		var Messaging = messaging;	
		var pendingTimeoutDuration  = timeoutDuration;
	    var isPending = false;		
		
		var pendingCompleteFunctionWrapper = function() {
			isPending = false;
			pendingTimeout = null;
			if (pendingCompleteFunction) {
				pendingCompleteFunction(Messaging.IMAccounts.data, true);
			}
		};
		
		var startTimeout = function(timeoutDuration) {
			pendingTimeout = setTimeout(pendingCompleteFunctionWrapper, timeoutDuration);
		};
		
		var cancelTimeout = function() {
            if(pendingTimeout) {
                clearTimeout(pendingTimeout);
                pendingTimeout = null;
            }			
		};
		
	    return {
			setPendingCompleteFunction: function(fn) {
				if(fn)
					pendingCompleteFunction = fn;
			},
			clearPendingCompleteFunction: function() {
				pendingCompleteFunction = null;
			},
	        startPendingTimeout: function() {
	            if(pendingTimeout)
					cancelTimeout();
				
				startTimeout(pendingTimeoutDuration);
	        },
			// This will cancel the pending timeout, but we will stay pending
			cancelPendingTimeout: function() {
				cancelTimeout();				
			},
			// reset the timer to the specified value in milliseconds
			resetTimeoutIfRunning: function(newTimeoutDuration) {
	            if (pendingTimeout) { // only reset if the timer is running
					clearTimeout(pendingTimeout);
					startTimeout(newTimeoutDuration);
				}				
			},			
	        forcePendingComplete: function() {
				isPending = false;
				cancelTimeout();
	            pendingCompleteFunctionWrapper();
	        },
			isPendingTimeoutRunning: function() {
				if(pendingTimeout)
					return true;
				else
					return false;
			},
	        setIsPending: function(isPendingValue) {
	            isPending = isPendingValue;
	        },
	        isPending: function() {
	            return isPending;
	        }
	
	    };
	}(this,30000);

	var handleAvailabilityPendingState = function() {
		return function(data){
			var account;
			var hasPending = false;
			var currentAvailability = null;
			for(var i=0; i<data.list.length && !hasPending; i++) {
				account = data.list[i];
					
				if(currentAvailability == null || account.availability < currentAvailability)
					currentAvailability = account.availability;
				
				if(account.availability == this.Availability.PENDING) {
					currentAvailability = this.Availability.PENDING;
					hasPending = true;
				}
			};      			
			
			if(!this.pendingManager.isPending() && currentAvailability == this.Availability.PENDING) {
				this.pendingManager.setIsPending(true);
			}			
			
			// if we are offline then force any pending to stop
			if(currentAvailability == this.Availability.OFFLINE && (this.pendingManager.isPending() || this.pendingManager.isPendingTimeoutRunning())) {
				this.pendingManager.forcePendingComplete();
			} else if(currentAvailability != this.Availability.PENDING && this.pendingManager.isPending() && !this.pendingManager.isPendingTimeoutRunning()) {
				this.pendingManager.startPendingTimeout();				
			}
		}.bind(this)			
	}.bind(this)();
	
	this.IMAccounts.observe(handleAvailabilityPendingState);
	/*
	this.BuddyGroups = function(groups){
		var groupsArr = [];
		var groupsIndexHash = {};		
		  
		return {
			updateGroups: function(groups) {
				groupsArr = [];
				groupsIndexHash = {};
				if (groups && groups.list) {
					var count = 0;
					for(var i=0; i<groups.list.length; i++){
						var group = groups.list[i];
						var isGroupExpanded = false;
						
						if (group.isGroupCollapsed) {
							isGroupExpanded = !parseInt(group.isGroupCollapsed)
						}
						if (group.groupNameLowerCase == String.fromCharCode(255, 255, 255, 255)) {
							group.groupName = $L("Offline");
						}
						var tmp = {
							id: group.id,
							name: group.groupName,
							nameLowerCase: group.groupNameLowerCase,
							expanded: isGroupExpanded,
							expandedClass: isGroupExpanded ? "palm-arrow-expanded" : "palm-arrow-closed",
							selectedClass: ''
						};
						groupsArr.push(tmp);
						groupsIndexHash[group.groupNameLowerCase] = count++;
					}
				}				
			},
			
			getGroup: function (groupName) {
				if(groupsIndexHash[groupName] != undefined)
					return groupsArr[groupsIndexHash[groupName]];
				else
					return null;
			},
			
			getGroupAndAllPreviousCollapsed: function (groupName) {
				if(groupsArr[groupsIndexHash[groupName]] == undefined) {
					Mojo.Log.warn("Error getGroupAndAllPreviousCollapsed - Group name does not exist.");
					return [];
				}
				var currentGroup = null;
				var foundCurrentGroup = false;
				var collapsedGroups = [];
				var foundNextOpenGroup = false;				
				
				// TODO: clean this up now that we have an index hash	
				for(var i=groupsArr.length-1; i >= 0 && !foundNextOpenGroup; i--) {
					var group = groupsArr[i];
					
					if(foundCurrentGroup) {
						if(!group.expanded)
							collapsedGroups.push(group);
						else
							foundNextOpenGroup = true;
					}
					
					// found the current group
					if (group.nameLowerCase == groupName) {
						foundCurrentGroup = true
						currentGroup = group;
					}
				}
				collapsedGroups = collapsedGroups.reverse();
				if(currentGroup)
					collapsedGroups.push(currentGroup);	
	
				return collapsedGroups;				
			},
			getAllCollapsedGroupNames: function() {
				var collapsed = [];
				for(var i = 0; i < groupsArr.length; i++) {
					var group = groupsArr[i];
					if(!group.expanded)
						collapsed.push(group.name);
				}
				return collapsed;
			},
			getLastGroupName: function() {
				if(groupsArr.length > 0)
					return groupsArr[groupsArr.length-1].nameLowerCase;
				else
					return null;
			},
			getGroupByIndex: function(index) {
				if(groupsArr && groupsArr[index])
					return groupsArr[index];
				else
					return null;
			},			
			expandGroup: function (groupName) {
				var groupIndex = groupsIndexHash[groupName];
				if (groupIndex != undefined) {
					var groupObject = groupsArr[groupIndex];
					//groupObject.expanded = true;
					groupObject.expandedClass = "palm-arrow-expanded";
					groupObject.selectedClass = "selected";
				} else {
					Mojo.Log.info("Error expanding group.  Group name does not exist");
				}					
			},
			collapseGroup: function (groupName) {
				var groupIndex = groupsIndexHash[groupName];
				if (groupIndex != undefined) {
					var groupObject = groupsArr[groupIndex];
					//groupObject.expanded = false;
					groupObject.expandedClass = "palm-arrow-closed";
					groupObject.selectedClass = "selected";
				} else {
					Mojo.Log.info("Error collapsing group.  Group name does not exist");
				}			
			}
		};
	}();
	*/

	/**
	 * Subscribed to Screen On/Off.  This object is observable.
	 * this.DisplayState.observe(callback) passes the boolean isDisplayOn
	 * to the specified callback
	 */
	this.DisplayState = function() {
		var hasInit = false;
		var dataRequest;
		var isDisplayOn = true;
		var handleUpdate = function(data) {
			// store the new data
			//this.data = data;
			
			if(data.event) {
				if(data.event == 'displayOn')
					isDisplayOn = true;
				else if(data.event == 'displayOff')
					isDisplayOn = false;
			}
			
			this.notifyObservers(isDisplayOn);
		};
		
		return Object.extend({
			init: function() {
				if (!hasInit) {
					hasInit = true;
					dataRequest = MessagingMojoService.getDisplayStatus(handleUpdate.bind(this));
				}
			},
			isDisplayOn: function() {
				return isDisplayOn;	
			}
		},new Observable());
	}();

	this.init();
}


Messaging.prototype.init = function() {
	var retryCount = 25;
	// get preference data + subscribe
	var f = function(data) {
		var needsRequery = false;
		if (data.messagingPrefs === undefined) {
			Mojo.Log.error("Messaging.init ... data.messagingPrefs === undefined");
			needsRequery = true;
		} else {
			this.messagingPrefs = data.messagingPrefs;
			// Fixup ringtone if it isn't set.
			if (this.messagingPrefs.enableNotificationSound == 2 &&
			    (!this.messagingPrefs.notificationRingtoneName || !this.messagingPrefs.notificationRingtonePath)) {
				Mojo.Log.warn("Rintone not set, reverting to system sound");
				this.messagingPrefs.enableNotificationSound = 1; //system sound
			}
 
			// check to see if we have a platform type 
			// if not, query for it and save it via the service
			this.handlePlatformType(this.messagingPrefs);
		}
		
		if (data.mmsPrefs === undefined) {
			Mojo.Log.error("Messaging.init ... data.mmsPrefs === undefined");
			needsRequery = true;
		} else {
			this.mmsPrefs = data.mmsPrefs;
		}
		
		if (data.carrierSettings === undefined) {
			Mojo.Log.error("Messaging.init ... data.carrierSettings === undefined");
			needsRequery = true;
		} else {
			this.carrierSettings = data.carrierSettings;
			// Special check because sometimes the result is not properly expanded to its byte value
			if (data.carrierSettings.mmscMessageSizeLimit < 9000) {
				Mojo.Log.error("Messaging.init ... data.carrierSettings.mmscMessageSizeLimit = %n", data.carrierSettings.mmscMessageSizeLimit);
				needsRequery = true;
			}
		}
		
		if (needsRequery === true && --retryCount > 0) {
			this.prefsRequest.cancel();
			this.prefsRequest = MessagingMojoService.getAllMessagingPreferences({onSuccess:f});	
		}
	}.bind(this);
	
	this.prefsRequest = MessagingMojoService.getAllMessagingPreferences({onSuccess:f});	
	
	// get IM Account data + subscribe
	this.IMAccounts.init();
	this.DisplayState.init();
	this.SupportedIMAccountTypes.init();
	this.Debug.init();
};

Messaging.prototype.handlePlatformType = function(prefHash) {
	if (!prefHash.platformType) {
		this.platformTypeRequest = MessagingMojoService.getPlatformType(function(result){
			//{"returnValue": true, "extended": {"meid": "A100000640014A", "platformType": "cdma"}}
			if(result.extended && result.extended.platformType) {
				var platformType = result.extended.platformType.toLowerCase();
				prefHash.platformType = platformType; // set this locally first
				// store the platform type so we don't have to look it up every time
				MessagingMojoService.setPlatformType(platformType);
			} else {
				Mojo.Log.warn('Unable to retrieve platform type.  The app will limit SMS messages to 160 characters');
			}
		}.bind(this))
	}
};

Messaging.prototype.setListViewVisible = function(view) {
	if(!view) {
		return;
	}
	if (this.listviewSceneAssistant) {
		if (view == this.Views.BUDDY && this.listviewSceneAssistant.loadBuddyView) {
			this.listviewSceneAssistant.loadBuddyView(true);
		} else if(view == this.Views.HISTORY && this.listviewSceneAssistant.loadHistoryView) {
			this.listviewSceneAssistant.loadHistoryView(true);
		}
	}		
	
	this.messagingPrefs.isHistoryViewSelected = (view == this.Views.HISTORY);
	this.setHistoryViewSelectedRequest = MessagingMojoService.setIsHistoryViewSelected(null, this.messagingPrefs.isHistoryViewSelected);		
};


// In the process of deprecating these constants... almost there.
var CONSTANTS = {
  PHONE_ICON : 'serviceicon-sms.png',
  GMAIL_ICON : 'msg-header-gtalk.png',
  PHONE_TYPE : 'PHONE',
  CUSTOM_MESSAGE : $L('Custom Message...'),
  PHONE_CLASS : 'sms',
  GMAIL_CLASS : 'gmail',
  YAHOO_CLASS : 'yahoo',
  SCENE_ID_LISTVIEW: 'listview',
  // This is used when launching the initial prefs scene.  We specify an ID
  // that we can use to jump back to this first scene (Mojo.Controller.stageController.popScenesTo)
  SCENE_ID_ACCOUNT_SUMMARY: 'prefAcctSummary',
  SCENE_ID_FIRST_LAUNCH: 'prefFirstLaunch',
  PHOTO_PICKER_DATA: [ // This is deprecated
      {label: "jabba", value: "jabba.jpg"},
      {label: "img500x50", value: "img500x50.jpg"},
      {label: "img50x500", value: "img50x500.jpg"},
      {label: "img50x50", value: "img50x50.jpg"}
  ],
  //NOTE text for 15 and 16 are verizon required strings for those errors
  ERROR: {
	1:$L("Unknown error while downloading the message."),
	2:$L("The attachment is too large."),
	3:$L("Message not found on server."),
	4:$L("A network error occurred."),
	5:$L("Message expired or not available."),
	6:$L("Message is corrupt."),
	7:$L("Unable to connect to MMS server."),
	8:$L("Database failure."),
	9:$L("Message content rejected."),
	10:$L("Message transport not initialized."),
	11:$L("Unknown error while sending the message."),
	12:$L("A network error occurred."),
	13:$L("Message content is corrupt."),
	14:$L("Device is full. Delete files to clear space."),
	15:$L("Service not activated on network."),
	16:$L("Invalid destination address."), 
	17:$L("Attachment file size exceeds the maximum allowed.") 
  }
}
