/*

These are generic SLS functions for interacting with the backend of the
WAMI gui.  This file is pretty much the original generic_galaxy, except
that now it delegates some of the _wamiHandlers methods on to the already
existing _galaxyHandlers model we've been using.  This way, we don't break
any of the existing javascript... hopefully.

This library code assumes a global variable called _galaxyHandlers which should be set
by anyone using the library.  It allows points of override to the default functionality

Definitions:
_galaxyHandlers.galaxyPollExceptionHandler(e)
		Called when an exception occurs within polling galaxy
_galaxyHandlers.responseExceptionHandler(e) 
		Called when an exception occurs within handleResponse
_galaxyHandlers.postGuiConnectedHandler
		Called after we have successfully connected audio, but before sending the "ready" message
_galaxyHandlers.initGalaxyHandler
		Called to get app specific XML to be used to initialize a customized AjaxControl
_galaxyHandlers.onLoadHandler
		Called 	at the beginning of onLoad
_galaxyHandlers.onUnloadHandler
		Called at the beginning of onUnLoad
_galaxyHandlers.playingHandler(isPlaying)
		Non-default playing behavior called after default playing() behavior
_galaxyHandlers.layoutHandler(frameWidth, frameHeight)
        Do any necessary layout of your components        
_galaxyHandlers.inputTypingHandler
		Called whenever there is typing in the inputNL box
_galaxyHandlers.typedInputSentHandler
		Called after the typed input has been sent
_galaxyHandlers.nbestDropDownActivated
		Called when the nbest drop down is activated
_galaxyHandlers.nbestTokenDropDownActivated
		Called when one token on the nbest drop down is activated
_galaxyHandlers.newNbest
		Called when we put a new nbest list in the correctable interface
_galaxyHandlers.localExternalAudioHandler
		Called with strings of "press" or "release" when recognition begins or ends
_galaxyHandlers.handleAudio
		Called with a reply node containing the status string
*/


// Delegate to galaxyHandlers which SLS systems already use
if (_galaxyHandlers) {
	var _wamiHandlers = new Object();
	
	_wamiHandlers.timeoutHandler = function() {};  // Handle timeouts in the genericResponseHandler.
	_wamiHandlers.responseHandler = _galaxyHandlers.responseHandler;
	_wamiHandlers.onUnloadHandler = _galaxyHandlers.onUnloadHandler;
	_wamiHandlers.pollExceptionHandler = _galaxyHandlers.galaxyPollExceptionHandler;
	_wamiHandlers.responseExceptionHandler = _galaxyHandlers.responseExceptionHandler;
	
	// Override the WAMI defaults for SLS systems:
	_wamiHandlers.layoutHandler = genericLayout;
	_wamiHandlers.onLoadHandler = onLoadHandler;
}


/* Globally available variables */
var _madeCorrectionWhilePlaying = false;

function onLoadHandler() {
	if(_galaxyHandlers && _galaxyHandlers.onLoadHandler) _galaxyHandlers.onLoadHandler();

	// Now the GUI is connected if we even load the page.
	postGuiConnected();
}

function abortAudio() {
	//postXML("<?xml version='1.0' encoding='UTF-8'?><update audio='abort' />");
	sendFrame("{c audio :abort_audio 1}");
}

function postGuiConnected() {
	if(!_audioHttpOnly) {
		if(document.getElementById("AudioApplet") && document.getElementById("AudioApplet").connect) {
			document.getElementById("AudioApplet").connect();
		}
	}

	status("Connected");
	
	if(_galaxyHandlers && _galaxyHandlers.postGuiConnectedHandler) _galaxyHandlers.postGuiConnectedHandler();

	postXML("<?xml version='1.0' encoding='UTF-8'?><update ready='true' />");	
	//send a message indicating everything is finally connected	
	//setTimeout("poll()", 5);
}

function sendNL(nlString, isCorrection) {
	if(!isCorrection) {
		isCorrection = false;
	}
	
	if(nlString == null) {
		var inputArea = document.getElementById("inputNL");
		nlString = inputArea.value;
		inputArea.value = "";
	}
	
	if(nlString == null || nlString == "") {
		return false;
	}
		
	var xmlDoc=newXMLDocument("update");
	var root = xmlDoc.firstChild;;
	root.setAttribute("inputNL", nlString);
	root.setAttribute("isCorrection", isCorrection ? 'true' : 'false');
	postXML(xmlDoc);	
	
	if(_galaxyHandlers && _galaxyHandlers.typedInputSentHandler) _galaxyHandlers.typedInputSentHandler();
}
	
