// Fusion Research Driver - Music Module - Server Feedback
//
// Copyright 2013-2021 Fusion Research, Inc.
// by Alexa Jade Fanti
//
// Comments:
//   This file contains mechanisms for reading event notification from the music server
//   and handling those events through calls to global data structures and objects!
//   We open a TCP connection, we note the user wants it open (so we can reconnect if 
//   necessary). Then we set up for receipt of data, and when we get data we add it to 
//   a buffer then try to pop off a message. We keep popping off messages until we can't
///  then wait for more data. If the connection is closed, we re-open if necessary!
//
// Depends: 
//   fusion_stringutils.js
//   fusion_debugging.js
//   fusion_sys_config.js
//   fusion_serverdiscovery.js
//
//System.Print("Fusion Driver - Music Module - Server Feedback loaded...\r\n");



// Global variables for TCP Feedback
//
var g_TCP_FEEDBACK_ACTIVE = true; // We set this to true to "maintain" a connection!

// This is a global timer to "ensure feedback connectivity".
var G_FeedbackTimer = new Timer();

//
//
// Define the global TCP connection to keep connected to the server to receive 
// notifications that we will turn into feedback.
//
var g_TCP_FEEDBACK_OBJECT = new TCP(OnTCPFeedback_CommRx);
g_TCP_FEEDBACK_OBJECT.OnConnectFunc = OnTCPFeedback_Connect;
g_TCP_FEEDBACK_OBJECT.OnDisconnectFunc = OnTCPFeedback_Disconnect;
//
// This is used to buffer data when we don't have enough to form a complete message
var g_TCP_FEEDBACK_DATA = "";
//


// Start accepting feedback (this opens the connection to the server)
function TCPFeedback_Start()
{
	DebugFeedback("Starting...", g_DEBUG_LEVEL_INFO);
	g_TCP_FEEDBACK_ACTIVE = true;

	G_FeedbackTimer.Stop(); // Just in case it's already running...
	TCPFeedback_EnsureConnection();	// This WILL start the timer.
}

function TCPFeedback_EnsureConnection()
{
	if (g_TCP_FEEDBACK_OBJECT.ConnectState == 1) {
		// We need to write things to the connection so it can detect closure.
		// But we can only do that when the connection THINKS it's connected!
		g_TCP_FEEDBACK_OBJECT.Write('Ping'); 
	}

	if ((g_TCP_FEEDBACK_ACTIVE) && (g_TCP_FEEDBACK_OBJECT.ConnectState == 0)) {
		DebugFeedback("TCPFeedback_EnsureConnection opening connection to " + OperationalServerIP() + ":9724...", g_DEBUG_LEVEL_INFO);
		g_TCP_FEEDBACK_OBJECT.Open(OperationalServerIP(), 9724);
	}

	if (g_SERVER.GetQueueCount() <= 0) {
		DebugFeedback("TCPFeedback_EnsureConnection: No queues... trying to re-initialize server communication...", g_DEBUG_LEVEL_WARN);
		g_SERVER.ConstructEverything();

		if (g_SERVER.GetQueueCount() > 0) {
			DebugFeedback("TCPFeedback_EnsureConnection Ready to control server. (API version = " + g_SERVER.version_api + ", " + g_SERVER.GetQueueCount() + " queues)", g_DEBUG_LEVEL_INFO);
		} else {
			DebugFeedback("TCPFeedback_EnsureConnection Server is not available with " + g_SERVER.GetQueueCount() + " queues", g_DEBUG_LEVEL_ERROR);
		}
	}

	DebugFeedback("TCPFeedback_EnsureConnection starting G_FeedbackTimer...", g_DEBUG_LEVEL_TRACE);
	G_FeedbackTimer.Start(TCPFeedback_EnsureConnection, 10 * 1000);
}

// Stop accepting feedback (this closes the connection to the server)
function TCPFeedback_Stop()
{
	DebugFeedback("TCPFeedback_Stop stopping G_FeedbackTimer", g_DEBUG_LEVEL_INFO);
	G_FeedbackTimer.Stop(); // No longer try to connect...

	g_TCP_FEEDBACK_ACTIVE = false;
	g_TCP_FEEDBACK_OBJECT.Close();
	DebugFeedback("Stopped.", g_DEBUG_LEVEL_TRACE);
}

//*************** BEGIN  TCP Connection Handlers (Feedback) ***************
function OnTCPFeedback_Connect()
{
	DebugFeedback("OnTCPFeedback_Connect", g_DEBUG_LEVEL_INFO);
	g_TCP_FEEDBACK_OBJECT.Write("listen 1\r"); // Tell the server we want everything!

	// I'd like to do a full queue status and entry fetch here...?
	//for (var x = 0; x < g_SERVER.queues.length; x++) {
	//	var queue = g_SERVER.queues[x];
	//	queue.Fetch(); // This should set variables!
	//}
}

