<%@ Register TagPrefix="speech" Namespace="Microsoft.Speech.Web.UI" Assembly="Microsoft.Speech.Web, Version=1.0.3200.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

<script runat="server" language="C#">
// <![CDATA[

// In the server side code, we expose the properties of this user control that
// can be set when using it.

	// Provide a client-side function to call if the transfer fails. This function will be
	// passed an argument that contains the cause (e.g., busy, noAnswer) of the failure.
	public string OnClientFailure 
	{
		get { return(onClientFailure); }
		set { onClientFailure = value; }
	}

	// Provide a client-side function to call if the transfer succeeds
	public string OnClientTransfered 
	{
		get { return(onClientTransfered); }
		set { onClientTransfered = value; }
	}

	// Provide the phone number to which you want to transfer
	public string TransferToNum 
	{
		get { return(transferToNum); }
		set { transferToNum = value; }
	}
	
	// Provide the phone number that you want to appear as the 'caller ID'.
	// Note that this is done using a private data field supported by the
	// the Intel TIM. Other TIMs might require different methods of handling this.
	public string CallerIDNum 
	{
		get { return(callerIDNum); }
		set { callerIDNum = value; }
	}
	
	// Provide the client activation function for the transfer. This function will be
	// called once, and if true the transfer will be attempted.
	public string ClientActivationFunction 
	{
		get { return(clientActivationFunction); }
		set { clientActivationFunction = value; }
	}
	
	private string clientActivationFunction = String.Empty;
	private string onClientFailure = String.Empty;
	private string onClientTransfered = String.Empty;
	private string transferToNum = String.Empty;
	private string callerIDNum = String.Empty;
	
	private void Page_Load(object sender, System.EventArgs e)
	{
		if (this.ClientActivationFunction == null || this.ClientActivationFunction.Length == 0) 
		{
			throw new ArgumentNullException("ClientActivationFunction", "ClientActivationFunction must be specified");
		}

		if (this.OnClientFailure == null || this.OnClientFailure.Length == 0) 
		{
			throw new ArgumentNullException("OnClientFailure", "OnClientFailure must be specified");
		}

		// The properties that are expected to be dynamic (the numbers we are calling
		// from and to) are rendered to hidden text fields, to avoid causing the
		// rendered script of the page to change (which would make it uncacheable).
		Page.RegisterHiddenField("hfSupervisedTransferControl_TransferToNum", this.transferToNum);
		Page.RegisterHiddenField("hfSupervisedTransferControl_CallerIDNum", this.callerIDNum);

		// Other properties (functions and handlers) are rendered directly
		// as variables, because this avoids having to use 'eval' to access the
		// function pointers and because they are not expected to change (within
		// a single usage of the user control).
		StringBuilder s = new StringBuilder();
		s.Append("<script>\r\n");
		s.Append(String.Format("var SupervisedTransferControl_CurrentStage = {0}() ? \"ConsultationMessage\" : \"Complete\";\r\n", this.clientActivationFunction));
		s.Append(String.Format("var SupervisedTransferControl_OnClientFailure = {0};\r\n", this.onClientFailure));
		s.Append(String.Format("var SupervisedTransferControl_OnClientTransfered = {0};\r\n", 
			this.onClientTransfered.Length > 0 ? this.onClientTransfered : "null"));

		s.Append("</sc" + "ript>\r\n"); // break up the tag to avoid looking like XML
		Page.RegisterStartupScript("SupervisedTransferControl_StartupScript", s.ToString());
		DataBind();
	}
// ]]>
</script>

<speech:SmexMessage id="ConsultationMessage" runat="server" OnClientBeforeSend="ConsultationMessage_OnClientBeforeSend"
	OnClientError="ConsultationMessage_OnClientError" OnClientReceive="ConsultationMessage_OnClientReceive"
	ClientActivationFunction="ConsultationMessage_ClientActivationFunction"></speech:SmexMessage>
<br>
<speech:SmexMessage id="TransferMessage" runat="server" OnClientBeforeSend="TransferMessage_OnClientBeforeSend"
	OnClientError="TransferMessage_OnClientError" OnClientReceive="TransferMessage_OnClientReceive"
	ClientActivationFunction="TransferMessage_ClientActivationFunction"></speech:SmexMessage>
<br>
<speech:SmexMessage id="ReconnectMessage" runat="server" OnClientBeforeSend="ReconnectMessage_OnClientBeforeSend"
	OnClientError="ReconnectMessage_OnClientError" OnClientReceive="ReconnectMessage_OnClientReceive"
	ClientActivationFunction="ReconnectMessage_ClientActivationFunction"></speech:SmexMessage>
<br>

<script>
// <![CDATA[

