<cfcomponent displayname="menu">

	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		default constructor code used to make sure that variables exists
		if the user does not call init()
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cfscript>
		variables.currentNode = "";
		variables.rootNode = "";
		variables.returnChar = chr(13);
		variables.type = "SMS";
		variables.gatewayID="unknown";
		variables.debug = false;
		variables.aStringBuffer = arrayNew(1);
		variables.start = 1;
		variables.max = 3;
		variables.maxlength = 254;
	</cfscript>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		init(): call this method as a default constructor
			input: String (type) determines the gateway type being used (SMS|IM|Socket)
			output: the created instance of the menu CFC
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="init" output="false" returntype="any" hint="call this method as a default constructor">
		<cfargument name="type" type="string" required="no" default="SMS" hint="determines the gateway type being used (SMS|IM|Socket)" />
		<cfargument name="gatewayID" type="string" required="Yes" hint="the Gateway ID you would like messages sent to">
		<cfscript>
			variables.currentNode = "";
			variables.rootNode = "";
			variables.type = arguments.type;
			variables.gatewayid = arguments.gatewayID;
			variables.aStringBuffer = arrayNew(1);
			variables.start = 1;
			variables.max = 3;
			variables.maxlength = 254;
		</cfscript>
		<cfreturn this />
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		setRoot(menuNode): sets the passed in menuNode as the root of the menu tree
			input: menuNode (node) -> node that will be the root of the tree
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="setRoot" output="true" returntype="void" hint="sets the passed in menuNode as the root of the menu tree">
		<cfargument name="node" type="menuNode" required="yes" hint="node that will be the root of the tree" />
		<cfset root = node />
		<cfset variables.currentNode = root />
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		getRoot(menuNode): return the root node of the tree.
			output: menunode
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="getRoot" output="true" returntype="void" hint="get the root of the menu tree">
		<cfreturn node>
	</cffunction>	
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		setGreeting(message): sets Greeting banner above the top level menu
			input: String (message) -> message to use
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="setGreeting" output="false" returntype="void" hint="set a greeting banner above the top level menu">
		<cfargument name="message" type="string" required="yes" hint="message to prepend" />
		<cfset variables.greeting = arguments.message />
	</cffunction>
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		setReturnChar(returnChar): sets the character used for line breaks	
			input: String (returnChar) -> character to be used for line break
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="setReturnChar" output="false" returntype="void" hint="sets the character used for line breaks">
		<cfargument name="returnChar" type="string" required="yes" hint="character to be used for line break" />
		<cfset variables.returnChar = arguments.returnChar />
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		setMaxMessages(maxMessages): sets the number of consecutive messages that can be sent
			input: int (MaxMessages) -> the number of consecutive messages that can be sent
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="setMaxMessages" output="false" returntype="void" hint="sets the number of consecutive messages that can be sent">
		<cfargument name="maxMessages" type="numeric" required="yes" hint="the number of consecutive messages that can be sent" />
		<cfset variables.max = arguments.maxMessages />
	</cffunction>
		
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		setMaxMessageLength(length): Sets the maximum number of characters that can be sent in a single message
			input: int (length) -> the maximum number of characters that can be sent in a single message
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="setMaxMessageLength" output="false" returntype="void" hint="Sets the maximum number of characters that can be sent in a single message">
		<cfargument name="length" type="numeric" required="yes" hint="the maximum number of characters that can be sent in a single message" />
		<cfset variables.maxlength = arguments.length />
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		getCurrentNode: get the current node of the tree.
			output: menunode
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="getCurrentNode" output="false" returntype="menuNode" hint="return the current level node.">
		<cfreturn variables.currentNode>
	</cffunction>	
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		enableDebug(booelan): Enable debugging statements
			input: Boolean (debug) -> Determines whether or not to display debug statments
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="enableDebug" output="false" returntype="void" hint="Enable debugging statements">
		<cfargument name="debug" type="boolean" required="yes" hint="Determines whether or not to display debug statments" />
		<cfset variables.debug = arguments.debug />
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		back(): navigates up one node in the tree
			input: none
			output: menuNode -> the node that the user is currently in
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="private" name="goBack" output="false" returntype="menuNode" hint="navigates up one node in the tree">
		<cfif NOT isSimpleValue(variables.currentNode.getParent())>
			<cfset variables.currentNode = variables.currentNode.getParent() />
		</cfif>
		<cfreturn variables.currentNode />
	</cffunction>

	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		navigate(String): navigates the user to the specified node in the tree
			input: String (str) -> the key of the node to navigate to
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="private" name="navigate" output="true" returntype="void" hint="navigates the user to the specified node in the tree">
		<cfargument name="str" type="string" required="yes" hint="the key of the node to navigate to" />

		<cfscript>
			// println("navigate(" & str & ")");
			if (str EQ "B")
			{
				goBack();
				return;
			}
			else if (variables.currentNode.isLeaf())
			{
				return;
			}
			
			found = false;
			aChildren = variables.currentNode.getChildren();
			for(i = 1; i LTE arrayLen(aChildren); i = i + 1)
			{
				if (aChildren[i].getKey() EQ arguments.str)
				{
					variables.currentNode = aChildren[i];
					break;
				}
			}
		</cfscript>
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		displayMenu(): Produces a navigation string based on the current node
						that the user is in.
			input: none
			output: String -> valid navigation options for the user
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="private" name="displayMenu" output="true" returntype="string" hint="Produces a navigation string based on the current node that the user is in.">
		<cfscript>
			returnString = variables.returnChar;
			aChildren = variables.currentNode.getChildren();
			for(i = 1; i LTE arrayLen(aChildren); i = i + 1)
			{
				returnString = returnString & aChildren[i].getKey() & " - " & aChildren[i].getName() & variables.returnChar;
			}
			if (NOT isSimpleValue(variables.currentNode.getParent()))
				returnString = returnString & "B - Back" & variables.returnChar;
			else if (isdefined("variables.greeting"))
				returnString = variables.greeting & variables.returnChar & returnString;
		</cfscript>
		<cfreturn returnString />
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		validate(String): determines if the passed in string is a valid navigation key
			input: String (str) -> user entered string that will be checked to see
							 if it is a valid navigation key
			output: boolean -> determination of the validity of the passed in key
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="private" name="validate" output="true" returntype="boolean" hint="determines if the passed in string is a valid navigation key">
		<cfargument name="str" type="string" required="yes" />
		<cfscript>
			found = false;
			aChildren = variables.currentNode.getChildren();
			for(i = 1; i LTE arrayLen(aChildren); i = i + 1)
			{
				if (aChildren[i].getKey() EQ arguments.str)
				{
					found = true;
					break;
				}
			}
			
			if (NOT found AND str EQ "B")
				found = true;
		</cfscript>
		<cfreturn found />
	</cffunction>
	
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		process(String, String, String, String): 
			input: 
				String (message) -> 
				String (message_from) -> 
				String (message_to) -> 
				String (networkInfo) -> 
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->	
	<cffunction access="public" name="process" output="true" returntype="void">
		<cfargument name="message" required="yes" default="" />
		<cfargument name="message_from" required="no" default="" />
		<cfargument name="message_to" required="no" default="" />
		<cfargument name="networkInfo" required="no" default="" />
		<cfargument name="invokeEnter" required="no" default="true" />

		<cfset var str = "" />
		

		<cfscript>
			println("++++++++++++++++++++++++++++++++++++++");
			println("RECEIVED MESSAGE");
			println("message: " &arguments.message);
			println("from: " &arguments.message_from);
			println("to: " &arguments.message_to);
		</cfscript>

		
		<cfscript>
			// check to see if the command is a valid menu option based on the user's current location
			if (validate(arguments.message))
			{
				// make sure that the buffer is cleared
				variables.aStringBuffer = arrayNew(1);
				
				navigate(arguments.message);
				if (variables.currentNode.isLeaf())
				{
					// invoke the "enter" method to see if the application sends something back on first entery
					if( variables.currentNode.isEnterMode() )
					{
						str = trim(variables.currentNode.invokeCFC(message=arguments.message, message_from=arguments.message_from, message_to=arguments.message_to, networkInfo=arguments.networkInfo, method="enter"));
					}
					else
					{
						str = trim(variables.currentNode.invokeCFC(message=arguments.message, message_from=arguments.message_from, message_to=arguments.message_to, networkInfo=arguments.networkInfo, method=variables.currentNode.getMethod()));
						goBack();
					}
					// the app has sent something back so send it to the user
					if (len(str) neq 0)
					{
						str = reReplace(str, "<br>", variables.returnChar, "all");
						if (len(str) GTE variables.maxlength)
						{
							// chunk the string into an array
							variables.aStringBuffer = arrayNew(1);
							for (i = 1; i LTE evaluate(len(str) / variables.maxlength + 1); i = i + 1)
							{
								arrayAppend(variables.aStringBuffer, Mid(str, i*variables.maxlength-variables.maxlength+1, variables.maxlength));
							}
	
							upperLoop = min(arrayLen(variables.aStringBuffer), variables.max);
							for (i = 1; i LTE upperLoop; i = i + 1)
							{
								sendMessage(variables.aStringBuffer[1], message_to, message_from, networkInfo);
								ArrayDeleteAt(variables.aStringBuffer, 1);
							}
							
							if (arrayLen(variables.aStringBuffer) NEQ 0)
							{
								sendMessage('"M"ore', arguments.message_to, arguments.message_from, arguments.networkInfo);
							}
						}
						else
						{
							sendMessage(str, arguments.message_to, arguments.message_from, arguments.networkInfo);
						}
					}
					// the app did not return anything from enter() so inform the user that they have entered the app
					else 
					{
						sendMessage("Entering " & variables.currentNode.getName(), arguments.message_to, arguments.message_from, arguments.networkInfo);
					}
					
				}
				else
				{
					sendMessage(displayMenu(), arguments.message_to, arguments.message_from, arguments.networkInfo);
				}
			}

			// if the user has entered an application from the menu pass on the request to the registered CFC
			else if (variables.currentNode.isLeaf())
			{
				// check if the user is requesting more from the previous session
				if (arrayLen(variables.aStringBuffer) NEQ 0 AND left(arguments..message, 1) EQ "M")
				{
					upperLoop = min(arrayLen(variables.aStringBuffer), variables.max);
					for (i = 1; i LTE upperLoop; i = i + 1)
					{
						sendMessage(variables.aStringBuffer[1], arguments.message_to, arguments.message_from, arguments.networkInfo);
						ArrayDeleteAt(variables.aStringBuffer, 1);
					}
					
					if (arrayLen(variables.aStringBuffer) NEQ 0)
					{
						sendMessage('"M"ore', arguments.message_to, arguments.message_from, arguments.networkInfo);
					}
				}
				else 
				{
					str = trim(variables.currentNode.invokeCFC(message=arguments.message, message_from=arguments.message_from, message_to=arguments.message_to, networkInfo=arguments.networkInfo));
					str = reReplace(str, "<br>", variables.returnChar, "all");
					if (len(str) GTE variables.maxlength)
					{
						// chunk the string into an array
						variables.aStringBuffer = arrayNew(1);
						for (i = 1; i LTE evaluate(len(str) / variables.maxlength + 1); i = i + 1)
						{
							arrayAppend(variables.aStringBuffer, Mid(str, i*variables.maxlength-variables.maxlength+1, variables.maxlength));
						}

						upperLoop = min(arrayLen(variables.aStringBuffer), variables.max);
						for (i = 1; i LTE upperLoop; i = i + 1)
						{
							sendMessage(variables.aStringBuffer[1], arguments.message_to, arguments.message_from, arguments.networkInfo);
							ArrayDeleteAt(variables.aStringBuffer, 1);
						}
						
						if (arrayLen(variables.aStringBuffer) NEQ 0)
						{
							sendMessage('"M"ore', arguments.message_to, arguments.message_from, arguments.networkInfo);
						}
					}
					else
						sendMessage(str, arguments.message_to, arguments.message_from, arguments.networkInfo);
				}
			}
			// unknown command and the user is not in an application so just show the current location in the menu system
			else
			{
				sendMessage(displayMenu(), arguments.message_to, arguments.message_from, arguments.networkInfo);
			}
		</cfscript>
		
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		sendMessage(String): sends the passed in string back to the gateway
			input: 
				String (message) -> message to send to gateway client
				String (message_from) -> which client the message is being sent from
				String (message_to) -> client that the message is being sent to
				String (networkInfo) -> 
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="private" name="sendMessage" output="false" returntype="void" hint="sends the passed in string back to the gateway">
		<cfargument name="message" type="string" required="yes" hint="message to send to gateway client" />
		<cfargument name="message_from" type="string" required="yes" hint="which client the message is being sent from" />
		<cfargument name="message_to" type="string" required="yes" hint="client that the message is being sent to" />
		<cfargument name="networkInfo" type="string" required="yes" />
		
		<cfscript>	
			println("++++++++++++++++++++++++++++++++++++++");
			if (variables.type EQ "IM")
			{
				println("SENDING IM");
				mesg = structNew();
				mesg.command = "submit";
				mesg.buddyID = arguments.message_to;
				mesg.message = arguments.message;
				sendGatewayMessage(variables.gatewayID, mesg);
			}
			else if (variables.type EQ "SMS")
			{
				println("SENDING SMS");
				mesg = structNew();
				mesg.command = "submit";
				mesg.sourceAddress = arguments.message_from;
				mesg.destAddress = arguments.message_to;
				mesg.shortMessage = arguments.message;
				sendGatewayMessage(variables.gatewayID, mesg);
			}
			else if (variables.type EQ "Socket")
			{
				println("SENDING Socket Msg");
				mesg = structNew();
				mesg.MESSAGE = arguments.message;
				mesg.OriginatorID = arguments.message_to;
				sendGatewayMessage(variables.gatewayID, mesg);
			}
			else
			{
				println("Don't know how to send the message: unknown TYPE");
			}
			
			println("SENT MESSAGE");
			println("GatewayId: " & variables.gatewayID);
			println("message: " &arguments.message);
			println("to: " &arguments.message_to);
			println("from: " &arguments.message_from);
			
		</cfscript>
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		dumpTree(): debug method that returns a string that represents the menu tree
			input: void
			output: String -> string representation of the menu tree
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="public" name="dumpTree" output="false" returntype="string" hint="debug method that returns a string that represents the menu tree">
		<cfreturn variables.rootNode.dumpTree() />
	</cffunction>
	
	<!--- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		println(String): debug method used to pipe information into a log file
			input: String (str) -> string to be sent to the log file
			output: void
	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --->
	<cffunction access="private" name="println" output="true" returntype="void" hint="debug method used to pipe information into a log file">
		<cfargument name="str" type="string" required="yes" hint="string to be sent to the log file" />
		<cflog text="#arguments.str#" type="Information" file="cfc_menu" application="yes" />
	</cffunction>
</cfcomponent>