function packageAndSendFrame(doc, frameName) {
	var frame = "{c " + frameName + " ";
	
	for(var i=2; i < arguments.length; i++) {
		var argName = arguments[i];
		var value = doc.getElementById(argName).value;

		//trim away surrounding spaces
		value = value.replace(/^\s+/g, ""); 
		value = value.replace(/\s+$/g, ""); 
		
		if(value != null && value != "") {
			frame += ":" + argName + ' "' + value + '" ';	
		}
	}
	frame += " }";
	
	return sendFrame(frame);	
}


function sendFrame(frame) {
  	var xmlDoc=newXMLDocument("update");
	var root = xmlDoc.firstChild;
	root.setAttribute("frame", frame);
   	postXML(xmlDoc);

   	return false;
}



function genericHandleAnticipatedReply(replyNode) {
	var str = replyNode.getAttribute("reply_string");
	
	var inputArea = document.getElementById("inputNL");
	if(inputArea) {
		inputArea.value = str;	
	}
}



function genericHandleAudio(replyNode) {
	//for, e.g. mobile devices, be more terse
	var terse = isSmallScreen();
		
	if (_inCar) {
		var statusStr = replyNode.getAttribute("status");
		
		if(statusStr == "Listening") status("Listening");
		if(statusStr == "Recording") status("Listening");
		
		if(statusStr == "Playing") {
			playing(true);		
			status("Playing");
		}
	
		if(statusStr == "Done Playing") {
			playing(false);
			status("Ready");
		}
	
		
		var statusNode = document.getElementById("AudioStatus");
		if(statusNode) {
			statusNode.removeChild(statusNode.firstChild);
			statusNode.appendChild(document.createTextNode(statusStr));
		}
	} else {
		var statusStr = replyNode.getAttribute("status");
		
		if(statusStr == "Listening") {
			status(terse ? "Start Speaking" : "Start speaking now");
		}
		
		if(statusStr == "Recording") status(terse ? "Streaming" : "Streaming speech to the recognition server");
		
		if(statusStr == "Playing") {
			playing(true);		
			status(terse ? "Playing" : "Playing synthesized speech.  Tap the 'Stop Speaking' button to stop.");
		}
	
		if(statusStr == "Done Playing") {
			playing(false);
			status(terse ? "Ready" : "Ready.  Tap the 'Click to talk' button to start speaking");
		}
	
		
		//implicitly start scenario when you start talking
		if(_scenarioIsMaximized && _showScenario && (statusStr == "Listening" || statusStr == "Recording")) {
			startScenario();
		}
		
		var statusNode = document.getElementById("AudioStatus");
		if(statusNode) {
			statusNode.removeChild(statusNode.firstChild);
			statusNode.appendChild(document.createTextNode(statusStr));
		}
	}
	
	if (_galaxyHandlers && _galaxyHandlers.handleAudio) _galaxyHandlers.handleAudio(replyNode); 
}

function genericHandleSpeakerList(replyNode) {
	var hyps = replyNode.getElementsByTagName("hyp");
	var speakerIDElement = document.getElementById("SpeakerID");
	
	if(hyps.length > 0 && speakerIDElement) {		
		var hypList = "";
		var numToUse = (hyps.length > 1) ? 1 : hyps.length;
		for(var i = 0; i < numToUse; i++) {
			var hyp = hyps[i];
			var score = hyp.getAttribute("score");
			var speaker = hyp.getAttribute("speaker");
			if(i > 0) {
				hypList += " | ";
			}

			var openTag = "<font color='red'>";
			var closeTag = "</font";
			if(parseFloat(score) > .5) {
				openTag = "<font color='green'>";
			}
			var formattedScore = Math.round(100.0 * score);
			hypList += (openTag + speaker + " (" + formattedScore + "%)" + closeTag);
		}	
		
		speakerIDElement.innerHTML = hypList;
	}
}

