Ext.namespace("LP");



/**
 * Application class.  This is the core application object that provides much of the common functionality.
 * @class LP.Application
 * @extends Ext.util.Observable
 */
LP.Application = Ext.extend(Ext.util.Observable, {



	// private data
	registry : null,
	isLoaded : false,
	fileDownloadFrameId: Ext.id(),



	/*
	 * Custom events
	 */
	events : {
		/**
		 * @event
		 * @type Boolean
		 */
		ready : true
	},



	/**
	 * Add a listener to the application.
	 * 
	 * @param {String} eventName The event to listen to.
	 * @param {Function} fn The callback function.
	 * @param {Object} scope The scope to use for the callback.
	 * @param {Object} options Additional options for the listener (@see Ext.util.Observable#addListener).
	 */
	addListener : function(eventName, fn, scope, options) {
		if ((eventName == 'ready') && this.isLoaded) {
			fn.createDelegate(scope)();
		} else {
			LP.Application.superclass.addListener.call(this, eventName, fn, scope, options);
		}
	},



	/**
	 * Initialize the application.
	 */
	init : function() {
		Ext.QuickTips.init();
		Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
		if (LP.Registry) {
			this.registry = new LP.Registry({
				scope : this,
				success : function() {
					this.isLoaded = true;
				},
				failure : function() {
					this.isLoaded = true;
					Ext.Msg.alert("Failed", "Failed to load registry.");
				}
			});
			this.waitLoad();
		} else {
			this.isLoaded = true;
			this.fireEvent('ready');
			this.clearSplash();
		}
	},



	/**
	 * Set the current application session ID (as received from the server).
	 * 
	 * @param {String} sessionID The session ID.
	 */
	setSessionID : function(sessionID) {
		Ext.state.Manager.set("session_id", sessionID);
	},



	/**
	 * Get the current application session ID.
	 * 
	 * @return {String} The application session ID.
	 */
	getSessionID : function() {
		return Ext.state.Manager.get("session_id");
	},



	getAccountID: function () {
		if ( this.registry && this.registry.account ) {
			return this.registry.account.account_id;
		}
		return null;
	},



	getAccountName: function () {
		if ( this.registry && this.registry.account ) {
			return this.registry.account.account_name;
		}
		return null;
	},



	/**
	 * Wait for the application to load up, retrying every 100ms.
	 */
	waitLoad : function() {
		if (!this.isLoaded) {
			this.waitLoad.defer(100, this);
		} else {
			this.clearSplash();
			this.fireEvent('ready');
		}
	},



	/**
	 * Clear the default loading splash that is shown when the page is loaded.
	 */
	clearSplash : function() {
		var el = Ext.get("loading");
		if (el) {
			el.fadeOut({
				duration : 0.5,
				remove : true
			});
		}
	},



	/**
	 * Set a run-time application value.  For example, the current client id.
	 * 
	 * @param {Object} config Mapping of name/value pairs for application values to set.
	 */
	setApplicationValue : function(config) {
		config = Ext.apply({
			params : {
				name : config.name,
				value : config.value
			}
		}, config);
		this.sendServiceRequest(LP.Application.SERVICE_ACTIONS.SET, config);
	},



	/**
	 * Log the user out.  Send the request asynchronously to the server and redirect to the login page immediately.
	 */
	logout : function() {
		this.sendServiceRequest( LP.Application.SERVICE_ACTIONS.LOGOUT );
		window.location = LP.Application.LOGIN_PAGE;
	},



	/**
	 * Send an Ajax request to the server.  Inserts common arguments for requests and adds some
	 * basic exception trapping.
	 * 
	 * TODO: add validation to the actionRequest argument
	 * 
	 * @param {LP.Application.SERVICE_ACTIONS} actionRequest The service action to request.
	 * @param {Object} config Configuration object for the Ajax request request (@see Ext.Ajax#request).
	 */
	sendServiceRequest : function( actionRequest, config ) {

		if (!actionRequest)
			return;

		config = config || {};

		var callbackSuccess = config.success;
		var callbackFailure = config.failure;
		var callbackScope = config.scope;
		var params = Ext.apply({
			a : actionRequest,
			s : LP.app.getSessionID()
		}, config.params);

		Ext.Ajax.request({

			url : LP.Application.SERVICE_URL,

			success : function ( response, opts ) {
				var responseObject = null;
				try {
					responseObject = Ext.util.JSON.decode(response.responseText);
				} catch (err) {
					if (callbackFailure) {
						callbackFailure.call(callbackScope, responseObject);
					}
				}
				if (responseObject.success) {
					if (callbackSuccess) callbackSuccess.call(callbackScope, responseObject);
				} else {
					LP.app.clearSplash();
					if (responseObject.error_type == LP.Application.ERROR_TYPE.INVALID_SESSION) {
						Ext.MessageBox.alert("Error", "Invalid session.  Please re-login.", function () {
							window.location = LP.Application.LOGIN_PAGE;
						});
					} else if ( responseObject.error_type == LP.Application.ERROR_TYPE.SESSION_TIMEOUT ) {
						Ext.MessageBox.alert("Error", "Your session has timed out.  Please re-login.", function () {
							window.location = LP.Application.LOGIN_PAGE;
						});
					} else {
						if (callbackFailure) {
							callbackFailure.call(callbackScope, responseObject);
						} else if ( responseObject.msg ) {
							Ext.MessageBox.alert("Error", "Server error. Please contact the system admin for assistance.");
						}
					}
				}
			},

			failure : function ( response, opts ) {
				if (callbackFailure) {
					callbackFailure.call(callbackScope, null);
				}
			},

			params : params

		});

	},



	/**
	 * Download a file from the server given the file key.  When a report is requested from the server, the request is
	 * first sent and key is returned for the file.  The key is a random string that maps to the generated report
	 * file.  This can then be used here to download it.
	 * 
	 * @param {String} key The file key.
	 */
	downloadFile : function( key ) {

		var url = LP.Application.SERVICE_URL + '?a=df&s=' + LP.app.getSessionID()+'&key='+key;

		if ( Ext.isIE ) {
			var wnd = new Ext.Window({
				modal: true,
				width: 150,
				title: "Download Report",
				items: [
					new Ext.Button({
						text: "Download",
						style: 'margin: 10px auto 10px auto;',
						listeners: {
							scope: this,
							click: function () {
								window.open(url);
								wnd.close();
							}
						}
					})
				]
			});
			wnd.show();
		} else {
			var frame = Ext.get( this.fileDownloadFrameId );
			if ( !frame ) {
				frame = Ext.DomHelper.append(document.body, { 
					tag: 'iframe', 
					id: this.fileDownloadFrameId,
					frameBorder: 0, 
					width: 0, 
					height: 0, 
					css: 'display:none;visibility:hidden;height:1px;', 
					src: url
				});
			} else {
				frame.set({ src: url });
			}
		}

	},



	/**
	 * Check permissions for a tab (aka. context).
	 * 
	 * TODO: define the available contexts as has been done with SERVICE_ACTIONs
	 * 
	 * @param {String} context The name of the context (ex. 'data', 'admin', etc.).
	 * @return {Boolean} True if access is allowed.  Otherwise false.
	 */
	checkContextPermission: function ( context ) {

		var contextNode = null;

		// validate context node
		for (var i in LP.Application.CONTEXT ) {
			if ( context == LP.Application.CONTEXT[i].id ) {
				contextNode = LP.Application.CONTEXT[i];
				break;
			}
		}
		if ( !contextNode ) return false;

		var cntAllowed = 0;
		for ( var i =0; i < contextNode.nodes.length; i++ ) {
			var objectName = contextNode.nodes[i].object;
			if ( this.registry.getObjectPermission( objectName, LP.Application.PERMISSION_TYPE.EDIT ) ) {
				cntAllowed++;
			}
		}

		if ( cntAllowed > 0 ) {
			return true;
		}

		return false;

	},



	/**
	 * Check if the current user has access to perform an action on an object.
	 * 
	 * @param {String} objectName The name of the object.
	 * @param {LP.Application.PERMISSION_TYPE} permissionType The action to be performed.
	 * @return {Boolean} True if access allowed.  Otherwise false.
	 */
	checkObjectPermission: function ( objectName, permissionType ) {

		// validate permission type
		var permission = null;
		for ( var j in LP.Application.PERMISSION_TYPE ) {
			if ( permissionType == LP.Application.PERMISSION_TYPE[j] ) {
				permission = permissionType;
				break;
			}
		}
		if ( !permission ) {
			return false;
		}

		return this.registry.getObjectPermission( objectName, permissionType );

	},



	/**
	 * Get the combo type for a given object.
	 * 
	 * @param {Strung} objectType The name of the object.
	 * @return {String} The class name for the combo.
	 */
	getComboXType: function ( objectType ) {
		switch ( objectType ) {
			case "client":
				return "LP.combos.ClientCombo";
			case "facility":
				return "LP.combos.FacilityCombo";
			case "department":
				return "LP.combos.DepartmentCombo";
			case "shift":
				return "LP.combos.ShiftCombo";
			case "process":
				return "LP.combos.ProcessCombo";
			case "employee":
				return "LP.combos.EmployeeCombo";
			case "customer":
				return "LP.combos.CustomerCombo";
		}
		return null;
	},



	/**
	 * Get the grid xtype associated with a given objectType.
	 * @param {String} objectType
	 */
	getGridXType: function ( objectType ) {
		switch ( objectType ) {
			case 'client':
				return 'LP.grids.ClientListGrid';
			case 'facility':
				return 'LP.grids.FacilityListGrid';
			case 'department':
				return 'LP.grids.DepartmentListGrid';
			case 'shift':
				return 'LP.grids.ShiftListGrid';
			case 'process':
				return 'LP.grids.ProcessListGrid';
			case 'employee':
				return 'LP.grids.EmployeeListGrid';
			case 'scheduling':
				return 'LP.grids.SchedulingListGrid';
			case 'item':
				return 'LP.grids.ItemListGrid';
			case 'item_employee':
				return 'LP.grids.ItemEmployeeListGrid';
			case 'qls':
				return 'LP.grids.QLSListGrid';
			case 'qls_employee':
				return 'LP.grids.QLSEmployeeListGrid';
			case 'account':
				return 'LP.grids.UserListGrid';
			case 'client_account':
				return 'LP.grids.ClientAccountListGrid';
			case 'evaluation_category':
				return 'LP.grids.EvaluationCategoryListGrid';
			case 'emp_evaluation':
				return 'LP.grids.EmployeeEvaluationListGrid';
		}
		return null;
	}



});
LP.Application.prototype.on = LP.Application.prototype.addListener;