function OnTCPFeedback_CommRx(data)
{
	DebugFeedback("HTTP_SYS OnCommRx", g_DEBUG_LEVEL_TRACE);
	DebugFeedback(data + "\r\n------------------------------", g_DEBUG_LEVEL_TRACE);

	g_TCP_FEEDBACK_DATA = g_TCP_FEEDBACK_DATA + data;

	var index = 0;
	while (index > -1) {
		index = g_TCP_FEEDBACK_DATA.indexOf("</FusionResearchLMS>");
		if (index > -1) {
			var message = g_TCP_FEEDBACK_DATA.substr(0, index +20);
			DebugFeedback("message = " + message, g_DEBUG_LEVEL_TRACE);
			g_TCP_FEEDBACK_DATA = g_TCP_FEEDBACK_DATA.substr(index +21);

			HandleFeedback(message);
		}
	}

	DebugFeedback("------------------------------", g_DEBUG_LEVEL_TRACE);
	DebugFeedback("Buffer contains " + g_TCP_FEEDBACK_DATA.length + " bytes", g_DEBUG_LEVEL_TRACE);
}

function OnTCPFeedback_Disconnect()
{
	if (g_TCP_FEEDBACK_ACTIVE) {
		DebugFeedback("OnTCPFeedback_Disconnect (But we didn't want to stop!!!)... Restarting...", g_DEBUG_LEVEL_ERROR);
		TCPFeedback_Start();
	} else {
		DebugFeedback("OnTCPFeedback_Disconnect", g_DEBUG_LEVEL_INFO);
	}
}
//*************** END    TCP Connection Handlers (Feedback) ***************



/******************************************************************
 * START Feedback Event Class
 ******************************************************************/
function FeedbackEvent(from, key, value, fields)
{
  	this.Initialize();

	if ((from   !== undefined) && (from   != null)) this.from  = from.toLowerCase();
	if ((key    !== undefined) && (key    != null)) this.key   = key.toLowerCase();
	if ((value  !== undefined) && (value  != null)) this.value = value.toLowerCase();
	if ((fields !== undefined) && (fields != null) && (typeof(fields) == "xml")) this.fields = fields;
}

FeedbackEvent.prototype.Initialize = function()
{
	this.from   = "";
	this.key    = "";
	this.value  = "";
	this.fields = new Array();

	this.title  = '';
	this.index  = '';
	this.target = '';
	this.url    = '';
	this.itemId = '';
	this.dryrun = false;
}

FeedbackEvent.prototype.isValid = function()
{
	var result = true; // Assume OK
	if ((this.from === undefined)  || (this.from == null))  result = false;
	if ((this.key === undefined)   || (this.key == null))   result = false;
	if ((this.value === undefined) || (this.value == null)) result = false;
	return result;
}

FeedbackEvent.prototype.toString = function()
{
	var result = this.from + " =>";

	if ((this.key !== undefined) && (this.key != null) && (this.key != "")) result += " " + this.key + " = " + this.value;

	if (this.fields.length() > 0) {
		result += " (";
		for (var x = 0; x < this.fields.length(); x++) {
			result += this.fields[x];
			if (x < this.fields.length() -1) result += ", ";
		}
		result += ")";
	}

	return result;
}
/******************************************************************
 * END   Feedback Event Class
 ******************************************************************/




//*************** BEGIN  Feedback Processing Handler ***************
// This parses the feedback the server spontaneously sends us and calls object methods to
// record stuff...
//
// Parameters
//		data - This is the data we received from the server over the TCP connection
//			   that we monitor for feedback status messages. It's in a particular format 
//			   that we are prepared to parse and translate into specific SystemVariables 
//			   so the information can be recorded properly.
//
function HandleFeedback(data)
{
	if (data == "") return;
	DebugFeedback("HandleFeedback :\r\n" + data + "\r\n------------------------------", g_DEBUG_LEVEL_TRACE);

	// Clean the feedback (cause it should be XML)
	var clean_data = SanitizeXML(data);
	if (clean_data == "") {
		DebugFeedback("HandleFeedback XML data is missing after 'cleaning'", g_DEBUG_LEVEL_ERROR);
		return;
	}
	// Create a new XML object from the feedback
	var xml = new XML(clean_data);

	// Extract the stuff we want from the feedback XML
	var from   = xml.feedback.message.@from;
	var key    = xml.feedback.message.@key;
	var value  = xml.feedback.message.@value;
	var fields = xml.feedback.message.field;

	var event = new FeedbackEvent(from, key, value, fields);

	// Extract additiona data!
	event.title  = xml.feedback.message.@title;
	event.index  = xml.feedback.message.@index;	
	event.target = xml.feedback.message.@target;
	event.url    = xml.feedback.message.@url;
	event.itemId = xml.feedback.message.@itemid;
	event.dryrun = parseBool(xml.feedback.message.@dryrun);

	DebugFeedback("HandleFeedback : " + event.toString(), g_DEBUG_LEVEL_INFO);

	g_SERVER.ProcessFeedback(event);
}
//*************** END    Feedback Processing Handler ***************
