// @ts-check
// Fusion Research Driver - Music Module - View-Player Map
//
// Copyright 2021 Fusion Research, Inc.
// by Alexa Jade Fanti
//
// Comments:
//   This defined a View-Player Map class used by the Server object to maintain 
//   a mapping between RTI System Views and the players the server manages.
//   This is used to support a "selected player" for each view.
//
// Depends: 
//   fusion_stringutils.js
//   fusion_misc.js
//   fusion_debugging.js
//   fusion_sys_config.js
//
//System.Print("Fusion Driver - Music Module - View-Player Map loaded...\r\n");


/******************************************************************
 * START View Player Class (used by ViewPlayerMap)
 ******************************************************************/
// This class simply associates an RTI System View ID (which is a number, but is not an index)
// with a PlayerIndex (which is a number, and an index into an array the customer maintains).
function ViewPlayer(viewID, playerIndex)
{
  	this.viewID         = parseInt(viewID, 10);
  	this.playerIndex    = parseInt(playerIndex, 10);
  	// We create this here because I think I can create it only once!
  	this.rti_queue_list = new SystemVarsList("SysVar_PlayerS_L_Queue_Entries%" + viewID);
}
/******************************************************************
 * END   View To Player Map Class
 ******************************************************************/


/******************************************************************
 * START View To Player Map Class
 ******************************************************************/
// The ViewPlayerMap is a class to track the Output (Player & Queue) associated with
// each RTI System View. The view ID is storred with an associated "playerIndex". The
// Player Index is the index of the Player and/or Queue in an array maintained by the 
// Server.
function ViewPlayerMap()
{
  	this.list = new Array();

  	// Build a map of the views and the players each has selected!
  	// By default we'll set each view to have selected player 1 (index = 0)
	var viewIDs = Config.Get("SYSTEM::TwoWayDeviceList"); // 1 3 6 11
	this.viewArray = CopyArray_RemoveNullUndefEmptyString(viewIDs.split(' ')); // ["1", "3", "6", "11"]

	
	for (var x = 0; x < this.viewArray.length; x++) {
		var vp = this.createViewPlayer(this.viewArray[x], 0);
		if (vp != null) this.list.push(vp);
	}

	// var viewArray = viewIDs.split(' '); // ["1", "3", "6", "11"]
	// for (var x = 0; x < viewArray.length; x++) {
	// 	var vp = this.createViewPlayer(viewArray[x], 0);
	// 	if (vp != null) this.list.push(vp);
	// }

	this.imageViewArray = new Array();

	var imageViewIDs = '';
	try {
		imageViewIDs = Config.Get("SYSTEM::ItemlistImageDevices");
	} catch(err) {
		DebugViewPlayerMap("Constructor: 'SYSTEM::ItemlistImageDevices' has FAILED with error: " + err, g_DEBUG_LEVEL_ERROR);
	}
	DebugViewPlayerMap("Constructor: SYSTEM::ItemlistImageDevices = '" + imageViewIDs + "'", g_DEBUG_LEVEL_TRACE);
	
	this.imageViewArray = CopyArray_RemoveNullUndefEmptyString(imageViewIDs.split(' ')); // ["1", "3", "6", "11"]
	
	DebugViewPlayerMap("Constructor: Image capable views are [" + arrayToCSV(this.imageViewArray) + "]", g_DEBUG_LEVEL_TRACE);
}

ViewPlayerMap.prototype.GetAllViews = function()
{
	return this.viewArray;
}

ViewPlayerMap.prototype.IsViewImageCapable = function(viewID)
{
	// Make sure the viewID is valid!
	viewID = parseInt(viewID, 10);
	if (isNaN(viewID)) {
		DebugViewPlayerMap("IsViewImageCapable: Parameter 'viewID' is invalid.", g_DEBUG_LEVEL_ERROR);
		return false;
	}

	var index = -1;
	var x = 0;
	while ((x < this.imageViewArray.length) && (index < 0)) {
		if (this.imageViewArray[x] == viewID) {
			index = x; 
		}
		x++;
	}
	DebugViewPlayerMap("IsViewImageCapable: imageViewArray index of 'viewID' (" + viewID + ") is " + index, g_DEBUG_LEVEL_TRACE);
  
    return index !== -1;
}

