// ------------------------------------------------
// DirCtrl.js
// Author:	jpotter@troupscreeksoftware.com
// Date:	Oct 27, 2006
// Desc:	Directory App logic flow controller
// Copyright 2006 Troups Creek Software LLC
// ------------------------------------------------
// Manages the TCS 'Directory' product application event-driven workflow. As
// program and user events are generated, this class determines the the current
// state of the application, and based on that state and what just occurred, 
// decides what should happen next.
// ------------------------------------------------
// GLOBAL states -----------------
var sNoConfig		= 0;	var sReady			= 6;
var sNoMap			= 1;	var sReadyAdmin		= 7;
var sNoData			= 2;	var sReadyEdit		= 8;
var sNoServices		= 3;	var sReadyEditCfg	= 9;
var sNoActions		= 4;	var sReadyAdd		= 10;
var sNoCurData		= 5;
// GLOBAL events -----------------
var eNone			= 0;	var eAdminClicked	= 6;	var eMapMoved		= 12;	var eAdminExited	= 18;
var eConfigOk		= 1;	var eDetailClicked	= 7;	var eMapZoomed		= 13;	var ePurgeOk		= 19;
var eMapOk			= 2;	var eEditClicked	= 8;	var eMapChanged		= 14;	var eEditConfig		= 20;
var eDataOk			= 3;	var eAddClicked		= 9;	var eLoadInProgress	= 15;	var eSave			= 21;
var eServicesOk		= 4;	var eFixLocClicked	= 10;	var eMaxMarkers		= 16;	var eCancel			= 22;
var eActionsOk		= 5;	var eCurListOk		= 11;	var eAdminGranted	= 17;	var eAddNewItem		= 23;
// ----------
var eNewItemOk		= 24;
var eEditItemOk		= 25;
var eCategoryChange = 26;
var eMapPopup		= 27;
var eAbort			= 28;
//
// DirectoryController::DirectoryController
function DirectoryController()
{
	// data -------------------
	this.state = sNoConfig;
	this.timeoutDelay = 10;		// timeout delay (in milliseconds)
	this.startDate = null;
	this.startUpdate = null;

	// -------------------------------------------------------------------------------------------
	//            B A S I C   A P P L I C A T I O N   B E H A V I O R
	// -------------------------------------------------------------------------------------------
	// Fundamental class methods.
	
	// -----------------------------------------
	// DirectoryController::triggerFlow
	this.triggerFlow = function(evt)
	{
		var tid = setTimeout("gApp.controller.update(" + evt + ")", this.timeoutDelay);
	}
	
	// -----------------------------------------
	// DirectoryController::update
	this.update = function(evt)
	{	// flow control / decision making function (i.e. state machine)
		switch(this.state)
		{
			// ---------------------------------------------------------------------------------------
			case sNoConfig:		// just started, we don't even have our configuration data yet
				if(evt == eNone)
				{	// we need to figure out what browser we're working with and
					// then request to retrieve our configuration data
					gMsg.displayMsg(0, "Reading configuration...");
					gApp.checkBrowser();
					gData.getConfig();		// returns immediately
				}
				else if(evt == eConfigOk)
				{	// we have the config data, so proceed to get our map set up
					this.state = sNoMap;
					this.triggerFlow(eNone);
				}
				break;
			// ---------------------------------------------------------------------------------------
			case sNoMap:		// we have started, but the map isn't up yet
				if(evt == eNone)
				{	// we need to initialize our map
					gMsg.displayMsg(0, "Initializing map...");
					if(gMap.init())
					{	// once we have the map set up, we can show our add-on map features
						gData.getMapFeatures(gConfig.domainid);
					}
				}
				else if(evt == eMapOk)
				{	// the map is set up, continue on
					this.state = sNoServices;
					this.triggerFlow(eNone);
				}
				break;
			// ---------------------------------------------------------------------------------------
			case sNoServices:	// we have started, but we don't have our web services yet
				if(evt == eNone)
				{	// initialize web services communication facility
					gMsg.displayMsg(0, "Initializing services...");
					gSvc.init();
				}
				else if(evt == eServicesOk)
				{	// service are set up, so proceed to prepare the UI
					this.state = sNoActions;
					this.triggerFlow(eNone);
				}
				break;
			// ---------------------------------------------------------------------------------------
			case sNoActions:	// we have started, but we don't have any UI showing yet
				if(evt == eNone)
				{	// we need to set up the UI action buttons
					gMsg.displayMsg(0, "Preparing UI...");
					gApp.setupUI();
					// if ads are enabled, start the ad rotator
					if(gConfig.ads == "true")
					{
						gAds.start();
					}
					// the UI is set up, continue on
					this.state = sNoData;
					this.triggerFlow(eNone);
				}
				break;
			// ---------------------------------------------------------------------------------------
			case sNoData:		// we don't have our app data yet or we need to refresh it
				if(evt == eNone || evt == ePurgeOk || evt == eNewItemOk || evt == eEditItemOk || evt == eCancel || evt == eAdminExited)
				{	// we need to read our application data
					gMsg.displayMsg(0, "Reading data...");
					gData.getItemList(gConfig.domainid);
				}
				else if(evt == eDataOk)
				{	// we have the application data, so proceed
					this.state = sNoCurData;
					this.triggerFlow(eDataOk);
				}
				break;
			// ---------------------------------------------------------------------------------------
			// P R I M A R Y   C A S E   F O R   C U R R E N T
			// ---------------------------------------------------------------------------------------
			case sNoCurData:	// we need to show our currently mapped data
				if((evt == eNone))
					{	// do nothing
					}
				else if((evt == eMapMoved) || (evt == eMapZoomed)) 
					{	// these are "ready" events...
						// we're here because a 2nd request has been issued before the 1st finished processing
						// so we are just going to ignore the second request
					}
				else if(evt == eDataOk)
				{	// first time in - show our all-inclusive list and initiate efforts to get the current data
					gMsg.displayMsg(0, "Creating current item list...");
					this.startDate = new Date();
					this.startUpdate = this.startDate.getTime();
					gApp.showItemList();
					gApp.setupCurrentList(false);	// don't clear the markers
				}
				else if(evt == eCurListOk)
				{	//  we're good to go -- display our data
					this.state = sReady;
					this.triggerFlow(eCurListOk);
				}
				else if(evt == eCategoryChange)
				{	// redo our lists
					gMsg.displayMsg(0, "Creating current item list...");
					this.startDate = new Date();
					this.startUpdate = this.startDate.getTime();
					gApp.showItemList();
					gApp.setupCurrentList(true);	// clear the markers
				}			
				else if(evt == eMaxMarkers)
				{	// too many markers
					gMsg.displayMsg(0, "Auto-adjusting map and current item list...");
					gMap.zoomIn(true);			// generates a user event which we will ignore
					gApp.setupCurrentList(true);	// clear the markers
				}
				else if(evt == eMapChanged)
				{	// map change detected in another state - act on it
					gMsg.displayMsg(0, "Updating current item list...");
					this.startDate = new Date();
					this.startUpdate = this.startDate.getTime();
					gApp.setupCurrentList(true);	// clear the markers
				}
				else if(evt == eMapPopup)
				{	// map moved when the popup displayed, but if we
					// issue an update, we may encounter the max markers condition
					// which will close the popup we just opened and fail to open it again...
					// so we are just going to go back to the "ready" state and wait until
					// the user does something else
					this.state = sReady;
					this.triggerFlow(eNone);
				}			
				else if(evt == eAdminGranted)
				{	// admin access granted - revamp the UI
					// NOTE: only happens if user moves map prior to password validation
					gApp.setupAdmin("ready");
				}
				break;
			// ---------------------------------------------------------------------------------------
			// P R I M A R Y   C A S E   F O R   R E A D Y
			// ---------------------------------------------------------------------------------------
			case sReady:		// we have started successfully and are ready to process user requests
				if((evt == eNone) || (evt == eMapChanged) || (evt == eCancel) || (evt == eSave))
				{	// do nothing
					gMsg.displayMsg(0, "Ready");
				}
				else if(evt == eCurListOk)
				{	// display our current data
					gApp.showCurrentList();
					// show how long it took to update the list
					var endDate = new Date();
					var endUpdate = endDate.getTime();
					gMsg.displayMsg(0, "Ready (" + ((endUpdate - this.startUpdate)/1000) + " seconds)");
				}
				else if(evt == eMapMoved)
				{	// map moved, we have to redo our current list
					this.state = sNoCurData;
					this.triggerFlow(eMapChanged);
				}
				else if(evt == eMapZoomed)
				{	// map zoomed, we have to redo our current list
					this.state = sNoCurData;
					this.triggerFlow(eMapChanged);
				}
				else if(evt == eMapPopup)
				{	// update the map after the popup opens and moves the map
					this.state = sNoCurData;
					this.triggerFlow(eMapPopup);
				}
				else if(evt == eCategoryChange)
				{	// listing category changed, redo the display
					this.state = sNoCurData;
					this.triggerFlow(eCategoryChange);
				}
				else if(evt == eAdminGranted)
				{	// admin access granted - revamp the UI
					gApp.setupAdmin("ready");
				}
				else if(evt == eAdminExited)
				{	// exited from admin mode - revamp the UI
					gApp.setupAdmin("exit");
				}
				else if(evt == ePurgeOk)
				{	// inactive items purged - invalidate the loaded data & force a reload & redisplay
					gMsg.displayMsg(1, "Items purged successfully.");
					gMap.removeAllMapMarkers();
					this.state = sNoData;
					this.triggerFlow(ePurgeOk);
				}
				else if(evt == eEditConfig)
				{	// admin function to change site setup/config
					this.state = sReadyEditCfg;
					this.triggerFlow(eNone);
				}
				else if(evt == eAddNewItem)
				{	// admin function to add a new item
					this.state = sReadyAdd;
					this.triggerFlow(eNone);
				}
				else if(evt == eEditClicked)
				{	// admin function to modify existing item
					this.state = sReadyEdit;
					this.triggerFlow(eNone);
				}
				break;
			// ---------------------------------------------------------------------------------------
			case sReadyEdit:
				if(evt == eNone)
				{	// update status display 
					gMsg.displayMsg(0, "Edit Item");
				}
				else if((evt == eMapMoved) || (evt == eMapZoomed))
				{	// update the item location
					gApp.updateItemLocation(true, false);
				}
				else if(evt == eSave)
				{	// item was created and saved
					gMsg.displayMsg(0, "Save...");
					gApp.saveEditItem();
				}
				else if(evt == eCancel)
				{	// cancel changes
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelEditItem(false);
					this.state = sNoData;
					this.triggerFlow(eCancel);
				}
				else if(evt == eAbort)
				{	// abort changes
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelEditItem(true);
					this.state = sNoData;
					this.triggerFlow(eCancel);
				}
				else if(evt == eAdminExited)
				{	// exited from admin mode w/o save or cancel
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelEditItem(false);
					this.state = sNoData;
					this.triggerFlow(eAdminExited);
				}
				else if(evt == eEditItemOk)
				{	// dataset updated successfully - reset the presentation
					gApp.cancelEditItem(false);
					gMsg.displayMsg(1, "Item modified successfully...");
					// proceed to reload our data (including the new item)
					this.state = sNoData;
					this.triggerFlow(eEditItemOk);
				}
				break;
			// ---------------------------------------------------------------------------------------
			case sReadyAdd:
				if(evt == eNone)
				{	// update status display
					gMsg.displayMsg(0, "Add New Item");
				}
				else if((evt == eMapMoved) || (evt == eMapZoomed))
				{	// update the item location
					gApp.updateItemLocation(true, false);
				}
				else if(evt == eSave)
				{	// item was created and saved
					gMsg.displayMsg(0, "Save...");
					gApp.saveNewItem();
				}
				else if(evt == eCancel)
				{	// cancel changes
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelNewItem(false);
					this.state = sNoData;
					this.triggerFlow(eCancel);
				}
				else if(evt == eAbort)
				{	// abort changes
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelNewItem(true);
					this.state = sNoData;
					this.triggerFlow(eCancel);
				}
				else if(evt == eAdminExited)
				{	// exited from admin mode w/o save or cancel
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelNewItem(false);
					this.state = sNoData;
					this.triggerFlow(eAdminExited);
				}
				else if(evt == eNewItemOk)
				{	// dataset updated successfully - reset the presentation
					gApp.cancelNewItem(false);
					gMsg.displayMsg(1, "Item added successfully...");
					// proceed to reload our data (including the new item)
					this.state = sNoData;
					this.triggerFlow(eNewItemOk);
				}
				break;
			// ---------------------------------------------------------------------------------------
			case sReadyEditCfg:
				if(evt == eNone)
				{	// update status display
					gMsg.displayMsg(0, "Edit Site Setup");
				}
				else if((evt == eMapMoved) || (evt == eMapZoomed))
				{	// update the start location
					gApp.updateStartLocation();
				}
				else if(evt == eSave)
				{	// config was updated and saved
					gMsg.displayMsg(0, "Save...");
					gApp.saveEditConfig();
				}
				else if(evt == eCancel)
				{	// cancel changes
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelEditConfig(false);
					this.state = sReady;
					this.triggerFlow(eCancel);
				}
				else if(evt == eAbort)
				{	// abort changes
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelEditConfig(true);
					this.state = sReady;
					this.triggerFlow(eCancel);
				}
				else if(evt == eAdminExited)
				{	// exited from admin mode w/o save or cancel
					gMsg.displayMsg(0, "Cancel...");
					gApp.cancelEditConfig(false);
					this.state = sReady;
					this.triggerFlow(eAdminExited);
				}
				else if(evt == eConfigOk)
				{	// config updated successfully
					this.state = sReady;
					this.triggerFlow(eNone);
				}
				break;
		}
	}
}