function genericHandleResponse(replyNode) {
	try {
		var replyType = replyNode.getAttribute("type");
		if(replyType == "nl") {
			genericHandleNL(replyNode);
		} else if(replyType == "audio") {
			genericHandleAudio(replyNode);
		} else if(replyType == "html") {
			genericHandleHTML(replyNode);
		}  else if(replyType == "reading") {
			genericHandleReading(replyNode);
		} else if (replyType == "read_paragraph") {
			genericHandleRead_Paragraph(replyNode);
		} else if (replyType == "correct_paragraph") {
			genericHandleCorrect_Paragraph(replyNode);
		}else if(replyType == "timeout") {
			genericHandleTimeout(replyNode);
			return(false);
		} else if(replyType == "status") {
			status(replyNode.getAttribute("status"));
		} else if(replyType == "anticipated_reply") {
			genericHandleAnticipatedReply(replyNode);
		} else if(replyType == "nbest_confusions") {
			genericHandleNbestConfusions(replyNode);
		} else if(replyType == "popup_window_html") {
			genericHandlePopupWindow(replyNode);
		} else if(replyType == "show_scenario") {
			genericHandleShowScenario(replyNode);
		} else if(replyType == "reload") {
			genericHandleReload(replyNode);
		} else if(replyType == "eval") {
			genericHandleEval(replyNode);
		} else if(replyType == "play_user_waveform") {
			genericHandlePlayUserWaveform(replyNode);
		} else if(replyType == "popout_form") {
			//popOutForm();
			window.open("bin.gwt/edu.mit.csail.sls.wami.userstudies.AnnotationModule/AnnotationModule.html");
		}
	} catch(e) {
		if (!_inCar) {
			alert(e);
		}
	}
	return(true);
	
}

//update the status bar, if there is one
function status(statusStr) {
	var statusBar = document.getElementById("StatusBar");
	if(statusBar) {
		if(statusBar.childNodes && statusBar.childNodes.length > 0) {
			statusBar.removeChild(statusBar.childNodes[0]);
		}
		
		var span = document.createElement("span");
		span.style.color = "black";
		span.appendChild(document.createTextNode(statusStr));
		statusBar.appendChild(span);
	}
}



function setSendButtonDisabled(status) {
		var sendButton = document.getElementById("SendButton");
		if(sendButton) {
			sendButton.disabled = status;
		}
}


/**
 * toggles button availability based on whether or not audio is currently
 * playing
 */
function playing(isPlaying) {
		_isPlaying = isPlaying;

		for(var i = 0; i < _correctionSpanNodes.length; i++) {
			_correctionSpanNodes[i].sendButton.disabled = false;
		}
				
		var sendButton = document.getElementById("SendButton");
		if(sendButton) {
			sendButton.disabled = isPlaying;
		}
		
		var shutupButton = document.getElementById("ShutUp");
		if(shutupButton) {
			shutupButton.disabled = !isPlaying;
		}
		
		var goBack = document.getElementById("GoBack");
		if(goBack) {
			goBack.disabled = isPlaying || _stateStack.length <= 1;
		}
		
		if(!isPlaying && _madeCorrectionWhilePlaying) {
			var recResults = document.getElementById("RecResultsSpan");
			if(recResults && recResults.sendButton) {
				recResults.sendButton.disabled = false;
			}			
		}
				
		if(isPlaying) {
			var recResults = document.getElementById("RecResultsSpan");
			if(recResults && recResults.sendButton) {
				//recResults.sendButton.disabled = true;
			}
		}
		
		_madeCorrectionWhilePlaying = false;						
		if(_galaxyHandlers && _galaxyHandlers.playingHandler) _galaxyHandlers.playingHandler(isPlaying);
}


function genericHandleNL(replyNode) {
	var replyString = replyNode.getAttribute("reply_string");
	var speaker = replyNode.getAttribute("speaker");

	appendConversation(speaker, replyString);	
}


/**
  * append conversational text
  */
function appendConversation(user, nlString) {
    
    // garym: when using flightbrowser with the management
    // no updates were made to conversation frame,
    // this might work more generally
    var conversationFrame = document.getElementById("conversation");
    
    if (conversationFrame) {
	//if(parent.conversation) {
 		//var body = parent.conversation.document.getElementsByTagName("body")[0];
		//var doc = parent.conversation.document; //garym
		var body = conversationFrame.contentDocument.getElementsByTagName("body")[0];
		var doc  = conversationFrame.contentDocument;

		var stringNode = doc.createElement("span");
		//Don't get rid of the extra " " -- we need it for diacritics in pinyin!!
		stringNode.innerHTML = "<b>" + user + "</b>: " + nlString + " ";
		//var stringNode = doc.createTextNode(user + ": " + nlString);
		if(body.childNodes.length == 0) {
			body.appendChild(stringNode);
		} else {
			body.insertBefore(doc.createElement("br"), body.childNodes[0]);
			body.insertBefore(stringNode, body.childNodes[0]);
		}
	}
	
}

