/**
 * @author rogerstringer
 */
var levelAssistantClass = null;

var contextLevels       = [{"context":"&lt;global&gt;", "level":"debug"},];
var levelNames          = [{"name":"none"},{"name":"emerg"},{"name":"alert"},{"name":"crit"},{"name":"err"},{"name":"warning"},{"name":"notice"},{"name":"info"},{"name":"debug"},];

var mojoLogLevels       = [];
var mojoLevelNames      = [{"name":"error", "logLevel":0},{"name":"warning", "logLevel":10},{"name":"info", "logLevel":20},]; 

var javaLevelNames      = [{"name":"SEVERE"},{"name":"WARNING"},{"name":"INFO"},{"name":"CONFIG"},{"name":"FINE"},{"name":"FINER"},{"name":"FINEST"},];
var javaLogLevels       = [];
var javaServiceNames    = []; // transient
var loadingJavaLogLevels= false;

var rdxdApplName        = "rdxd";
var rdxdApplLogLevel    = null;
var telephonyApplName   = "telephony";
var telephonyApplLogLevel = null;

// used to map changes in process
var levelSelected       = null;
var contextSelected     = null;

var mojoApplSelected    = null;
var mojoLevelSelected   = null;

var javaServiceSelected = null;
var javaLevelSelected   = null;

var customApplSelected = null;

var changeList          = [];
var resetInProcess      = false;
var profileUpdateList   = [];
var profileApplyErrorCount = 0;

var bVerifyJSLevels     = false;

// this section is psuedo constants whose value does not change
var defaultStyle        = "\"color: black; \"";
var selectedStyle       = "\"color: silver; background-color: maroon;\"";
var changedStyle        = "\"color: maroon; font-style: italic;\"";
var originalStyle       = "\"color: teal; \"";

var PMLOG_NAME          = "pmlog";
var MOJO_NAME           = "mojo";
var JAVA_NAME           = "java";
var CUSTOM_NAME         = "special";

var NEW_SERVICE_NAME    = "palm://com.palm.logctld";
var JAVA_SERVICE_BASE   = "palm://com.palm.";

var buildingJavaList    = [];
var buildingJavaListName = "Analyzing, please wait...";


// need to test when luna-send isn't functional
var inSimulator         = false; // set to true in simulator
var	testContextLevels   = [{"context":"&lt;global&gt;", "level":"debug"},{"context":"TIL.CCP", "level":"err"},{"context":"TIL.LOG", "level":"err"},{"context":"PmLogCtl", "level":"info"},{"context":"com.palm.wifi", "level":"info"},{"context":"netcfg.lib", "level":"debug"},{"context":"netcfg.mgr", "level":"debug"},{"context":"powermond", "level":"debug"},{"context":"BluetoothMonitor", "level":"debug"},{"context":"wan.mgr", "level":"debug"},{"context":"uploadd", "level":"debug"},{"context":"tidspmon", "level":"debug"},{"context":"media", "level":"debug"},{"context":"ConMan", "level":"debug"},{"context":"PmBtContacts", "level":"debug"},{"context":"PmBtEngine", "level":"debug"},];
var testMojoLogLevels   = [{"appl":"com.palm.app.backup","title":"Backup","logLevel":10,},{"appl":"com.palm.app.browser","title":"Web","logLevel":99,},{"appl":"com.palm.app.docviewer","title":"Doc View","logLevel":99,},{"appl":"com.palm.app.filepicker","title":"File Picker","logLevel":0,},{"appl":"com.palm.app.findapps","title":"App Catalog","logLevel":20,},{"appl":"com.palm.app.firstuse","title":"First Use","logLevel":20,},{"appl":"com.palm.app.location","title":"Location","logLevel":20,},{"appl":"com.palm.app.maps","title":"Google Maps","logLevel":99,},{"appl":"com.palm.app.messaging","title":"Messaging","logLevel":0,},{"appl":"com.palm.app.notes","title":"Memos","logLevel":0,},{"appl":"com.palm.app.phone","title":"Phone","logLevel":99,},{"appl":"com.palm.app.tasks","title":"Tasks","logLevel":0,},];
var testJavaLogLevels   = [{"name":"contacts","level":"INFO",},{"name":"calendar","level":"FINE"},{"name":"accounts","level":"SEVERE"},{"name":"pimsync","level":"WARNING"},{"name":"messaging","level":"FRED"},{"name":"messagingrouter","level":"WARNING"},{"name":"mail","level":"FINEST"},{"name":"findapps","level":"WARNING"},{"name":"customization","level":"WARNING"},{"name":"location","level":"WARNING"},{"name":"notes","level":"WARNING"},{"name":"packageinfo","level":"WARNING"},];