// Use global variables to hold state. Note that (for simplicity) no attempt is made 
// to avoid collisions with existing variables with the same names. 
//
// NB: this control has not been made robust to post-back (e.g., in any of the handlers)

var SupervisedTransferControl_NewCallId = null;
var SupervisedTransferControl_FailureCause = null;
var SupervisedTransferControl_CallWasHeld = false;

var SupervisedTransferControl_TransferToNum = document.all["hfSupervisedTransferControl_TransferToNum"].value;
var SupervisedTransferControl_CallerIDNum = document.all["hfSupervisedTransferControl_CallerIDNum"].value;

function getNodeText(theNode) {
  if (theNode == null) return "";
  return(theNode.text);
}

// Each of the three SmexMessage controls has four methods defined: onClientBeforeSend, 
// onClientError, onClientReceived, and clientActivationFunction.

//
// ConsultationMessage
//
function ConsultationMessage_ClientActivationFunction() {
	return (SupervisedTransferControl_CurrentStage == "ConsultationMessage");
}

function ConsultationMessage_OnClientBeforeSend() {
	if (SupervisedTransferControl_TransferToNum == 0) 
	{
		throw("TransferToNum must be specified");
	}
	var numberToDial = SupervisedTransferControl_TransferToNum.replace(/[^\d]/g, "");

	var callID = RunSpeech.CurrentCall().Get("CallID");
	var deviceID = RunSpeech.CurrentCall().Get("DeviceID");
	var outboundCallingDevice = SupervisedTransferControl_CallerIDNum;
	if (outboundCallingDevice == "")
		outboundCallingDevice = "123456";

	return(
	"<ConsultationCall xmlns=\"http://www.ecma.ch/standards/ecma-323/csta/ed2\">" + 
	"	<existingCall>" + 
	"		<callID>" + callID + "</callID>" + 
	"		<deviceID>" + deviceID + "</deviceID>" + 
	"	</existingCall>" + 
	"	<consultedDevice>" + numberToDial + "</consultedDevice>" + 
	"	<extensions>" + 
	"		<privateData>" +
	"			<private xmlns:pri=\"http://schemas.microsoft.com/speech/2003/08/CSTAPrivateData\">" +
	"				<pri:setOutboundCallingDevice>" + outboundCallingDevice + "</pri:setOutboundCallingDevice>" +
	"				<pri:setCallAnalysis>false</pri:setCallAnalysis>" +
	"			</private>" +
	"		</privateData>" +
	"	</extensions>" +
	"</ConsultationCall>"
	);
}

function ConsultationMessage_OnClientError() {
	SupervisedTransferControl_FailureCause = "SmexError";
	SupervisedTransferControl_CurrentStage = "ReconnectMessage";
}

function ConsultationMessage_OnClientReceive(smexObj, docEl) {
	switch(docEl.baseName) {
		case "ConsultationCallResponse":
			// Store the new call ID for use in either the Transfer or the Reconnect messages.
			SupervisedTransferControl_NewCallId = getNodeText(docEl.selectSingleNode("/csta:ConsultationCallResponse/csta:initiatedCall/csta:callID"));
			break;

		case "HeldEvent":
			// We set a flag saying that the call has been held, so that we know
			// if it makes sense to attempt a reconnect following a failure.
			SupervisedTransferControl_CallWasHeld = true;
			break;

		case "DeliveredEvent":
			break;

		case "NetworkReachedEvent":
			break;

		case "EstablishedEvent":
			// EstablishedEvent means we have successfully connected to the
			// new number and can attempt to Transfer.
			SupervisedTransferControl_CurrentStage = "TransferMessage";
			return(true);
			break;

		// A FailedEvent or CSTAErrorCode both mean we've failed for one
		// reason or another to reach the new party
		case "FailedEvent":
			SupervisedTransferControl_FailureCause = getNodeText(docEl.selectSingleNode("/csta:FailedEvent/csta:cause"));
			SupervisedTransferControl_CurrentStage = "ReconnectMessage";
			return(true);
			break;
		
		case "CSTAErrorCode":
			SupervisedTransferControl_FailureCause = getNodeText(docEl.selectSingleNode("/csta:CSTAErrorCode"));
			SupervisedTransferControl_CurrentStage = "ReconnectMessage";
			return(true);
			break;

		default:
			break;
	}
	return(false);
}

//
// TransferMessage
//
function TransferMessage_ClientActivationFunction() {
	return (SupervisedTransferControl_CurrentStage == "TransferMessage");
}