/**
  * handle selection
  */
function select(index) {
	postXML("<?xml version='1.0' encoding='UTF-8'?><update selected='" + index + "' />");
}

//bindings for up/down if using the keyboard to control speech..not currently used
function keyDown(e) {
	if(!e) e = window.event;
	
	if(_isPlaying) {	
		sendFrame("{c audio :abort_audio 1}");
	} else {
		var audioApplet = document.getElementById("AudioApplet");
		if(audioApplet != null) {
			audioApplet.starListening();
		}
	}
}

function keyUp(e) {
	if(!e) e = window.event;
	
	if(!_isPlaying) {		
		var audioApplet = document.getElementById("AudioApplet");
		if(audioApplet != null) {
			audioApplet.stopRecording();
		}
	}
}


function genericHandleHTML(replyNode) {
	var html = replyNode.childNodes[0].nodeValue;
	parent.galaxyHTML.document.open("text/html");
	parent.galaxyHTML.document.write(html);
	parent.galaxyHTML.document.close();	
}

function genericHandleReading(replyNode) {
	var html = replyNode.childNodes[0].nodeValue;
	parent.readingHTML.document.open("text/html");
	parent.readingHTML.document.write(html);
	parent.readingHTML.document.close();	
}

function genericHandleRead_Paragraph(replyNode) {
	displayParagraphToRead();	
}

function genericHandleCorrect_Paragraph(replyNode) {
	//correctParagraph(replyNode.getAttribute("usid"));	
	correctParagraph();
}

function genericHandleTimeout(replyNode) {	
	setSendButtonDisabled(true);	
	stopPolling();
	alert("Session has timed out.  Click browser's reload button to restart");
}


//fires when some input is being typed
function fireInputTyping() {
	if(_galaxyHandlers && _galaxyHandlers.inputTypingHandler) _galaxyHandlers.inputTypingHandler();
}

var _popup = null;
//pops up a window with some html in it
function genericHandlePopupWindow(replyNode) {
	if(_popup) {
		closePopup();
	}
	
	var html = replyNode.getAttribute("html_string");
	html += "<br><div style='text-align:center;'><span class='PseudoButton' onclick='closePopup()'> Close </span></div>";
	//alert(html);
	
	_popup = createHtmlWindow(html, "GenericPopupWindow", "Popup");
}

function closePopup() {
	if(_popup) {
		var body = document.getElementsByTagName("body")[0];
		body.removeChild(_popup);
		_popup = null;
	}
}

var _scenarioIsMaximized = true;
var _scenarioControlDiv;
function genericHandleShowScenario(replyNode) {
	_scenarioIsMaximized = true;
	
	var scenarioDiv = document.getElementById("ScenarioDiv");
	if(scenarioDiv == null) {
		scenarioDiv = document.createElement("div");
		scenarioDiv.id = "ScenarioDiv";
		scenarioDiv.className = "ScenarioWindow";
		scenarioDiv.onscroll = function() { scrolled('scenarioDiv'); };
		document.getElementsByTagName("body")[0].appendChild(scenarioDiv);	
	}
	
	while(scenarioDiv.childNodes.length > 0) {
		scenarioDiv.removeChild(scenarioDiv.childNodes[0]);
	}
	
	var titleSpan = document.createElement("div");
	titleSpan.className = "ScenarioTitle";
	titleSpan.id = "ScenarioTitle";
	var scenarioId = replyNode.getAttribute("id");
	var numScenarios = replyNode.getAttribute("num_scenarios");
	titleSpan.appendChild(document.createTextNode("Scenario " + scenarioId + " of " + numScenarios));
	titleSpan.appendChild(document.createElement("br"));
	
	var contentSpan = document.createElement("span");
	contentSpan.id = "ScenarioContent";
	contentSpan.innerHTML = replyNode.getAttribute("scenario");
						
	var controlDiv = document.createElement("div");
	controlDiv.id = "ScenarioControlDiv";
	controlDiv.style.textAlign = "center";
	var controlSpan = document.createElement("span")
	controlSpan.className = "PseudoButton";
	controlDiv.appendChild(controlSpan);
	controlSpan.onclick = function() { startScenario(); };
	controlSpan.appendChild(document.createTextNode("Click to Start!"));
	
	scenarioDiv.appendChild(titleSpan);
	scenarioDiv.appendChild(controlDiv);
	scenarioDiv.appendChild(contentSpan);
	
	
	_scenarioControlDiv = controlDiv;		
	doLayout();	
}