// This is used internally by the object to create a ViewPlayer object 
// with the specified viewID and playerIndex. It does NOT add it to the
// internal list and may not succeed!
// WARNING: This CAN return NULL!!!
ViewPlayerMap.prototype.createViewPlayer = function(viewID, playerIndex)
{
	// Make sure the viewID is valid!
	viewID = parseInt(viewID, 10);
	if (isNaN(viewID)) {
		DebugViewPlayerMap("createViewPlayer: Parameter 'viewID' is invalid.", g_DEBUG_LEVEL_ERROR);
		return null;
	}

	// Make sure the playerIndex is valid!
	playerIndex = parseInt(playerIndex, 10);
	if (isNaN(playerIndex)) {
		DebugViewPlayerMap("createViewPlayer: Parameter 'playerIndex' is invalid.", g_DEBUG_LEVEL_ERROR);
		return null;
	}

	// We create a ViewPlayer object with the specified viewID and playerIndex.
	// We'll validate the playerIndex using the configured player count.
	var count = ReadConfig_AsInt("Player_Count", 0);
	if (playerIndex < count) {
		DebugViewPlayerMap("createViewPlayer: Creating view " + viewID + " and assigning playerIndex " + playerIndex, g_DEBUG_LEVEL_TRACE);
		return new ViewPlayer(viewID, 0);
	} else {
		DebugViewPlayerMap("createViewPlayer: Creating view " + viewID + " and assigning invalid (-1) playerIndex (count = " + count + " while requested playerIndex = " + playerIndex + ")", g_DEBUG_LEVEL_WARN);
		return new ViewPlayer(viewID, -1);
	}
}

// When we need to know which player (playerIndex) a specific RTI System View should
// be displaying, we call this with the viewID and should get back a playerIndex. 
// If we don't have the view already (for some reason) we'll create it.
// NOTE: This may return a -1 indicating an invalid playerIndex!
ViewPlayerMap.prototype.GetPlayerIndexForViewID = function(viewID)
{
	DebugViewPlayerMap("GetPlayerIndexForViewID: Looking for view " + viewID + "...", g_DEBUG_LEVEL_TRACE);

	viewID = parseInt(viewID, 10);
	if (isNaN(viewID)) {
		DebugViewPlayerMap("GetPlayerIndexForViewID: Parameter 'viewID' is invalid. Returning invalid (-1) playerIndex", g_DEBUG_LEVEL_ERROR);
		return -1;
	}
	
	// Find the view in our list and return the playerIndex associated!	
	for (var x = 0; x < this.list.length; x++) {
		if (this.list[x].viewID == viewID) {
			var playerIndex = this.list[x].playerIndex;
			DebugViewPlayerMap("GetPlayerIndexForViewID: Returning playerIndex " + playerIndex + " for view " + viewID, g_DEBUG_LEVEL_TRACE);
			return playerIndex;
		}
	}

	// If we get here we didn't have a view somehow (this should NEVER happen).
	// Still we'd better create it!
	var vp = this.createViewPlayer(viewID, 0);
	if (vp != null) {
		this.list.push(vp);
		var playerIndex = vp.playerIndex;
		DebugViewPlayerMap("GetPlayerIndexForViewID: Returning playerIndex " + playerIndex + " for fabricated view " + viewID, g_DEBUG_LEVEL_WARN);
		return playerIndex;
	}

	// If we get here we couldn't even create an entry for some reason!
	DebugViewPlayerMap("GetPlayerIndexForViewID: Can't get playerIndex for view " + viewID + ". Returning -1 (invalid)", g_DEBUG_LEVEL_ERROR);
	return -1; // We couldn't find a view or even create one?
}