function TransferMessage_OnClientBeforeSend() {
	var callID = RunSpeech.CurrentCall().Get("CallID");
	var deviceID = RunSpeech.CurrentCall().Get("DeviceID");

	return(
	"<TransferCall xmlns=\"http://www.ecma.ch/standards/ecma-323/csta/ed2\">"+
	"	<heldCall>" + 
	"		<callID>" + callID + "</callID>" +
	"		<deviceID>" + deviceID + "</deviceID>" + 
	"	</heldCall>" + 
	"	<activeCall>" + 
	"		<callID>" + SupervisedTransferControl_NewCallId + "</callID>" + 
	"		<deviceID>" + deviceID + "</deviceID>" + 
	"	</activeCall>" + 
	"</TransferCall>");
}

function TransferMessage_OnClientError() {
	SupervisedTransferControl_FailureCause = "SmexError";
	SupervisedTransferControl_CurrentStage = "ReconnectMessage";
}

function TransferMessage_OnClientReceive(smexObj, docEl) {
	switch(docEl.baseName) {
		case "TransferCallResponse":
			break;

		case "TransferedEvent":
			// The TransferedEvent (note the spelling with a single 'r') indicates the two
			// parties are connected; upon receiving it we can call our success handler.
			SupervisedTransferControl_CurrentStage = "Complete";
			if (SupervisedTransferControl_OnClientTransfered != null) {
				SupervisedTransferControl_OnClientTransfered();
			}
			return(true);
			break;

		// A FailedEvent or CSTAErrorCode both mean we've failed for one
		// reason or another to complete the transfer and we should attempt
		// to reconnect the original (held) call.
		case "FailedEvent":
			SupervisedTransferControl_FailureCause = getNodeText(docEl.selectSingleNode("/csta:FailedEvent/csta:cause"));
			SupervisedTransferControl_CurrentStage = "ReconnectMessage";
			return(true);
			break;

		case "CSTAErrorCode":
			SupervisedTransferControl_FailureCause = getNodeText(docEl.selectSingleNode("/csta:CSTAErrorCode"));
			SupervisedTransferControl_CurrentStage = "ReconnectMessage";
			return(true);
			break;

		default:
			break;
	}
	return(false);
}

//
// ReconnectMessage
//
function ReconnectMessage_ClientActivationFunction() {
	if (SupervisedTransferControl_CurrentStage == "ReconnectMessage") {
		SupervisedTransferControl_CurrentStage = "Complete";

		// If the call was never Held, there is not point issuing a 
		// ReconnectMessage, so we can call the failure handler immediately
		if (SupervisedTransferControl_CallWasHeld == true) {
			return(true);
		} else {
			SupervisedTransferControl_OnClientFailure(SupervisedTransferControl_FailureCause);
			return(false);
		}
	} else {
		return(false);
	}
}

function ReconnectMessage_OnClientBeforeSend() {
	var callID = RunSpeech.CurrentCall().Get("CallID");
	var deviceID = RunSpeech.CurrentCall().Get("DeviceID");

	return(
	"<ReconnectCall xmlns=\"http://www.ecma.ch/standards/ecma-323/csta/ed2\">" + 
	"	<heldCall>" + 
	"		<callID>" + callID + "</callID>" + 
	"		<deviceID>" + deviceID + "</deviceID>" + 
	"	</heldCall>" + 
	"	<activeCall>" + 
	"		<callID>" + SupervisedTransferControl_NewCallId + "</callID>" + 
	"		<deviceID>" + deviceID + "</deviceID>" + 
	"	</activeCall>" + 
	"</ReconnectCall>");
}

function ReconnectMessage_OnClientError() {
	SupervisedTransferControl_FailureCause = "SmexError";
	SupervisedTransferControl_OnClientFailure(SupervisedTransferControl_FailureCause);
	window.close();
}

function ReconnectMessage_OnClientReceive(smexObj, docEl) {
	switch(docEl.baseName) {
		case "ReconnectCallResponse":
			break;

		case "RetrievedEvent":
			// If we reach RetrievedEvent, we've recovered from whatever went wrong
			// and we can call the failure handler and then return control to the 
			// 'calling' page.
			SupervisedTransferControl_OnClientFailure(SupervisedTransferControl_FailureCause);
			SupervisedTransferControl_CurrentStage = "Complete";
			return(true);
			break;

		// A FailedEvent or CSTAErrorCode both mean we've failed to even reconnect
		// the held call, which is essentially fatal, and we might as well just
		// do a window.close to free up resources.
		case "FailedEvent":
			SupervisedTransferControl_OnClientFailure(SupervisedTransferControl_FailureCause);
			SupervisedTransferControl_CurrentStage = "Complete";
			window.close();
			return(true);
			break;

		case "CSTAErrorCode":
			SupervisedTransferControl_OnClientFailure(SupervisedTransferControl_FailureCause);
			SupervisedTransferControl_CurrentStage = "Complete";
			window.close();
			return(true);
			break;

		default:
			break;
	}
	return(false);
}
// ]]>
</script>
<br>