var _scenarioFinished = false;
function startScenario() {
	var scenarioDiv = document.getElementById("ScenarioDiv");
	var finishDiv = document.createElement("div");
	finishDiv.id = "ScenarioFinishDiv";
	finishDiv.style.textAlign = "center";
	
	var finishSpan = document.createElement("span");
	finishSpan.className = "PseudoButton";
	finishSpan.onclick = function() { finishScenario(true); };
	finishSpan.appendChild(document.createTextNode("Finished"));

	
	var giveupSpan = document.createElement("span");
	giveupSpan.className = "PseudoButton";	
	giveupSpan.onclick = function() { finishScenario(false); };
	giveupSpan.appendChild(document.createTextNode("Give Up"));
	
	var spacerSpan = document.createElement("span");
	spacerSpan.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
	
	finishDiv.appendChild(finishSpan);
	finishDiv.appendChild(spacerSpan);
	finishDiv.appendChild(giveupSpan);
	
	scenarioDiv.insertBefore(finishDiv, document.getElementById("ScenarioContent"));

	if(scenarioDiv && _scenarioControlDiv) {
		scenarioDiv.removeChild(_scenarioControlDiv);
	}
	setScenarioIsMaximized(false);
	
	sendFrame('{c gui :user_model_update 1 :update {q update :clicked_to_start_scenario 1}}');	
}

function setScenarioIsMaximized(value) {
	_scenarioIsMaximized = value;
	doLayout();
}

function finishScenario(success) {
	if(!_scenarioFinished) {
		_scenarioFinished = true;
		sendFrame('{c gui :user_model_update 1 :update {q update :clicked_to_finish_scenario 1 :success ' +  (success ? '1' : '0') + '}}');
		var xmlDoc=newXMLDocument("update");
		var root = xmlDoc.firstChild;
		root.setAttribute("finished_scenario", "true");
		root.setAttribute("success", success ? 'true' : 'false');
		postXML(xmlDoc);	
	} else {
		//send the frame so we record the click, but don't update the progress database
		sendFrame('{c gui :user_model_update 1 :update {q update :clicked_to_finish_scenario 1 :success ' +  (success ? '1' : '0') + '}}');
		alert("It appears you've already clicked Finished or Give Up.  Your browser should have refreshed automatically.  If it didn't, you can hit REFRESH yourself, or close and re-open your browser.");
	}
}

function genericHandleReload(replyNode) {
	window.location.reload(true); 
}

function genericLocalExternalAudioHandler(action) {
	if(action == "press") {
		status("Recording");
	} else if(action == "release") {
		status("Done Recording");
	}
}

function genericHandleEval(replyNode) {
	var code = replyNode.firstChild.nodeValue;
	eval(code);
}

var _playUserWaveformPopup = null;
function genericHandlePlayUserWaveform(replyNode) {
	var playing = replyNode.getAttribute("playing");
	
	if("true" == playing) {		
		var html = "<center><h2>Now playing what I heard</h2></center>";
		_playUserWaveformPopup = createHtmlWindow(html, "PlayUserWaveformPopupWindow", "PlayUserWaveformPopup");	
	} else {
		_playUserWaveformPopup.parentNode.removeChild(_playUserWaveformPopup);		
	}
}

//logs that an element has been scrolled, just set the following in your div:
//<div id="mydiv" onscroll="scrolled('mydiv')>
var _lastScrollTime = new Object();
var SCROLL_UPDATE_THRESHOLD = 1000; //only send scrolls on same element if more than 1 second apart
function scrolled(element) {
	var date = new Date();
	var time = date.getTime();
	if(_lastScrollTime[element] == null || 
	   (time - _lastScrollTime[element]) > SCROLL_UPDATE_THRESHOLD) {
		_lastScrollTime[element] = time;
		sendFrame('{c gui :user_model_update 1 :update {q update :scrolled "' + element + '"}}');	
	}
}