/*
 * Various global constants.
 */
Ext.apply(LP.Application, {

	SERVICE_URL : "lp",
	LOGIN_PAGE: "index.php",
	APPLICATION_PAGE: "application.php",

	SERVICE_ACTIONS : {
		LOGIN : "li",
		LOGOUT : "lo",
		SET : "t",
		GET_REGISTRY : "gr",
		LIST : "l",
		CHARTDATA: "cd",
		GET : "g",
		GETMETA : "gm",
		SAVE : "s",
		DELETE : "d",
		TREEDATA : "td",
		LIST_REPORTS: "lr",
		LIST_REPORT_PARAMETERS: "lrp",
		RUN_REPORT: "rr",
		DOWNLOAD_FILE: "df",
		QUERYPARAM: "qp",
		REPORTCONFIG: "rc"
	},

	PERMISSION_TYPE: {
		LIST: "list",
		ADD: "add",
		EDIT: "edit",
		DELETE: "delete",
		BLOCK: "block",
		UNBLOCK: "unblock"
	},

	CONTEXT: {

		DATA : {
			id : 'data',
			nodes : [{
				id : 'data_item',
				text : 'LMS Data Entry',
				object: 'item',
				leaf : true
			}, {
				id : 'data_qls',
				text : 'QLS Data Entry',
				object: 'qls',
				leaf : true
			}, {
				id : 'data_employee_shift',
				text : 'Employee Shift Records',
				leaf : true
			}, {
				id : 'data_scheduling',
				text : 'Scheduling',
				object: 'scheduling',
				leaf : true
			}, {
				id : 'data_employee_evaluation',
				text : 'Employee Evaluations',
				object: 'emp_evaluation',
				leaf : true
			}]
		},

		REPORTS : {
			id : 'reports',
			nodes : []
		},

/*		QUALITY_CONTROL : {
			id : 'qc',
			nodes : [{
				id : 'qc_startend',
				text : 'Start-End Hours QC',
				leaf : true
			}, {
				id : 'qc_incomplete_items',
				text : 'Incomplete Items QC',
				leaf : true
			}, {
				id : 'qc_employee_shift',
				text : 'Employee-Shift Hours QC',
				leaf : true
			}]
		}, */

		SETUP : {
			id : 'setup',
			nodes : [{
				id : 'setup_facility',
				text : 'Facilities',
				object: 'facility',
				leaf : true
			}, {
				id : 'setup_department',
				text : 'Departments',
				object: 'department',
				leaf : true
			}, {
				id : 'setup_shift',
				text : 'Shifts',
				object: 'shift',
				leaf : true
			}, {
				id : 'setup_process',
				text : 'Processes',
				object: 'process',
				leaf : true
			}, {
				id : 'setup_employee',
				text : 'Employees',
				object: 'employee',
				leaf : true
			}, {
				id : 'setup_evaluation_category',
				text : 'Evaluation Categories',
				object: 'evaluation_category',
				leaf : true
			}]
		},

		ADMINISTRATION : {
			id : 'admin',
			nodes : [{
				id : 'admin_user',
				text : 'Users',
				iconCls: 'icon-group',
				object: 'account',
				leaf : true
			}, {
				id : 'admin_client',
				text : 'Clients',
				object: 'client',
				leaf : true
			}, {
				id : 'admin_import',
				text : 'Import',
				iconCls : 'icon-import',
				leaf : true
			}]
		}

	},

	ERROR_TYPE : {
		INVALID_SESSION : 1,
		SESSION_TIMEOUT : 2,
		ACTION_UNSUPPORTED : 3,
		ACCESS_DENIED : 4,
		FOREIGN_KEY : 5
	},

	GRID_PAGESIZE: 25

});



/*
 * Core application instance.
 */
LP.app = new LP.Application();



/*
 * Override the default onReady and insert our own.
 */
Ext.coreOnReady = Ext.onReady;
Ext.onReady = LP.onReady = function(fn, scope, override) {
	LP.app.on('ready', fn, scope);
}
Ext.coreOnReady(function() {
	LP.app.init();
});