// We want to hold on to a RTI System List object for the Queue associated with a view.
// This lets us retrieve that list from a view ID.
// NOTE: This may return undefined indicating something went wrong!
ViewPlayerMap.prototype.GetQueueListForViewID = function(viewID)
{
	DebugViewPlayerMap("GetQueueListForViewID: Looking for view " + viewID + "...", g_DEBUG_LEVEL_TRACE);

	viewID = parseInt(viewID, 10);
	if (isNaN(viewID)) {
		DebugViewPlayerMap("GetQueueListForViewID: Parameter 'viewID' is invalid. Returning invalid (undefined) list", g_DEBUG_LEVEL_ERROR);
		return undefined;
	}
	
	// Find the view in our list and return the list associated!	
	for (var x = 0; x < this.list.length; x++) {
		if (this.list[x].viewID == viewID) {
			var list = this.list[x].rti_queue_list;
			DebugViewPlayerMap("GetQueueListForViewID: Returning list of " + list.Size + " items for view " + viewID, g_DEBUG_LEVEL_TRACE);
			return list;
		}
	}

	// If we get here we didn't have a view somehow (this should NEVER happen).
	// We're not gonna do anything about it except return undefined!
	return undefined; // We couldn't find a view!
}


// This takes the soecified view ID and player Index and finds the entry in our list 
// by the viewID. It then updates the playerIndex for this viewID.
ViewPlayerMap.prototype.RecordSelectedPlayer = function(viewID, playerIndex)
{
	// Make sure the viewID is valid!
	viewID = parseInt(viewID, 10);
	if (isNaN(viewID)) {
		DebugViewPlayerMap("RecordSelectedPlayer: Parameter 'viewID' is invalid.", g_DEBUG_LEVEL_ERROR);
		return false;
	}

	// Make sure the playerIndex is valid!
	playerIndex = parseInt(playerIndex, 10);
	if (isNaN(playerIndex)) {
		DebugViewPlayerMap("RecordSelectedPlayer: Parameter 'playerIndex' is invalid.", g_DEBUG_LEVEL_ERROR);
		return false;
	}

	// Go through each entry in our list looking for the ONLY entry for the specified viewID.
	// Once we find it, we update the playerIndex and return.
	for (var x = 0; x < this.list.length; x++) {
		if (this.list[x].viewID == viewID) {
			this.list[x].playerIndex = playerIndex;
			DebugViewPlayerMap("RecordSelectedPlayer: Setting view " + viewID + " to playerIndex " + playerIndex, g_DEBUG_LEVEL_TRACE);
			return true;
		}
	}

	// If we get here we didn't have a view entry and need to create one!
	var vp = this.createViewPlayer(viewID, playerIndex);
	if (vp != null) {
		DebugViewPlayerMap("RecordSelectedPlayer: Creating view " + viewID + " and assigning playerIndex " + playerIndex, g_DEBUG_LEVEL_WARN);
		this.list.push(vp);
		return true;
	}
	
	DebugViewPlayerMap("RecordSelectedPlayer: Unable to record playerIndex " + playerIndex + " to view " + viewID, g_DEBUG_LEVEL_ERROR);
	return false;
}

// When we want a list of all RTI System Views that should be "seeing" a specific
// player or queue, we call this to get an array of viewIDs... this requires the 
// player's "index".
ViewPlayerMap.prototype.GetViewIDArrayForPlayerIndex = function(playerIndex)
{
	var result = new Array(); // We may return an empty array, but it'll always be a valid array!

	// Go through our list and look for entry with a matching playerIndex.
	// When found, we add that entry's viewID to our result array.
	for (var x = 0; x < this.list.length; x++) {
		if (this.list[x].playerIndex == playerIndex) result.push(this.list[x].viewID);
	}
	DebugViewPlayerMap("GetViewIDArrayForPlayerIndex: Returning these views [" + result + "] for the playerIndex = " + playerIndex, g_DEBUG_LEVEL_TRACE);
	
	return result;
}
/******************************************************************
 * END   View To Player Map Class
 ******************************************************************/