////////////////////////////////////////////////////////////////////////////////////////////////////////
/// The contolling class
var LevelAssistant = Class.create ({
	
	javaServiceTimer: null,
	
	initialize: function() {
	/* this is the creator function for your scene assistant object. It will be passed all the 
	   additional parameters (after the scene name) that were passed to pushScene. The reference
	   to the scene controller (this.controller) has not be established yet, so any initialization
	   that needs the scene controller should be done in the setup function below. */
	  levelAssistantClass = this;
	},

	setup: function() {
		this._HandleJavaServiceRequestTimeout = this.handleJavaServiceRequestTimeout.bind(this);

		// Setting up a true Listwidget. Define the list model
		// Note that we specify a callback function for loading list items in the widget attributes.
		Mojo.Log.info ("Setup(): Level: Lists");

		this.controller.setupWidget('mojoLogList', this.mojoListAttributes, this.mojoListModel);

		this.controller.setupWidget('systemLogList', this.systemListAttributes, this.systemListModel);

		this.controller.setupWidget('javaLogList', this.javaListAttributes, this.javaListModel);

		this.controller.setupWidget('rdxdButton', this.customListAttributes, this.customListModel_rdxd);
		//Mojo.Event.listen('rdxdButton', 'mojo-propertyChange', this.handleRdxdButtonUpdate);

		this.controller.setupWidget('telephonyButton', this.customListAttributes, this.customListModel_tele);
		//Mojo.Event.listen('telephonyButton', 'mojo-propertyChange', this.handleTelephonyButtonUpdate);

		Mojo.Log.info ("Setup(): App Menu");
		this.levelMenuModel = {
			items: [
                {label: $L('Undo'), command: 'undo'},
                {label: $L('Reset changes'), command: 'reset'},
				{
					label:$L('Import/Export'), 
					items: [
						{label: $L('Import Profile...'), command: 'import'},
						{label: $L('Export Profile...'), command: 'export'},
					]
				},                
                {label: $L('Help...'), command: 'help'}
			],
		};
		this.controller.setupWidget(Mojo.Menu.appMenu, {omitDefaultItems: true}, this.levelMenuModel);
		
		this.controller.setupWidget('process_spinner', this.spinnerAttributes, this.spinnerModel);
		this.controller.setupWidget('profile_name', this.descAttributes, this.descModel);
		
		this.profileView = this.controller.get('levelProfileView');
		this.logListView = this.controller.get('levelLogListView');
		this.COMMAND_MENU = {
			loadProfileView: {command: 'cmdLoadProfile', target: this.loadProfileView.bind(this) },
			loadLogListView: {command: 'cmdLoadLogList', target: this.loadLogListView.bind(this) },
		};
		this.toggleCmd = this.COMMAND_MENU.loadProfileView.command;
		this.currentView = this.profileView;
		this.logListView.style.display = 'none';
		this.profileView.style.display = 'block';
	    
	    // Specifying the width in pixels is a hack.  The widget should support percentages.
		Mojo.Log.info ("Setup(): View Menu");
	    this.topMenuModelItems = [ {
			label: 'logList/profile',
			toggleCmd: this.toggleCmd,
			items: [{
				label: $L('Set Log Levels'),
				command: this.COMMAND_MENU.loadLogListView.command,
				width: 160
			}, {
				label: $L('Use Log Profile'),
				command: this.COMMAND_MENU.loadProfileView.command,
				width: 160
			}]
		}];         

		// Setup the top (view) menu
		this.topMenuModel = {
			visible: true,
			items: this.topMenuModelItems
		}; 
	    this.controller.setupWidget(Mojo.Menu.viewMenu, undefined, this.topMenuModel);
		
		
		this.controller.setupWidget('profileLevelList', this.profileListAttributes, this.profileListModel);

	},
	
	
	activate: function(event) {
		/* put in event handlers here that should only be in effect when this scene is active. For
		   example, key handlers that are observing the document */
		Mojo.Log.info ("activate(): Level setup");
		this.controller.get('mojoLogList').observe(Mojo.Event.listTap, this.handleMojoListTap.bindAsEventListener(this));
		this.controller.get('systemLogList').observe(Mojo.Event.listTap, this.handleListTap.bindAsEventListener(this));
		this.controller.get('javaLogList').observe(Mojo.Event.listTap, this.handleJavaListTap.bindAsEventListener(this));
		this.controller.get('rdxdButton').observe(Mojo.Event.propertyChange, this.handleRdxdButtonUpdate.bindAsEventListener(this));
		this.controller.get('telephonyButton').observe(Mojo.Event.propertyChange, this.handleTelephonyButtonUpdate.bindAsEventListener(this));

		Mojo.Log.info ("activate(): profile setup");
		this.controller.get('manage_profiles_button').observe(Mojo.Event.tap, this.handleProfileButton.bindAsEventListener(this));
		this.controller.get('minimal_button').observe(Mojo.Event.tap, this.handleMinimalButton.bindAsEventListener(this));
		this.controller.get('profileLevelList').observe(Mojo.Event.listTap, this.handleProfileListTap.bindAsEventListener(this));
 		this.controller.get('profileLevelList').observe(Mojo.Event.listDelete, this.handleProfileListDelete.bindAsEventListener(this));
		Mojo.Log.info ("activate(): getprofilelist");
		this.profileListModel.items = profileStore.getProfileList(true);
		this.controller.modelChanged(this.profileListModel);

  		Mojo.Log.info ("activate(): initialize");
	  	this.getLevelNames();
		this.getContextLevels();
		this.getMojoLogLevels();
		this.getCustomLogLevels();
		this.getJavaLogLevels();
		bVerifyJSLevels = false;
	},
	
	deactivate: function(event) {
		/* remove any event handlers you added in activate and do any other cleanup that should happen before
		   this scene is popped or another scene is pushed on top */
		this.controller.get('mojoLogList').stopObserving(Mojo.Event.listTap, this.handleMojoListTap);
		this.controller.get('systemLogList').stopObserving(Mojo.Event.listTap, this.handleListTap);
		this.controller.get('javaLogList').stopObserving(Mojo.Event.listTap, this.handleJavaListTap);
		this.controller.get('rdxdButton').stopObserving(Mojo.Event.propertyChange, this.handleRdxdButtonUpdate);
		this.controller.get('telephonyButton').stopObserving(Mojo.Event.propertyChange, this.handleTelephonyButtonUpdate);
		this.controller.get('manage_profiles_button').stopObserving(Mojo.Event.tap, this.handleProfileButton);
		this.controller.get('minimal_button').stopObserving(Mojo.Event.tap, this.handleMinimalButton);
		this.controller.get('profileLevelList').stopObserving(Mojo.Event.listTap, this.handleProfileListTap);
 		this.controller.get('profileLevelList').stopObserving(Mojo.Event.listDelete, this.handleProfileListDelete);
	},
	
	cleanup: function(event) {
		/* this function should do any cleanup needed before the scene is destroyed as 
		   a result of being popped off the scene stack */
		contextLevels = [{"context":"<global>", "level":"debug"},]; // revert to default
		mojoLogLevels = [];
		javaLogLevels = [];
		while (javaServiceNames.length > 0) {javaServiceNames.pop();}

	},

	////////////////////////////////////////////////////////////////////////
	//   Handle command methods
	////////////////////////////////////////////////////////////////////////
	
	loadProfileView: function(updateToggle) {
	    
		if (!this.currentView || this.currentView != this.profileView) {
			this.logListView.style.display = 'none';			
			this.profileView.style.display = 'block';
			this.controller.sceneScroller.mojo.revealTop(0);
			this.currentView = this.profileView;			
		}
		
		if(updateToggle && this.topMenuModelItems[0].toggleCmd != this.COMMAND_MENU.loadProfileView.command) {
	       this.topMenuModelItems[0].toggleCmd = this.COMMAND_MENU.loadProfileView.command;
	       this.controller.modelChanged(this.topMenuModel);      
	    }  	
	},

	loadLogListView: function(updateToggle) {
	    
		if (!this.currentView || this.currentView != this.logListView) {
			this.profileView.style.display = 'none';			
			this.logListView.style.display = 'block';
			this.controller.sceneScroller.mojo.revealTop(0);
			this.currentView = this.logListView;			
		}
		
		if(updateToggle && this.topMenuModelItems[0].toggleCmd != this.COMMAND_MENU.loadLogListView.command) {
	       this.topMenuModelItems[0].toggleCmd = this.COMMAND_MENU.loadLogListView.command;
	       this.controller.modelChanged(this.topMenuModel);      
	    }  	
	},

 	handleCommand: function(event) {
		if (event.type == Mojo.Event.command) {
			if (event.command == 'reset') {
				Mojo.Log.info("------------------ Reset -------------------------");
				if (false == resetInProcess) {
					if (0 == changeList.length) {
						this.showDialogBox(null, "There are no changes to be reset!")
					}
					else {
						resetInProcess = true;
						if (changeList.length > 10) {this.activateSpinner("Reset Changes");}
						this.resetChanges(null, true);
					}
				} else {Mojo.Log.info ("handleCommand(): Reset ignored");}
				
			} else if (event.command == 'undo') {
				Mojo.Log.info("------------------ Undo -------------------------");
				if (false == resetInProcess) {
					if (0 == changeList.length) {
						this.showDialogBox(null, "There are no changes to undo!")
					}
					else {
						resetInProcess = true;
						this.resetChanges(1, true);
					}
				}else {Mojo.Log.info ("handleCommand(): Undo ignored");}
				
			} else if (event.command == 'help') {
				Mojo.Log.info("------------------ help -------------------------");
				this.showDialogBox(null, 
					(this.currentView == this.profileView 
				 		? "Select a pre-built set of log levels by tapping a profile description."
							 + (this.profileListModel.items.length > 0 ? "<br><br>To delete an existing profile, drag it off the side of the screen." : "")
						: "Scroll to find the program you are interested in.  "
							+ "Tap on the name and select a new logging level."
							+ "<br>Before closing the application, you can reset all your changes by selecting the \"Reset changes...\" menu item"
					)
					+ "<br><br><i>Changes to Mojo applications don't take effect while they are running, you'll have to exit and restart them.</i>"
					+ "<br><i>Changes to Java modules don't currently survive a reboot.</i>"
					+ "<br><br>If you set a more detailed logging, but don't see the extra detail, you may need a development version of PmLog.conf");

			} else if (event.command == 'cmdLoadProfile') {
				Mojo.Log.info("------------------ cmdLoadProfile -------------------------");
				this.loadProfileView (true);
				
			} else if (event.command == 'cmdLoadLogList') {
				Mojo.Log.info("------------------ cmdLoadLogList -------------------------");
				if (this.spinnerModel.spinning == true) {
	       			this.topMenuModelItems[0].toggleCmd = this.COMMAND_MENU.loadProfileView.command;
	       			this.controller.modelChanged(this.topMenuModel);      
					this.showDialogBox(null, "Please wait until current activity has completed!");
				} else {
					this.loadLogListView(true);
				}
			} else if (event.command == 'import') {
				if (this.spinnerModel.spinning == true) {
					this.showDialogBox (null, "Please wait until current activity has completed!");
				} else {
					Mojo.Log.info("handleCommand(): import");
					this.fetchProfileFile();
				}
			} else if (event.command == 'export') {
				if (profileStore.getExportStatus() == true) {
					this.showDialogBox (null, "An export of a log profile is already in process!");
				} else {
					Mojo.Log.info("handleCommand(): export");
					this.exportProfileFile();
				}
			} else {
				Mojo.Log.info("------------ Command [" + event.command +  "] ignored! -------------------------");
			}
		}
	},
	
	handleProfileButton: function (event) {
		Mojo.Log.info("------------------ Manage profiles -------------------------");
		if (false == resetInProcess) {
			Mojo.Controller.stageController.pushScene('profile', this);
		} else {Mojo.Log.info ("handleProfileButton(): Manage ignored");}
	},
	
	handleMinimalButton: function (event) {
		Mojo.Log.info("------------------ Minimal -------------------------");
		if (false == resetInProcess) {
			this.controller.showAlertDialog({
				title: $L("Are you sure?"),
				message: $L("<b>Do you want to set all log levels to minimal?</b> <br>This means logging only errors. <br><br>This can't be undone."),
				allowHTMLMessage: true,
				choices: [{label: $L('OK'), value: 'ok',type: 'affirmative'},
						{label: $L('Cancel'), value: 'cancel', type: 'negative'}],
				onChoose: function(value) {
					if (value === 'ok') {
						resetInProcess = true;
						this.minimalProfile ();
					}
				}
			});	
		} else {Mojo.Log.info ("handleMinimalButton: Minimal ignored");}
	},
	
	////////////////////////////////////////////////////////////////////////
	//   Following set of methods relate to PmLog-based log levels
	////////////////////////////////////////////////////////////////////////
	
	getLevelNames: function () {
		var that = this;
		var saveRequest = this.controller.serviceRequest(NEW_SERVICE_NAME, {
			method: 'getlevelnames',
			parameters: {
			},
			onSuccess: function (result) {
				if (result.returnValue && result.returnValue === "true") {
					levelNames = result.levelNames;
				};
				that.controller.cancelServiceRequest(saveRequest);
			},
			onFailure: function(result) {
				console.log("failed");
			}
		});
	},

	getContextLevels: function () {
		if (inSimulator == true) {contextLevels = testContextLevels;}
		var that = this;
		// get context levels
		var saveRequest = this.controller.serviceRequest(NEW_SERVICE_NAME, {
			method: 'getloglevel',
			parameters: {
			},
			onSuccess: function (result) {
				if (result.returnValue && result.returnValue === "true") {
					contextLevels = result.values;
				};
				that.controller.cancelServiceRequest(saveRequest);
				that.processContextLevels();
			},
			onFailure: function(result) {
				console.log("failed");
				//that.processContextLevels();
			}
		});
		
		if (inSimulator == true) {this.processContextLevels();}
	},
	
	processContextLevels: function () {
		contextLevels.sort(this.sortCallbackContexts);
		this.systemListModel.items = contextLevels;
		for (var i = 0 ; i < contextLevels.length ; i++) {
			contextLevels[i].style = defaultStyle;
			contextLevels[i].levelStyle = (this.findInChangeList(contextLevels[i].context, PMLOG_NAME) === true
					? changedStyle : originalStyle);

		}
		this.controller.modelChanged(this.systemListModel);
	},

	sortCallbackContexts: function (a, b) {
		var aS = a.context.toLowerCase();
		var bS = b.context.toLowerCase();
		if (aS == bS) return 0;
		if (a.context == "&lt;global&gt;") return -1;
		else if (b.context == "&lt;global&gt;") return 1;
		if (aS < bS) return -1;
		if (aS > bS) return 1;
		return 0; // just in case :-)
	},
	
	// returns row id, or -1
	findContextRow: function (contextName) {
		if (!contextName) contextName = contextSelected;
		for (var i = 0 ; i < contextLevels.length ; i++) {
			if (contextName === contextLevels[i].context) {
				return i;
			}
		}
		return -1;
	},

	handleListTap: function (event) {
		if (event.item && event.item.context) {
			//Mojo.Controller.stageController.pushScene('select', event.item.context, event.item.level, levelNames);		
			contextSelected = event.item.context;
			levelSelected = event.item.level;
			this.levelSelection (event);
		}
	},
	
	// listener/handler for a tap - popup menu handling:
    levelSelection: function(e){
		try {
			Mojo.Log.info ("levelSelection(): prepare list of choices");
			var row = this.findContextRow();
			var i, x;
			var items = [];
			for (i = 0, x = (levelNames.length - 1); i < levelNames.length; i++) {
				items.push({
					label: levelNames[i].name.toUpperCase(),
					command: i.toString()
				});
				if (levelNames[i].name === levelSelected) {x = i;}
			}
			
			//this.showDialogBox (levelSelected, items.toString() + "<br> i=" + i.toString() + "<br> x=" + x.toString());
			if (0 == i || row < 0) {return;}
			contextLevels[row].style = selectedStyle;
			this.controller.modelChanged(this.systemListModel);
			
			Mojo.Log.info ("levelSelection(): popup submenu");
			this.controller.popupSubmenu({
				onChoose: this.popupLevelChoose.bind(this),
				placeNear: e.target,
				toggleCmd: x.toString(),
				items: items,
			});
		} catch (e) {
			Mojo.Log.error ("levelSelection(): Exception: " + e)
			this.showDialogBox ($L("Error"), "levelSelection(): Exception: " + e);

		}
	
    },

    // deal with the choice made by the user in the popup menu:
	popupLevelChoose: function(value){
		try {
			var saveLevelName = levelSelected;
			var saveContextSelected = contextSelected;
			var row = this.findContextRow();
			if (row >= 0) {
				contextLevels[row].style = defaultStyle;
				this.controller.modelChanged(this.systemListModel);
			}
			if (value != undefined) {
		        var newName = null;
				var i;
				var that = this;
				i = +(value);
		 		newName = levelNames[i].name;
				if (!value || (!i && value != '0') || i < 0 || i >= levelNames.length || row < 0 
						|| levelSelected == newName) {
					return; // do nothing
				}
				
				contextLevels[row].level = newName;
				this.controller.modelChanged(this.systemListModel);
				
				// set context level
				var saveRequest = this.controller.serviceRequest(NEW_SERVICE_NAME, {
					method: 'setloglevel',
					parameters: {
						'context': contextSelected,
						'level': newName
					},
					onSuccess: function (result) {
						that.controller.cancelServiceRequest(saveRequest);
						that.addToChangeList (saveContextSelected, saveLevelName, PMLOG_NAME);
						that.getContextLevels(); // refresh
					},
					onFailure: function(result) {
						row = that.findContextRow(saveContextSelected);
						contextLevels[row].level = saveLevelName;
						that.controller.modelChanged(that.systemListModel);
						that.showDialogBox ($L("Error"), "Couldn\'t change log level. <br>" +(!result.errorText ? "" : result.errorText));
					}
				});
			} else {
				Mojo.Log.error("popupLevelChoose(): Menu ignored");
			}
		} catch (e) {
			Mojo.Log.error ("popupLevelChoose(): Exception: " + e);
			this.showDialogBox ($L("Error"), "popupLevelChoose(): Exception: " + e);
		}
	},

	////////////////////////////////////////////////////////////////////////
	//   Following set of methods relate to Mojo application log levels
	////////////////////////////////////////////////////////////////////////
	
	getMojoLogLevels: function () {
		if (inSimulator == true) {mojoLogLevels = testMojoLogLevels;}

		var that = this;
		// get log levels
		var saveRequest = this.controller.serviceRequest(NEW_SERVICE_NAME, {
			method: 'getmojologlevel',
			parameters: {
			},
			onSuccess: function (result) {
				that.controller.cancelServiceRequest(saveRequest);
				if (result.returnValue && result.returnValue === "true" && result.mojoApps) {
					mojoLogLevels = result.mojoApps;
				};
				that.processMojoLogLevels();
			},
			onFailure: function(result) {
				console.log("failed");
				//that.processMojoLogLevels();
			}
		});

		if (inSimulator == true) {this.processMojoLogLevels();}
	},
	
	processMojoLogLevels: function () {
		mojoLogLevels.sort(this.sortCallbackMojoLog);
	
		for (var i= 0 ; i < mojoLogLevels.length ; i ++) {
			if (!mojoLogLevels[i].level) {
				mojoLogLevels[i].level = mojoLevelNames[0].name; // default to error
				for (var j = mojoLevelNames.length - 1 ; j >= 0 ; j--) {
					if (mojoLogLevels[i].logLevel && (+mojoLogLevels[i].logLevel) >= (+mojoLevelNames[j].logLevel)) {
						mojoLogLevels[i].level = mojoLevelNames[j].name;
						break;
					}
				}
			}
			mojoLogLevels[i].style = defaultStyle;
			mojoLogLevels[i].levelStyle = (this.findInChangeList(mojoLogLevels[i].appl, MOJO_NAME) === true
					? changedStyle : originalStyle);
		}
	
		this.mojoListModel.items = mojoLogLevels;
		this.controller.modelChanged(this.mojoListModel);
	},

	sortCallbackMojoLog: function (a, b) {
		var aS = a.title.toLowerCase();
		var bS = b.title.toLowerCase();
		if (aS < bS) return -1;
		if (aS > bS) return 1;
		return 0;
	},
	
	handleMojoListTap: function (event) {
		if (event.item && event.item.appl) {
			mojoApplSelected = event.item.appl;
			mojoLevelSelected = event.item.level;
			this.mojoLevelSelection (event);
		}
	},
	
	// returns row id, or -1
	findMojoApplRow: function (applName) {
		if (!applName) applName = mojoApplSelected;
		for (var i = 0 ; i < mojoLogLevels.length ; i++) {
			if (applName === mojoLogLevels[i].appl) {
				return i;
			}
		}
		return -1;
	},

	   // listener/handler for a tap - popup menu handling:
    mojoLevelSelection: function(e){
		try {
			var i, x;
			Mojo.Log.info ("mojoLevelSelection(): Preparing level select");
			var row = this.findMojoApplRow();
			var items = [];
			for (i = 0, x = (mojoLevelNames.length - 1) ; i < mojoLevelNames.length ; i++) {
				items.push ({
					label: mojoLevelNames[i].name.toUpperCase(), 
					command: i.toString()
				});
				if (mojoLevelNames[i].name === mojoLevelSelected) {x = i;}
			}
	
			//this.showDialogBox (levelSelected, items.toString() + "<br> i=" + i.toString() + "<br> x=" + x.toString());
			if (0 == i || row < 0) return;
			mojoLogLevels[row].style = selectedStyle;
			this.controller.modelChanged(this.mojoListModel);
	
			Mojo.Log.info ("mojoLevelSelection(): display submenu");
			this.controller.popupSubmenu({
				onChoose: this.popupMojoLevelChoose.bind(this),
			//	placeNear: e.target,
				toggleCmd: x.toString(),
	            items:  items, 
			});
		} catch (e) {
			Mojo.Log.error ("mojoLevelSelection(): Exception: " + e);
			this.showDialogBox ($L("Error"), "mojoLevelSelection(): Exception: " + e);

		}


    },
	
    // deal with the choice made by the user in the popup menu:
	popupMojoLevelChoose: function(value){
		try {
			var saveMojoLevelName = mojoLevelSelected;
			var saveMojoLogLevel = 0;
			var saveApplSelected = mojoApplSelected;
			var row = this.findMojoApplRow();
			if (row >= 0) {
				mojoLogLevels[row].style = defaultStyle;
				this.controller.modelChanged(this.mojoListModel);
			}
			if (value != undefined) {
		        var newName = null;
				var newLogLevel = 0;
				var i;
				var that = this;
		
				//this.showDialogBox ("", value);
				i = +(value);
		 		newName = mojoLevelNames[i].name;
				if (!value || (!i && value != '0') || i < 0 || row < 0 || i >= mojoLevelNames.length
						|| newName == mojoLevelSelected) {
					return; // do nothing
				}
				newLogLevel = mojoLevelNames[i].logLevel;
				
				saveMojoLogLevel = mojoLogLevels[row].logLevel;
				mojoLogLevels[row].logLevel = newLogLevel;
				mojoLogLevels[row].level = newName;
				this.controller.modelChanged(this.mojoListModel);
				
				// set log levels level
				var saveRequest = this.controller.serviceRequest(NEW_SERVICE_NAME, {
					method: 'setmojologlevel',
					parameters: {
						'appl':  mojoApplSelected,
						'logLevel': newLogLevel
					},
					onSuccess: function (result) {
						that.controller.cancelServiceRequest(saveRequest);
						that.addToChangeList (mojoApplSelected, saveMojoLogLevel.toString(), MOJO_NAME);
						that.getMojoLogLevels(); // refresh
						var j = that.findContextRow ('LunaSysMgrJS');
						if (newName != 'error' && bVerifyJSLevels == false && j >= 0) {
							Mojo.Log.info ("popupMojoLevelChoose(): Need to check JS debugging: " + contextLevels[j].level);
							if (contextLevels[j].level.toLowerCase() != 'debug') {
								bVerifyJSLevels = true;  // only show once per session!
								that.showDialogBox ($L("Information"), "To get detailed logging from Mojo applications you should set log level for \'LunaSysMgrJS\' to \'debug\'");
							}
						} else {
							Mojo.Log.info ("popupMojoLevelChoose(): No need to check JS debugging: " + newName + "; j:" + j);
							
						}
					},
					onFailure: function(result) {
						row = that.findMojoApplRow(mojoApplSelected);
						mojoLogLevels[row].logLevel = saveMojoLogLevel;
						mojoLogLevels[row].level = saveMojoLevelName;
						that.controller.modelChanged(that.mojoListModel);
						that.showDialogBox ($L("Error"), "Couldn\'t change log level. <br>" +(!result.errorText ? "" : result.errorText) + "<br>" + mojoApplSelected + " to #" + newLogLevel);
					}
				});
			} else {
				Mojo.Log.info ("popupMojoLevelChoose(): popup menu dismissed without change");
			}
		} catch (e) {
			Mojo.Log.error ("popupMojoLevelChoose(): Exception: " + e);
			this.showDialogBox ($L("Error"), "popupMojoLevelChoose(): Exception: " + e);

		}

	},

	////////////////////////////////////////////////////////////////////////
	//   Following set of methods relate to Java module log levels
	////////////////////////////////////////////////////////////////////////
	
	getJavaLogLevels: function () {
		if (inSimulator == true) {javaLogLevels = testJavaLogLevels;}

		var that = this;
		// get possible service levels
		that.javaListModel.items = [{
								'name': buildingJavaListName,
								'level': "",
								'levelStyle': originalStyle,
								'style': defaultStyle
							}];
		javaLogLevels = [];
		loadingJavaLogLevels = true;
		this.controller.modelChanged(this.javaListModel);
		this.activateSpinner ("Read current<br>logging levels");
		Mojo.Log.info ("getJavaLogLevels(): About to get list of Java services");

		var saveRequest = this.controller.serviceRequest(NEW_SERVICE_NAME, {
			method: 'getlistjavaservices',
			parameters: {
			},
			onSuccess: function (result) {
				if (result.returnValue && result.returnValue === "true" && result.javaServices) {
					that.controller.cancelServiceRequest(saveRequest);
					Mojo.Log.info ("getJavaLogLevels(): " + result.javaServices.length + " Java services");
					for (var i = 0 ; i < result.javaServices.length ; i++) {
						if (result.javaServices[i].name && result.javaServices[i].name.length > 0) {
							javaServiceNames.push({
								'name': result.javaServices[i].name,
								'service': null
							});
						}
					}
					this.controller.modelChanged(this.javaListModel);
					//that.showDialogBox (null, javaServiceNames[0].name + "<br>" + javaServiceNames[1].name + "<br>" + javaServiceNames[2].name );
					this.getJavaEachLogLevel();
				}
				else {
				//	that.showDialogBox (null, "Problem from getlistjavaservices.<br><br>" +
				//		(!result.errorText ? "": result.errorText));
				}
			}.bind(this),
			onFailure: function(result) {
				console.log("failed");
				if (true === loadingJavaLogLevels) {
					that.javaListModel.items = [];
					loadingJavaLogLevels = false;
					Mojo.Log.error ("getJavaLogLevels(): Failed to get Java services list");
					that.controller.modelChanged(that.javaListModel);
					that.stopSpinner();
					that.showDialogBox (null, "Failure from getlistjavaservices.<br><br>" +
						(!result.errorText ? "": result.errorText));
				}
			}
		});

		if (inSimulator == true) {this.processJavaLogLevels();}
	},
	
	processJavaLogLevelRequestSuccess: function (result, i) {
		try {
			
			Mojo.Log.info ("processJavaLogLevelRequestSuccess(): Got loglevel for Java application: \'" + javaServiceNames[i].name + "\' " + Object.toJSON(result));

				javaLogLevels.push({
					'name': javaServiceNames[i].name,
					'level': result.level,
					'levelStyle': originalStyle,
					'style': defaultStyle
				});
				this.javaListModel.items.push({
					'name': javaServiceNames[i].name,
					'level': result.level,
					'levelStyle': originalStyle,
					'style': defaultStyle
				});
				this.controller.modelChanged(this.javaListModel);
		
			// handle countdown and possible end to the requests
			if (javaServiceNames[i].request != null) {
				this.controller.cancelServiceRequest(javaServiceNames[i].request);
				javaServiceNames[i].request = null;
			}
			this.javaServiceRequests --;
			if (this.javaServiceRequests == 0) { this.endJavaLogLevelRequests();}
		} catch (e) {
			Mojo.Log.error ("getJavaEachLogLevelRequestSuccess(): Error caught: " + e);
		}
	},

	processJavaLogLevelRequestFail: function (result, i) {
		try {
			if (true == loadingJavaLogLevels) {
				Mojo.Log.error ("processJavaLogLevelRequestFail(): Continuing after failed to get Java loglevel: " 
					+ (result && result.errorText ? result.errorText : javaServiceNames[i].name));
			} else {
				Mojo.Log.error ("processJavaLogLevelRequestFail(): Continuing after failed to get loglevel for Java application: " + Object.toJSON(result));
			}
			// handle countdown and possible end to the requests
			if (javaServiceNames[i].request != null) {
				this.controller.cancelServiceRequest(javaServiceNames[i].request);
				javaServiceNames[i].request = null;
			}
			this.javaServiceRequests --;
			if (this.javaServiceRequests == 0) { this.endJavaLogLevelRequests();}
		} catch (e) {
			Mojo.Log.error ("processJavaLogLevelRequestFail(): Error caught: " + e);
		}
	},
	
	endJavaLogLevelRequests: function () {
		if (null != this.javaServiceTimer) {clearTimeout(this.javaServiceTimer); this.javaServiceTimer = null; }
		if (true === loadingJavaLogLevels) {
			loadingJavaLogLevels = false;
		}
		while (javaServiceNames.length > 0) {javaServiceNames.pop();}
		this.processJavaLogLevels();
		
	},
	
	handleJavaServiceRequestTimeout: function () {
		this.javaServiceTimer = null;
		Mojo.Log.error ("handleJavaServiceRequestTimeout(): " + this.javaServiceRequests + " service requests unfulfilled!");
		if (true === loadingJavaLogLevels) {
			loadingJavaLogLevels = false;
		}
		this.processJavaLogLevels();		
	},

	callJavaServiceLevelRequest: function (i) {
		try {
			javaServiceNames[i].request = this.controller.serviceRequest(JAVA_SERVICE_BASE + javaServiceNames[i].name.toLowerCase(), {
					method: 'com/palm/logging/getLevel',
					parameters: {},
					onSuccess: function(result){
						this.processJavaLogLevelRequestSuccess(result, i);
					}.bind(this),
					onFailure: function(result){
						this.processJavaLogLevelRequestFail(result, i);
					}.bind(this)
			});
			
		} catch (e) {
			Mojo.Log.error ("callJavaServiceLevelRequest(): Error caught: " + e);
		}
		
	},

	getJavaEachLogLevel: function () {
		this.javaServiceRequests = 0;
		try {
			if (null != this.javaServiceTimer) {clearTimeout(this.javaServiceTimer); this.javaServiceTimer = null; }
			this.javaServiceTimer = setTimeout(this._HandleJavaServiceRequestTimeout, 12 * 1000); // typically takes under 5 secs to process all on a Castle device
			this.javaListModel.items[0].name = buildingJavaListName;
			this.controller.modelChanged(this.javaListModel);
			for (var i = 0 ; i < javaServiceNames.length ; i++) {
				if (javaServiceNames[i].name && javaServiceNames[i].name.length > 0) {
					Mojo.Log.info ("getJavaEachLogLevel(): Request loglevel for Java application: " + javaServiceNames[i].name);
					this.callJavaServiceLevelRequest (i);
					this.javaServiceRequests ++;
				}
			}
		} catch (e) {
			Mojo.Log.error ("getJavaEachLogLevel(): Error caught: " + e);
		}
		if (this.javaServiceRequests == 0) { this.endJavaLogLevelRequests();}			
	},
	
	processJavaLogLevels: function () {
		var that = this;
		javaLogLevels.sort(this.sortCallbackJavaLog);
		for (var i= 0 ; i < javaLogLevels.length ; i ++) {
			javaLogLevels[i].levelStyle = (this.findInChangeList(javaLogLevels[i].name, JAVA_NAME) === true
				 ? changedStyle : originalStyle);
		}
	
		this.javaListModel.items = javaLogLevels; 
		this.controller.modelChanged(this.javaListModel);
		this.stopSpinner();
		Mojo.Log.info ("processJavaLogLevels(): Got levels on " + javaLogLevels.length + " Java applications");
		
		if (profileStore.getProfileDescription(profileStore.getBaseProfileName()) == null) { // need to build and add a base profile
			Mojo.Log.info ("processJavaLogLevels(): about to save base profile");
			this.saveProfile (
				profileStore.getBaseProfileName(), 
				"[ Restore Base Profile ]",
				function () { // onsuccess, redisplay list
					that.profileListModel.items = profileStore.getProfileList(true);
					that.controller.modelChanged(that.profileListModel);
				});
		} else Mojo.Log.info ("processJavaLogLevels(): No need to save base profile");
	},
	

	sortCallbackJavaLog: function (a, b) {
		var aS = a.name.toLowerCase();
		var bS = b.name.toLowerCase();
		if (aS < bS) return -1;
		if (aS > bS) return 1;
		return 0;
	},
	
	handleJavaListTap: function (event) {
		if (event.item && event.item.name && event.item.level && 0 < event.item.level.length) {
			javaServiceSelected = event.item.name;
			javaLevelSelected = event.item.level;
			this.javaLevelSelection (event);
		}
	},
	
	// returns row id, or -1
	findJavaServiceRow: function (service) {
		if (!service) service = javaServiceSelected;
		for (var i = 0 ; i < javaLogLevels.length ; i++) {
			if (service === javaLogLevels[i].name) {
				return i;
			}
		}
		return -1;
	},

	   // listener/handler for a tap - popup menu handling:
    javaLevelSelection: function(e){
		try{
			var i, x;
			Mojo.Log.info ("javaLevelSelection(): prepare list of choices");
			var row = this.findJavaServiceRow();
			var items = [];
			for (i = 0, x = -1 ; i < javaLevelNames.length ; i++) {
				items.push ({
					label: javaLevelNames[i].name, 
					command: i.toString()
				});
				if (javaLevelNames[i].name === javaLevelSelected) {x = i;}
			}
	
			if (x == -1 && javaLevelSelected && 0 < javaLevelSelected.length) { // what happens if level not found?
				javaLevelNames.push({"name":javaLevelSelected});
				x = (javaLevelNames.length - 1);
				items.push ({
					label: javaLevelSelected, 
					command: x.toString()
				});
			}
	
			//this.showDialogBox (levelSelected, items.toString() + "<br> i=" + i.toString() + "<br> x=" + x.toString());
			if (0 == javaLevelNames.length || row < 0) return;
			javaLogLevels[row].style = selectedStyle;
			this.controller.modelChanged(this.javaListModel);
	
			Mojo.Log.info ("javaLevelSelection(): display submenu");
			this.controller.popupSubmenu({
				onChoose: this.popupJavaLevelChoose.bind(this),
				placeNear: e.target,
				toggleCmd: x.toString(),
	            items:  items, 
			});
		} catch (e) {
			Mojo.Log.error ("javaLevelSelection(): Exception: " + e);
			this.showDialogBox ($L("Error"), "javaLevelSelection(): Exception: " + e);
		}

    },
	
    // deal with the choice made by the user in the popup menu:
	popupJavaLevelChoose: function(value){
		try {
			var saveLevelName = javaLevelSelected;
			var saveServiceSelected = javaServiceSelected;
			var row = this.findJavaServiceRow();
			if (row >= 0) {
				javaLogLevels[row].style = defaultStyle;
				this.controller.modelChanged(this.javaListModel);
			}
			if (value != undefined) {
				var newName = null;
				var newLogLevel = 0;
				var i;
				var that = this;
				
				//this.showDialogBox ("", value);
				i = +(value);
				newName = javaLevelNames[i].name;
				if (!value || (!i && value != '0') || i < 0 || row < 0 || i >= javaLevelNames.length ||
				newName == javaLevelSelected) {
					return; // do nothing
				}
				
				javaLogLevels[row].level = newName;
				this.controller.modelChanged(this.javaListModel);
				
				// set log levels level
				var saveRequest = this.controller.serviceRequest(JAVA_SERVICE_BASE + saveServiceSelected.toLowerCase(), {
					method: 'com/palm/logging/setLevel',
					parameters: {
						'level': newName
					},
					onSuccess: function(result){
						that.controller.cancelServiceRequest(saveRequest);
						row = that.findJavaServiceRow(saveServiceSelected);
						javaLogLevels[row].levelStyle = changedStyle;
						that.addToChangeList(saveServiceSelected, saveLevelName, JAVA_NAME);
						that.controller.modelChanged(that.javaListModel); // refresh display
					},
					onFailure: function(result){
						that.controller.cancelServiceRequest(saveRequest);
						row = that.findJavaServiceRow(saveServiceSelected);
						javaLogLevels[row].level = saveLevelName;
						that.controller.modelChanged(that.javaListModel);
						that.showDialogBox($L("Error"), "Couldn\'t change log level. <br>" + (!result.errorText ? "" : result.errorText) + "<br>\"com.palm." + saveServiceSelected + "\" to level \"" + newName + "\"");
					}
				});
			} else {
				Mojo.Log.error("popupJavaLevelChoose(): Menu ignored");
			}
		} catch (e) {
			Mojo.Log.error ("popupJavaLevelChoose(): Exception: " + e);
			this.showDialogBox ($L("Error"), "popupJavaLevelChoose(): Exception: " + e);
		}

	},
	
	////////////////////////////////////////////////////////////////////////
	//   Following set of methods support get/set of custom log levels
	//   Currently supported are RDXD and telephony
	////////////////////////////////////////////////////////////////////////
	getCustomLogLevels: function () {
		this.getCustomRdxdLevel();  // start the string of custom appl gets

	},

	getCustomRdxdLevel: function () {

		var that = this;
		var saveRequest = this.controller.serviceRequest(JAVA_SERVICE_BASE + rdxdApplName, {
			method: 'getautoupload',
			parameters: {
			},
			onSuccess: function (result) {
				that.controller.cancelServiceRequest(saveRequest);
				if (result.AutoUpload != null) {
					that.customListModel_rdxd.value = (result.AutoUpload === true || result.AutoUpload === "true" ? true : false);
					that.customListModel_rdxd.disabled = false;
					that.controller.modelChanged(that.customListModel_rdxd);
					that.getCustomTelephonyLevel(); // next in string
				}
				else {
					that.getCustomTelephonyLevel(); // next in string
					that.showDialogBox (null, "Problem from getCustomRdxdLevel.<br><br>" +
						(!result.errorText ? "": result.errorText));
				}
			},
			onFailure: function(result) {
				console.log("failed");
				that.controller.cancelServiceRequest(saveRequest);
				that.getCustomTelephonyLevel(); // next in string
			}
		});
		if (inSimulator == true) {
			this.customListModel_rdxd.disabled = false;
			this.controller.modelChanged(this.customListModel_rdxd);
		}
	},

	getCustomTelephonyLevel: function () {

		var that = this;
		var saveRequest = this.controller.serviceRequest(JAVA_SERVICE_BASE + telephonyApplName, {
			method: 'loggingQuery',
			parameters: {
			},
			onSuccess: function (result) {
				that.controller.cancelServiceRequest(saveRequest);
				if (result.extended.verbose != null) {
					that.customListModel_tele.value = (result.extended.verbose === true || result.extended.verbose === "true" ? true : false);
					that.customListModel_tele.disabled = false;
					that.controller.modelChanged(that.customListModel_tele);
					// End of string
					that.processCustomLogLevels(); // end of string
				}
				else {
					that.processCustomLogLevels(); // end of string
					that.showDialogBox (null, "Problem from getCustomTelephonyLevel.<br><br>" +
						result.returnValue + "; " + result.extended + "; " + result.extended.verbose);
				}
			},
			onFailure: function(result) {
				console.log("failed");
				that.controller.cancelServiceRequest(saveRequest);
				that.processCustomLogLevels(); // end of string
			}
		});
		if (inSimulator == true) {
			this.customListModel_tele.disabled = false;
			this.customListModel_tele.value = true;
			this.controller.modelChanged(this.customListModel_tele);
		}
	},
	
	processCustomLogLevels: function () {
		// No sort
		this.controller.get("rdxdButtonName").style.fontStyle =(this.findInChangeList(rdxdApplName, CUSTOM_NAME) === true
					? "italic" : "normal");
		this.controller.get("telephonyButtonName").style.fontStyle =(this.findInChangeList(telephonyApplName, CUSTOM_NAME) === true
					? "italic" : "normal");
		if (this.customListModel_rdxd.disabled == false) {this.controller.get("rdxd_div").show(); }
		else {this.controller.get("rdxd_div").hide(); }
		if (this.customListModel_tele.disabled == false) {this.controller.get("telephony_div").show(); }
		else {
			this.controller.get("telephony_div").hide();
			if (this.customListModel_rdxd.disabled == true) {this.controller.get("customLogList").hide();}	
		}
		
	},

	handleRdxdButtonUpdate: function (valueP) {  // called when rdxdButton tap'ed
		var newLevel = this.customListModel_rdxd.value;
		var that = this;
		// set log levels level
		var saveRequest = this.controller.serviceRequest(JAVA_SERVICE_BASE + rdxdApplName, {
			method:  'setautoupload',
			parameters: {
				'AutoUpload':(newLevel === true ? 'true' : 'false')
			},
			onSuccess: function(result) {
				that.controller.cancelServiceRequest(saveRequest);
				that.controller.get("rdxdButtonName").style.fontStyle = "italic";
				that.addToChangeList(rdxdApplName, (newLevel == true ? "false" : "true"), CUSTOM_NAME);
			},
			onFailure: function(result){
				that.customListModel_rdxd.value = (newLevel == true ? false : true);
				that.controller.modelChanged(that.customListModel_rdxd);
				that.showDialogBox($L("Error"), "Couldn\'t make change!<br><br>" +
					result.errorText);
			}
		});

	},

	handleTelephonyButtonUpdate: function (valueP) { // called when telephonyButton tap'ed
		var that = this;
		var newLevel = this.customListModel_tele.value;
		// set log levels level
		var saveRequest = this.controller.serviceRequest(JAVA_SERVICE_BASE + telephonyApplName, {
			method:  'loggingSet',
			parameters: {
				"verbose":newLevel
			},
			onSuccess: function(result) {
				that.controller.cancelServiceRequest(saveRequest);
				that.controller.get("telephonyButtonName").style.fontStyle = "italic";
				that.addToChangeList(telephonyApplName, (newLevel == true ? false : true), CUSTOM_NAME);
			},
			onFailure: function(result){
				that.customListModel_tele.value = (newLevel == true ? false : true);
				that.controller.modelChanged(that.customListModel_tele);
				that.showDialogBox($L("Error"), "Couldn\'t make change!<br><br>" 
						+ result.errorText + "; #" + result.errorCode);			
			}
		});
	},


	////////////////////////////////////////////////////////////////////////
	//   Following set of methods relate to undo/reset log levels, supporting all types
	////////////////////////////////////////////////////////////////////////
	
	// returns true/false
	findInChangeList: function (name, type) {
		if (!name) return false;
		for (var i = 0 ; i < changeList.length ; i++) {
			if ((!type || type === changeList[i].type) 
				&& (name === changeList[i].name) ) return true;
		}
		return false;
	},
	
	// returns 0 if not added or new count of changeList elements
	// note: all three parameters required and must be valid!
	addToChangeList: function (name, level, type) {
		changeList.push ({
			'name': name,
			'oldLevel': level,
			'type': type
		});
//		that.showDialogBox(null, "Change added to undo-list<br><br>" 
//						+ name + "; " + level + "; " + type);
		return changeList.length;
	},

	// recursive function to reset all changes
	// count can be null for all, or a positive value of maximum to change 
	resetChanges: function (count, isReset) {
		var that = this;
		if (isReset == null) {isReset = true;}
		if ((isReset == true && 0 == changeList.length)
				|| (isReset != true && 0 == profileUpdateList.length)
				|| (null != count && count <= 0) ) {
			resetInProcess = false; // finished
			Mojo.Log.info("resetChanges(): posting completed; mode: " + (isReset == true ? "undo/reset" : "profile change"));
			this.stopSpinner ();
			this.getContextLevels();
			this.getMojoLogLevels();
			this.getCustomLogLevels();
			this.processJavaLogLevels(); // refresh display
			if (null == count) {
				this.showDialogBox (null, 
					(isReset == true ? "Reset of changes" : "Profile update") + " completed!"
					+ (isReset == true || profileApplyErrorCount <= 0 ? ""
						: "<br><br>With " + profileApplyErrorCount + " errors."));
			}
		}
		else {
			var param;
			var methodName;
			var actionName = NEW_SERVICE_NAME;
			var row = -1;
			var changeItem = (isReset == true ? changeList.pop() : profileUpdateList.pop());
			Mojo.Log.info("resetChanges(): Process item: " + Object.toJSON(changeItem));

			if (row < 0 && (MOJO_NAME === changeItem.type) ) {
				row = this.findMojoApplRow(changeItem.name);
				if (row >= 0) {
					methodName = 'setmojologlevel';
					params = {
						'appl':  changeItem.name,
						'logLevel': changeItem.oldLevel
					};
				}
			}
			
			if (row < 0 && (PMLOG_NAME === changeItem.type) ) {
				row = this.findContextRow(changeItem.name);
				if (row >= 0) {
					methodName = 'setloglevel';
					params = {
						'context': changeItem.name,
						'level': changeItem.oldLevel
					};
				}
			}
			
			if (row < 0 && (JAVA_NAME === changeItem.type) ) {
				row = this.findJavaServiceRow(changeItem.name);
				if (row >= 0) {
					actionName = JAVA_SERVICE_BASE + changeItem.name.toLowerCase();
					methodName = 'com/palm/logging/setLevel';
					params = {
						'level': changeItem.oldLevel.toString()
					};
				}
			}
			
			if (row < 0 && (CUSTOM_NAME === changeItem.type) ) {
				actionName = JAVA_SERVICE_BASE + changeItem.name;  // works for both rdxd and telephony
				if (changeItem.name == telephonyApplName) {
					methodName = 'loggingSet';
					params = {
						"verbose": changeItem.oldLevel
					};
					row = 1;
				} else if (changeItem.name == rdxdApplName) {
					methodName = 'setautoupload';
					params = {
						"AutoUpload": changeItem.oldLevel
					};
					row = 0;
				}
			}
			
			if (row < 0) {
				resetInProcess = false; // finished
				Mojo.Log.error("resetChanges(): Could not reset item: " + Object.toJSON(changeItem));
				this.stopSpinner ();
				this.getContextLevels();
				this.getMojoLogLevels();
				this.processJavaLogLevels(); // refresh display
				this.getCustomLogLevels();
				this.showDialogBox($L("Error"), "Couldn\'t apply change for \"" + changeItem.name + "\"!");
			}
			else {
				// set log levels level
				var saveRequest = this.controller.serviceRequest(actionName, {
					method: methodName,
					parameters: params,
					onSuccess: function(result) {
						that.controller.cancelServiceRequest(saveRequest);
						if (JAVA_NAME === changeItem.type) {
							javaLogLevels[row].level = changeItem.oldLevel; // correct now!
						}
						changeItem = null;
						that.resetChanges(null == count ? null : count - 1, isReset);
					},
					onFailure: function(result){
						that.controller.cancelServiceRequest(saveRequest);
						if (resetInProcess === true) {
							var msg = (result && result.errorText ? result.errorText : methodName + " {" + params + "}")
							Mojo.Log.error("resetChanges(): Failed " + (isReset == true ? "reset" : "change") + " item; Error: " + msg);
							if (isReset != true) { // keep trying to apply profile change
								changeItem = null;
								profileApplyErrorCount ++;
								that.resetChanges(null == count ? null : count - 1, isReset);
							} else {
								resetInProcess = false; // finished
								that.stopSpinner ();
								that.getContextLevels();
								that.getMojoLogLevels();
								if (changeItem) {
									changeList.push(changeItem); // add back to change list
									changeItem = null;
								}
								that.processJavaLogLevels(); // refresh display
								that.getCustomLogLevels();
								that.showDialogBox($L("Error"), "Couldn\'t apply change!<br><br>" + msg);
							}
						}
					}
				});
			}
		}
	},

	////////////////////////////////////////////////////////////////////////
	//   Profile maintenance
	////////////////////////////////////////////////////////////////////////
	
	// saves current profile
	saveProfile: function (name, description, onSuccess) {
		var profile = [];
		var i;
		Mojo.Log.info("saveProfile(): Name: " + name + "; Desc: " + description);
		for (i= 0 ; i < javaLogLevels.length ; i ++) {
			profile.push( {'name':  javaLogLevels[i].name, 
					'level': javaLogLevels[i].level, 
					'type':  JAVA_NAME} );
		}
		Mojo.Log.info("saveProfile(): Java items = " + i);
		for (var i= 0 ; i < mojoLogLevels.length ; i ++) {
			profile.push( {'name':  mojoLogLevels[i].appl, 
					'level': mojoLogLevels[i].logLevel, 
					'type':  MOJO_NAME} );
		}
		Mojo.Log.info("saveProfile(): Mojo items = " + i);
		for (var i = 0 ; i < contextLevels.length ; i++) {
			profile.push( {'name':  contextLevels[i].context, 
					'level': contextLevels[i].level, 
					'type':  PMLOG_NAME} );
		}
		Mojo.Log.info("saveProfile(): PmLog items = " + i);
		if (profile.length > 10) { // ensure there are some...
			Mojo.Log.info("saveProfile(): Total items = " + profile.length);
			profileStore.updateProfile(name, description, profile, onSuccess);
		}

	},
	
	// any application with log level more detailed than error, change to error (or equivalent)
	minimalProfile: function () {
		var i;
		var count;
		profileUpdateList = [];
		
		Mojo.Log.info("minimalProfile(): Set Mojo levels");
		for (i = 0, count = 0 ; i < mojoLogLevels.length; i++) {
			if (mojoLogLevels[i].logLevel > 0) {
				profileUpdateList.push ({
					'name': mojoLogLevels[i].appl,
					'oldLevel': "0",
					'type': MOJO_NAME
				});
				count++;
			}
		}
		Mojo.Log.info("minimalProfile(): Set PmLog levels; " + count + " Mojo changes");
		
		for (i = 0, count = 0 ; i < contextLevels.length; i++) {
			if (contextLevels[i].level != "none" 
					&& contextLevels[i].level != "emerg" 
					&& contextLevels[i].level != "alert" 
					&& contextLevels[i].level != "crit" 
					&& contextLevels[i].level != "err" ) {
				profileUpdateList.push ({
					'name': contextLevels[i].context,
					'oldLevel': "err",
					'type': PMLOG_NAME
				});
				count++;		
			}
		}
		Mojo.Log.info("minimalProfile(): Set Java levels; " + count + " PmLog changes; ");
		
		for (i = 0, count = 0 ; i < javaLogLevels.length; i++) {
			if (javaLogLevels[i].levelStyle != "SEVERE") {
				profileUpdateList.push ({
					'name': javaLogLevels[i].name,
					'oldLevel': "SEVERE",
					'type': JAVA_NAME
				});
				count++;
			}
		}
		Mojo.Log.info("minimalProfile(): " + count + " Java changes; " + profileUpdateList.length + " total changes to be applied");

		if (profileUpdateList.length > 0)  {
			//this.showDialogBox("Changes", Object.toJSON(profileUpdateList));
			if (profileUpdateList.length > 10 || count > 1) { // more than one java change, show spinner
				this.activateSpinner ("Setting minimal<br>logging levels");
			}
			profileApplyErrorCount = 0;
			resetInProcess = true;
			this.resetChanges (null, false);
		} else {
			this.showDialogBox(null, "Log levels are already at minimum levels.");
		}

	},
	
	// restores selected profile (only changes items which are different)
	restoreProfile: function (profile) {
		var i;
		var javaCount = 0;
		if (!profile){
			Mojo.Log.error ("restoreProfile(): null parameter");
			return;
		}
		profileUpdateList = [];
		Mojo.Log.info("restoreProfile(): Count: " + profile.length);

		for (i = 0; i < profile.length; i++) {
			var row;
			if (profile[i].type == JAVA_NAME) {
				row = this.findJavaServiceRow(profile[i].name);
				Mojo.Log.info ("restoreProfile(): Java name: " + profile[i].name 
					+ (row >= 0 ? " at #" + row : " not found!"));
				if (row >= 0 && javaLogLevels[row].level != profile[i].level) {
					profileUpdateList.push ({
						'name': profile[i].name,
						'oldLevel': profile[i].level,
						'type': JAVA_NAME
					});
					javaCount++;
				}
				
			} else if (profile[i].type == MOJO_NAME) {
				row = this.findMojoApplRow(profile[i].name);
				Mojo.Log.info ("restoreProfile(): Mojo name: " + profile[i].name 
					+ (row >= 0 ? " at #" + row : " not found!"));
				if (row >= 0 && (+(mojoLogLevels[row].logLevel)) != (+(profile[i].level))) {
					profileUpdateList.push ({
						'name': profile[i].name,
						'oldLevel': profile[i].level.toString(),
						'type': MOJO_NAME
					});
				}
			} else if (profile[i].type == PMLOG_NAME) {
				row = this.findContextRow(profile[i].name);
				Mojo.Log.info ("restoreProfile(): PmLog name: " + profile[i].name 
					+ (row >= 0 ? " at #" + row : " not found!"));
				if (row >= 0 && contextLevels[row].level != profile[i].level) {
					profileUpdateList.push ({
						'name': profile[i].name,
						'oldLevel': profile[i].level,
						'type': PMLOG_NAME
					});
				}
			} else {
				Mojo.Log.error ("restoreProfile(): unrecognized type: " + profile[i].type + " for name " + profile[i].name);
			}
		}
		if (profileUpdateList.length > 0)  {
			//this.showDialogBox("Changes", Object.toJSON(profileUpdateList));
			if (profileUpdateList.length > 10 || javaCount > 1) { // more than one java change, show spinner
				this.activateSpinner ("Set log levels<br>from profile");
			}

			Mojo.Log.info("restoreProfile(): Make " + profileUpdateList.length + " changes; Java count:" + javaCount);
			Mojo.Log.info("restoreProfile(): " + Object.toJSON(profileUpdateList));

			profileApplyErrorCount = 0;
			resetInProcess = true;
			this.resetChanges (null, false);
		} else {
			this.showDialogBox(null, "Log levels are already at desired levels.");
		}
			
	},
	
	handleProfileListTap: function (event) {
		if (event.item && event.item.name) {
			Mojo.Log.info("handleProfileListTap(): Name: " + event.item.name);
			if (false == resetInProcess) {
				var name = event.item.name;
				var that = this;
				this.controller.showAlertDialog({
					title: $L("Confirmation"),
					message: $L("<b>Do you want to change all log levels to this profile?</b> This can't be undone.<br><br>" + event.item.desc),
					allowHTMLMessage: true,
					choices: [{label: $L('OK'), value: 'ok',type: 'affirmative'},
							{label: $L('Cancel'), value: 'cancel', type: 'negative'}],
					onChoose: function(value) {
						if (value === 'ok') {
							var bRet = profileStore.getProfileDetails(
								name, 
								function(details) { // onSuccess
									Mojo.Log.info("handleProfileListTap(): " + Object.toJSON(details));
									that.restoreProfile (details);
								}, 
								function(result){ // onFailure
									that.showDialogBox (null, "<b>Couldn't use profile.<b><br><br>"
										+ (!result || !result.errortText ? "" : result.errorText));
								}
							);
							if (bRet == false) {that.showDialogBox (null, "<b>Couldn't use profile.<b>");}
						}
					}
				});	
			} else {Mojo.Log.info ("handleProfileListTap(): Tap ignored");}

		} else Mojo.Log.info("handleProfileListTap(): " + Object.toJSON (event));
	},
	
	handleProfileListDelete: function (event) {
		if (event.item && event.item.name) {
			Mojo.Log.info("handleProfileListDelete(): Name: " + event.item.name);
			profileStore.deleteProfile (event.item.name);
		} else Mojo.Log.info("handleProfileListDelete(): " + Object.toJSON (event));
	},
	

	
	////////////////////////////////////////////////////////////////////////
	//   Handle import and export of profiles
	////////////////////////////////////////////////////////////////////////
	
	fetchProfileFile: function() {
		var importParams = {
			fullPath: null,
			onSuccess: function (){
				Mojo.Log.info("fetchProfileFile(): Import completed ");
				this.profileListModel.items = profileStore.getProfileList(false);
				this.controller.modelChanged(this.profileListModel);
			}.bind(this),
			onFailure: function (msg) {
				Mojo.Log.info("fetchProfileFile(): Import failed: " + msg);
				if (msg != null) this.showDialogBox( $L('Error'), msg);
			}.bind(this),
			
		};

		var pickParams = {
			actionType: 'select', //'open',
			kinds: ['file'],
			defaultKind: 'file',
			extensions: ['json'],
			onSelect: function(file){
				Mojo.Log.info("fetchProfileFile(): Selected: " + Object.toJSON(file));
				importParams.fullPath = this.sanitizeSystemPath(file.fullPath);
				Mojo.Log.info("fetchProfileFile(): About to call import: " + Object.toJSON(importParams));
				profileStore.importProfile(importParams);
			}.bind(this),
			onCancel: function(){
				Mojo.Log.info("fetchProfileFile(): Pick cancelled ");
			}.bind(this),
		};
		Mojo.FilePicker.pickFile(pickParams, this.controller.stageController);

	},
	
	exportProfileFile: function () {
		var list = profileStore.getProfileList(false);
		if (list == null || list.length == 0) {
			this.showDialogBox(null, $L("You haven't created any custom profiles to save!"));
			Mojo.Log.info("exportProfileFile(): Nothing to export");
		} else {
			Mojo.Controller.stageController.pushScene('pickprofile');
		}
	},

	////////////////////////////////////////////////////////////////////////
	//   Utility methods
	////////////////////////////////////////////////////////////////////////
	
	sanitizeSystemPath: function(systemPath){
		var newPath = systemPath.replace(/^\s*/, '').replace(/\s*$/, '');
		if (newPath.indexOf("file://") == 0) 
			newPath = newPath.substring(7);
		Mojo.Log.info("sanitizeSystemPath(): New: " + newPath + "; Old: " + systemPath);
		return newPath;
	},
	
	// any parameter can be null or omitted, but very silly not to have 'message'
	showDialogBox: function (title, message, func) {
		this.controller.showAlertDialog({
			onChoose: function(value){
				if (func) func(value);
			},
			title: (title && title.length > 0 ? title : $L("Information")),
			message: (message ? message : ''),
			allowHTMLMessage: true,
			choices: [{label: $L('OK'),	value: 'ok', type: 'dismiss'}]
		});
	},
	
	activateSpinner: function (desc) {
		$('level_progress_label').innerHTML = (!desc || desc.length == 0 ? "Processing..." : desc);
		$('levelProfileBody').style.display = "none";
		$('spinner_div').style.display = "block";
		this.spinnerModel.spinning = true;
		this.controller.modelChanged(this.spinnerModel);
		this.controller.sceneScroller.mojo.revealElement('spinner_div');
	},
	
	stopSpinner: function () {
		$('spinner_div').style.display = "none";
		this.spinnerModel.spinning = false;
		this.controller.modelChanged(this.spinnerModel);
		$('levelProfileBody').style.display = "block";
		
	},
	
	////////////////////////////////////////////////////////////////////////
	//   class variables
	////////////////////////////////////////////////////////////////////////
	
	systemListModel: {
		'listTitle': $L("System Log Levels"),
		//'items': [], // establish a default value
		'items': [{"context":"&lt;global&gt;", "level":"debug"}], // establish a default value
	},
		
	systemListAttributes: {
		listTemplate: 'level/item-header', // container or wrapper for the list
		itemTemplate: 'level/item-details', // definition of each item in the list
		fixedHeightItems: false,
	},	
	
	mojoListModel: {
		'listTitle': $L("Mojo Application Log Levels"),
		'items': [], // establish a default value
	},

	mojoListAttributes: {
		listTemplate: 'level/mojo-header', // container or wrapper for the list
		itemTemplate: 'level/mojo-details', // definition of each item in the list
		fixedHeightItems: false,
	},	

	javaListModel: {
		'listTitle': $L("Java Module Log Levels"),
		'items': [], // establish a default value
	},

	javaListAttributes: {
		listTemplate: 'level/java-header', // container or wrapper for the list
		itemTemplate: 'level/java-details', // definition of each item in the list
		fixedHeightItems: false,
	},	
		
	customListModel_rdxd: {"value": false, "disabled": true}, // RDXD
	
	customListModel_tele: {"value": false, "disabled": true}, // Telephony

	customListAttributes: {		// RDXD and Telephony
		trueLabel: $L("Yes"),
		falseLabel: $L("No"),
	},
	
	spinnerAttributes: {
		spinnerSize: 'large',
		frameHeight:  '100px',
	}, 

	spinnerModel: {
		spinning: false
	},
	
	//Create the attributes for the textfield
	descAttributes: {
		hintText: 'Profile description',
		focus: true,
		enterSubmits: true,
		multiline: true,
		modelProperty: 'originalValue',
		modifierState: Mojo.Widget.sentenceCase,
		focusMode: Mojo.Widget.focusInsertMode,
		requiresEnterKey: true,
	},
	
	//Create the model for the text field
	descModel: {
		'originalValue' : ''
	},

	profileListModel: {
		'listTitle': $L("Select Log Profile"),
		'items': [], // establish a default value
	},
		
	profileListAttributes: {
		listTemplate: 'profile/profile-header', // container or wrapper for the list
		itemTemplate: 'profile/profile-details', // definition of each item in the list
		//swipeToDelete: false,
		swipeToDelete: true,
		autoconfirmDelete: false,
		fixedHeightItems: false,
	},	



});
