// Fusion Research OMS Audio Driver
// Copyright 2013 Fusion Research Inc.
// by Alexa Jade Fanti
//

var g_PRODUCT_NAME = 'Music Server Driver';

System.Print(g_PRODUCT_NAME + " loaded...\r\n");

/******************************************************************
 * START System Configuration
 ******************************************************************/

/******************************************************************
 * END   System Configuration
 ******************************************************************/




/******************************************************************
 * START System Startup
 ******************************************************************/
if (OperationalServerIP() == '') {
    DebugGeneral('Server IP address is unknown...', g_DEBUG_LEVEL_INFO);
    DiscoverServer();
} else {
    DebugGeneral('Server IP address is fixed at ' + OperationalServerIP(), g_DEBUG_LEVEL_INFO);
    CoreCreate();
}
/******************************************************************
 * END   System Startup
 ******************************************************************/

function  DriverTest1(view) 
{
/*
	var regexp = /\/spotty=[0-9+]\.0_(.+)\.[0-9+]/;

	var str = '/service/spotty=1.0_femm%20isolation.0';
	var matches_array = str.match(regexp);
	if (matches_array !== null) {
		System.Print(matches_array);
		System.Print('Length = ' + matches_array.length);
	} else {
		System.Print('No match found!');
	}

	var str = '/service/spotty=1.0_femm.0';
	var matches_array = str.match(regexp);
	if (matches_array !== null) {
		System.Print(matches_array);
		System.Print('Length = ' + matches_array.length);
	} else {
		System.Print('No match found!');
	}

	var str = '/service/spotty=1.0_This is a test!.0';
	var matches_array = str.match(regexp);
	if (matches_array !== null) {
		System.Print(matches_array);
		System.Print('Length = ' + matches_array.length);
	} else {
		System.Print('No match found!');
	}


	var regexp = /^\/\[service\]\/spotty=[0-9+]\.0$|^\/service\/spotty=[0-9+]\.0$|^\/app\/spotty=[0-9+]\.0$/g;

	var str = '/service/spotty=1.0';
	var matches_array = str.match(regexp);
	if (matches_array !== null) {
		System.Print(matches_array);
		System.Print('Length = ' + matches_array.length);
	} else {
		System.Print('No match found!');
	}

	var str = '/app/spotty=1.0';
	var matches_array = str.match(regexp);
	if (matches_array !== null) {
		System.Print(matches_array);
		System.Print('Length = ' + matches_array.length);
	} else {
		System.Print('No match found!');
	}

	var str = '/[service]/spotty=1.0';
	var matches_array = str.match(regexp);
	if (matches_array !== null) {
		System.Print(matches_array);
		System.Print('Length = ' + matches_array.length);
	} else {
		System.Print('No match found!');
	}

	var str = '/service/spotty=0.0';
	var matches_array = str.match(regexp);
	if (matches_array !== null) {
		System.Print(matches_array);
		System.Print('Length = ' + matches_array.length);
	} else {
		System.Print('No match found!');
	}
	
	var str = '/service/spotty=0.0.0';
	var matches_array = str.match(regexp);
	if (matches_array !== null) {
		System.Print(matches_array);
		System.Print('Length = ' + matches_array.length);
	} else {
		System.Print('No match found!');
	}
*/

	//var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
	//var regexp = /[A-E]/gi;
	//var matches_array = str.match(regexp);

	//System.Print(matches_array);
// ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']

}

function  DriverTest2(view) 
{
}

function  DriverTest3(view) 
{
}

function  DriverTest4(view) 
{
}

function  DriverTest5(view) 
{
}




/******************************************************************
 * START Library System Functions
 ******************************************************************/
function  SysFunc_Library_SelectAndOpenTarget(playerIndex, index, view, windowtop)
{
    Debug_SysFuncs("SysFunc_Library_SelectAndOpenTarget : Index = '" + index + "'' for playerIndex = '" + playerIndex + "'' or RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);

    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_SelectAndOpenTarget : Can't perform select and open '" + index + "' because the view is bad ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }

    browser.Open(index, "", windowtop, playerIndex, view); // No searching from here!
}

function  SysFunc_Library_OpenParent(view)
{
    Debug_SysFuncs("SysFunc_Library_OpenParent : RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);
 
    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_OpenParent : Can't perform goto 'parent' because the view is bad ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }
    browser.Back();
}

function  SysFunc_Library_OpenRoot(view)
{
    Debug_SysFuncs("SysFunc_Library_OpenRoot : RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);
 
    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_OpenRoot : Can't perform goto home/root because the view is bad ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }
    browser.Home();
}

function  SysFunc_Library_GotoPath(path, view)
{
    Debug_SysFuncs("SysFunc_Library_GotoPath : '" + path + "' on RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);
 
    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_GotoPath : Can't perform goto path '" + path + "' because the view is bad ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }
    browser.OpenPath(path);
}

function  SysFunc_Library_PlayTarget(playerIndex, action, view)
{
    Debug_SysFuncs("SysFunc_Library_PlayTarget : Action = '" + action + "'' for playerIndex = '" + playerIndex + "'' or RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);

    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_PlayTarget : Can't find library browser for view ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    var target = browser.getTarget();
    if ((target === undefined) || (target == null)) {
        Debug_SysFuncs("SysFunc_Library_PlayTarget : Can't play target that is undefined!", g_DEBUG_LEVEL_ERROR);
        return;
    }

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Library_PlayTarget : Can't play target '" + target.path + "'' because we couldn't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    if (action == 'now') {
        // Do we have things in the queue already?
        var count = queue.entries.length;

        if (count == 0) {
            // We can just PlayNow!
            Debug_SysFuncs("SysFunc_Library_PlayTarget : Action = Now and queue is empty so we're loading...", g_DEBUG_LEVEL_TRACE);
            queue.PlayThis(target.path, 'load');
        } else {
            // We have multiple things in the queue
            // If we're adding a radio station... we should load anyway...
            if ((target.path.startsWith('/service') || target.path.startsWith('/[service]')) && 
                (target.path.includes('pandora') || target.path.includes('sirius') || target.path.includes('soundmachine'))) {
                Debug_SysFuncs("SysFunc_Library_PlayTarget : Action = Now and target is Pandora, SiriusXM or SoundMachine so we're loading...", g_DEBUG_LEVEL_TRACE);
                queue.PlayThis(target.path, 'load');
            } else if (target.path.startsWith('/radio')) {
                Debug_SysFuncs("SysFunc_Library_PlayTarget : Action = Now and target is Internet Radio so we're loading...", g_DEBUG_LEVEL_TRACE);
                queue.PlayThis(target.path, 'load');
            } else {
                // Look at what's already in the queue and if it's Pandora, SiriusXM or SoundMachine OR it's internet radio then we still load...
                var first = queue.entries[0];
                if (first.path.includes('frpandora') || first.path.includes('frsiriusxmu') || first.path.includes('frsoundmachine') ||
                    first.path.includes('http://') || first.path.includes('https://')) {
                    Debug_SysFuncs("SysFunc_Library_PlayTarget : Action = Now and target appears to be a track, but the top queue entry is Pandora, SiriusXM, SoundmMachine or Internet Radio so we're loading...", g_DEBUG_LEVEL_TRACE);
                    queue.PlayThis(target.path, 'load');
                } else {
                    Debug_SysFuncs("SysFunc_Library_PlayTarget : Action = Now and target appears to be a track so we're inserting and calling next...", g_DEBUG_LEVEL_TRACE);
                    queue.PlayThis(target.path, 'insert');
                    queue.SetTransport('next');
                }            
            }
        }
    } else {
        queue.PlayThis(target.path, action);
    }
}

function  SysFunc_Library_PlayFavorite(playerIndex, index, view)
{
    Debug_SysFuncs("SysFunc_Library_PlayFavorite : '" + index + "'' for playerIndex = '" + playerIndex + "'' or RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Library_PlayFavorite : Can't play favorite " + index + " because we couldn't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }    
    
    var favorite = g_SERVER.favorite_tool.getEntry(index);
    if (favorite === undefined) {
        Debug_SysFuncs("SysFunc_Library_PlayFavorite : Can't play favorite " + index + " because we couldn't find it.", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    queue.PlayThis(favorite.path, 'load');
}

function  SysFunc_Library_PlayFavoriteEx(playerIndex, id, view)
{
    Debug_SysFuncs("SysFunc_Library_PlayFavoriteEx : '" + id + "'' for playerIndex = '" + playerIndex + "'' or RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Library_PlayFavoriteEx : Can't play favorite " + id + " because we couldn't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    queue.PlayFavoriteEX(id);
}

function  SysFunc_Library_AddTargetAsFavorite(view)
{
    Debug_SysFuncs("SysFunc_Library_AddTargetAsFavorite : On RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);
    
    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_AddTargetAsFavorite : Can't find library browser for view ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    var target = browser.getTarget();
    if (target === undefined) {
        Debug_SysFuncs("SysFunc_Library_AddTargetAsFavorite : Library has no target to operate on", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    g_SERVER.favorite_tool.AddFavorite(target.name, target.favoriteUrl, target.favoriteArtUrl);
}

function  SysFunc_Library_RemoveTargetAsFavorite(view)
{
    Debug_SysFuncs("SysFunc_Library_RemoveTargetAsFavorite : On RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);

    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_RemoveTargetAsFavorite : Can't find library browser for view ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    var target = browser.getTarget();
    if (target === undefined) {
        Debug_SysFuncs("SysFunc_Library_RemoveTargetAsFavorite : Library has no target to operate on", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    g_SERVER.favorite_tool.RemoveFavorite(target.favoriteId);
}



function  SysFunc_Library_RenameTargetPlaylistAs(view)
{
    Debug_SysFuncs("SysFunc_Library_RenameTargetPlaylistAs : On RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);

    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_RenameTargetPlaylistAs : Can't find library browser for view ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    var target = browser.getTarget();
    if (target === undefined) {
        Debug_SysFuncs("SysFunc_Library_RenameTargetPlaylistAs : Library has no target to operate on", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    // We open the keyboard and ask them for a name...
    getKeyboard_fromViewID_andOpen(view, target.name, 'Callback_Library_RenameTargetPlaylistAs(' + target.playlistId + ', "$1")');
}

function  Callback_Library_RenameTargetPlaylistAs(playlistId, name)
{
    Debug_SysFuncs("Callback_Library_RenameTargetPlaylistAs : Keyboard returned '" + name + "' for playlistId = " + playlistId, g_DEBUG_LEVEL_TRACE);
    g_SERVER.RenamePlaylist(playlistId, name)
}

function  SysFunc_Library_DeleteTargetPlaylist(view)
{
    Debug_SysFuncs("SysFunc_Library_DeleteTargetPlaylist : On RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);

    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_DeleteTargetPlaylist : Can't find library browser for view ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    var target = browser.getTarget();
    if (target === undefined) {
        Debug_SysFuncs("SysFunc_Library_DeleteTargetPlaylist : Library has no target to operate on", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    g_SERVER.DeletePlaylist(target.playlistId);
}

function  SysFunc_Library_RemoveTargetPlaylistEntry(view)
{
    Debug_SysFuncs("SysFunc_Library_RemoveTargetPlaylistEntry : On RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);

    var browser = gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);
    if (browser === undefined) {
        Debug_SysFuncs("SysFunc_Library_RemoveTargetPlaylistEntry : Can't find library browser for view ('" + view + "')", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    var target = browser.getTarget();
    if (target === undefined) {
        Debug_SysFuncs("SysFunc_Library_RemoveTargetPlaylistEntry : Library has no target to operate on", g_DEBUG_LEVEL_ERROR);
        return;
    }    

    g_SERVER.RemovePlaylistItem(target.playlistId, target.playlistItemIndex);
}
/******************************************************************
 * END   Library System Functions
 ******************************************************************/


/******************************************************************
 * START Player Transport Functions
 ******************************************************************/
function  SysFunc_Player_SetTransport(playerIndex, command, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);
    
    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_SetTransport : Can't set transport to '" + command + "' because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SetTransport(command);
}

function  SysFunc_Player_SetTransportPosition(playerIndex, value, format, view)
{
    // format = seconds | percent

    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_SetTransportPosition : Can't set position to '" + value + "' (" + format + ") because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    if (format == 'percent') queue.SetPosition_Percent(value);
        else queue.SetPosition_Seconds(value);
}
/******************************************************************
 * END   Player Transport Functions
 ******************************************************************/


/******************************************************************
 * START Player Volume Functions
 ******************************************************************/
function  SysFunc_Player_SetVolume(playerIndex, value, direction, view)
{
    // Direction = + | 0 | -
    // Value must be between 0 and 100
    value = parseInt(value, 10);
    if (isNaN(value)) {
        Debug_SysFuncs("SysFunc_Player_SetVolume : Can't set volume because 'value' is not a number", g_DEBUG_LEVEL_ERROR);
        return;
    }
    if (value < 0) {
        Debug_SysFuncs("SysFunc_Player_SetVolume : Setting volume to 0 (because " + value + " is less than 0)", g_DEBUG_LEVEL_WARN);
        value = 0;
    }
    if (value > 100) {
        Debug_SysFuncs("SysFunc_Player_SetVolume : Setting volume to 100 (because " + value + " is greater than 100)", g_DEBUG_LEVEL_WARN);
        value = 100;
    }
    
    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_SetVolume : Can't set volume to '" + value + "' because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SetVolume(value, direction);
}

function  SysFunc_Player_SetMute(playerIndex, command, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);
    
    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_SetMute : Can't set mute to '" + command + "' because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SetMute(command);
}
/******************************************************************
 * END   Player Volume Functions
 ******************************************************************/


/******************************************************************
 * START Misc Queue Functions
 ******************************************************************/
function  SysFunc_Player_SetRepeat(playerIndex, command, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);
    
    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_SetRepeat : Can't set repeat to '" + command + "' because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SetRepeat(command);
}

function  SysFunc_Player_SetShuffle(playerIndex, command, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_SetShuffle : Can't set shuffle to '" + command + "' because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SetShuffle(command);
}

function  SysFunc_Player_QueueGoTo(playerIndex, index, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_QueueGoTo : Can't set queue index to " + index + " because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SetQueueIndex(index);
}

function  SysFunc_Player_QueueRemove(playerIndex, index, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_QueueRemove : Can't remove queue entry because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.RemoveQueueEntry(index);
}

function  SysFunc_Player_QueueSaveAs(playerIndex, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_QueueSaveAs : Can't save queue because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    // We open the keyboard and ask them for a name...
    getKeyboard_fromViewID_andOpen(view, "New Playlist", 'Callback_Player_QueueSaveAs(' + playerIndex + ', ' + view + ', "$1")');
}
function  Callback_Player_QueueSaveAs(playerIndex, view, name)
{
    Debug_SysFuncs("Callback_Player_QueueSaveAs : Keyboard returned '" + name + "' for playerIndex = '" + playerIndex + "' or on RTI System View '" + view + "'", g_DEBUG_LEVEL_TRACE);
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("Callback_Player_QueueSaveAs : Can't save queue because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SaveQueueAs(name);
}

function  SysFunc_Player_QueueClear(playerIndex, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_QueueClear : Can't clear queue because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SetQueueClear();
}

function  SysFunc_Player_QueueFetch(playerIndex, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_QueueFetch : Can't refresh (fetch) queue status and entries because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.Fetch(0);
}
/******************************************************************
 * END   Misc Queue Functions
 ******************************************************************/


/******************************************************************
 * START Music Rating Functions
 ******************************************************************/
function  SysFunc_Player_RateTrack(playerIndex, direction, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_RateTrack : Can't rate track because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    if (direction.toLowerCase() == 'up') {
        queue.RatePositive();
    } else
    if (direction.toLowerCase() == 'down') {
        queue.RateNegative();
    } else {
        Debug_SysFuncs("SysFunc_Player_RateTrack : Can't rate track because 'direction' (" + direction + ") must be one of 'up' or 'down'", g_DEBUG_LEVEL_ERROR);
    }
}
/******************************************************************
 * END   Music Rating Functions
 ******************************************************************/


/******************************************************************
 * START Misc Player Functions
 ******************************************************************/
function  SysFunc_Player_SetPower(playerIndex, command, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);
    
    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_SetPower : Can't set player power because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    queue.SetPlayerPower(command);
}

function  SysFunc_Player_SyncAll(playerIndex, action, view) // 'all' | 'none'
{
    Debug_SysFuncs("SysFunc_Player_SyncAll : Sync '" + action + "' players", g_DEBUG_LEVEL_TRACE);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_SyncAll : Can't sync/unsync because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    if (action.toLowerCase() == 'all') queue.SyncAll();
        else queue.UnsyncAll();
}

function  SysFunc_Player_Sync(masterIndex, slaveIndex, view)
{
    masterIndex = parseInt(masterIndex, 10);
    slaveIndex = parseInt(slaveIndex, 10);

    var masterQueue = g_SERVER.GetQueueByIndexOrView(masterIndex, view);
    if ((masterQueue === undefined) || (masterQueue == null)) {
        Debug_SysFuncs("SysFunc_Player_Sync : Can't sync master player because we can't find a queue for masterIndex = '" + masterIndex + "' or on RTI System View '" + view + "'", g_DEBUG_LEVEL_ERROR);
        return;
    }

    var slaveQueue = g_SERVER.GetQueueByIndexOrView(slaveIndex, view);
    if ((slaveQueue === undefined) || (slaveQueue == null)) {
        Debug_SysFuncs("SysFunc_Player_Sync : Can't sync slave player because we can't find a queue for slaveIndex = '" + slaveIndex + "' or on RTI System View '" + view + "'", g_DEBUG_LEVEL_ERROR);
        return;
    }

    Debug_SysFuncs("SysFunc_Player_Sync : Syncing " + slaveQueue._playerMac + " to master " + masterQueue._playerMac, g_DEBUG_LEVEL_INFO);
    masterQueue.SyncTo(slaveQueue._playerMac);
}

function  SysFunc_Player_SyncToggle(masterIndex, slaveIndex, view)
{
    masterIndex = parseInt(masterIndex, 10);
    slaveIndex = parseInt(slaveIndex, 10);

    var masterQueue = g_SERVER.GetQueueByIndexOrView(masterIndex, view);
    if ((masterQueue === undefined) || (masterQueue == null)) {
        Debug_SysFuncs("SysFunc_Player_SyncToggle : Can't toggle sync master player because we can't find a queue for masterIndex = '" + masterIndex + "' or on RTI System View '" + view + "'", g_DEBUG_LEVEL_ERROR);
        return;
    }

    var slaveQueue = g_SERVER.GetQueueByIndexOrView(slaveIndex, view);
    if ((slaveQueue === undefined) || (slaveQueue == null)) {
        Debug_SysFuncs("SysFunc_Player_SyncToggle : Can't toggle sync slave player because we can't find a queue for slaveIndex = '" + slaveIndex + "' or on RTI System View '" + view + "'", g_DEBUG_LEVEL_ERROR);
        return;
    }

    // If the slave is already synced (to ANYTHING) we unsync it!
    var peers = g_SERVER.sync_tool.FindPeers(slaveQueue._playerMac);
    if (peers.length > 0) {
        Debug_SysFuncs("SysFunc_Player_SyncToggle : Slave " + slaveQueue._playerMac + " is already synced... so we'll unsync it!", g_DEBUG_LEVEL_INFO);
        slaveQueue.SyncTo();
    } else {
        Debug_SysFuncs("SysFunc_Player_SyncToggle : Syncing " + slaveQueue._playerMac + " to master " + masterQueue._playerMac, g_DEBUG_LEVEL_INFO);
        masterQueue.SyncTo(slaveQueue._playerMac);
    }
}

function  SysFunc_Player_Unsync(playerIndex, view)
{
    // We need the player ID first... If this is -1 we need to use the view to fins the player! 
    playerIndex = parseInt(playerIndex, 10);

    var queue = g_SERVER.GetQueueByIndexOrView(playerIndex, view);
    if (queue === undefined) {
        Debug_SysFuncs("SysFunc_Player_Unsync : Can't sync/unsync because we can't find a queue.", g_DEBUG_LEVEL_ERROR);
        return;
    }

    Debug_SysFuncs("SysFunc_Player_Unsync : Un-syncing " + queue._playerMac, g_DEBUG_LEVEL_INFO);
    queue.SyncTo();
}

function  SysFunc_Player_SyncGroups_Fetch()
{
    g_SERVER.sync_tool.Fetch(0); // Force the fetch RIGHT NOW and blocking!
}

function  SysFunc_Sync_VolumeMode_Set(mode)
{
	if ((mode === undefined) || (mode === null)) {
		Debug_SysFuncs("SysFunc_Sync_VolumeMode_Set : Mode is undefined... using NONE", g_DEBUG_LEVEL_WARN);
		mode = "none";
	}
	
    var url = GetURL_ServerPrefix() + '/oms/api/1/manage/squeezelite?action=store_sync_volume&sync_volume=' + mode;
    Debug_SysFuncs("SysFunc_Sync_VolumeMode_Set : URL = " + url, g_DEBUG_LEVEL_TRACE);

    Debug_SysFuncs("SysFunc_Sync_VolumeMode_Set : Setting sync group volume mode to '" + mode + "'", g_DEBUG_LEVEL_TRACE);
	var httpReq = new HttpRequest(url);
	httpReq.Fetch();
}

/******************************************************************
 * END   Misc Player Functions
 ******************************************************************/

/******************************************************************
 * START Misc Functions
 ******************************************************************/
function  SysFunc_UIVisibility_Detail(visible, view) 
{
	// This is just here to hlpe RTI in their Keyboard Layer functionality...
	// All it does is let the integrator send a command that sets these boolean variables (flags)
	if (visible == 1) {
		UIVisibility_Set(view, 2);
	} else {
		UIVisibility_Set(view, 0);
	}
}


function  UIVisibility_Set(view, mode)
{
	// Mode = 0 -> Main Visible (TRUE), Keyboard Hidden (FALSE), Detail Hidden (FALSE)
	// Mode = 1 -> Main Hidden (FALSE), Keyboard Hidden (TRUE),  Detail Hidden (FALSE)
	// Mode = 2 -> Main Hidden (False), Keyboard Hidden (FALSE), Detail Hidden (TRUE)
	
	switch(mode) {
		case 0 : {  // Mode = 0 -> Main Visible (TRUE), Keyboard Hidden (FALSE), Detail Hidden (FALSE)
				SysVar_WriteBool("SysVar_UIVisibility_B_MainVisible%" + view, true);
				SysVar_WriteBool("SysVar_UIVisibility_B_KeyboardVisible%" + view, false);
				SysVar_WriteBool("SysVar_UIVisibility_B_DetailVisible%" + view, false);
			}
			break;
		case 1 : {	// Mode = 1 -> Main Hidden (FALSE), Keyboard Hidden (TRUE),  Detail Hidden (FALSE)
				SysVar_WriteBool("SysVar_UIVisibility_B_MainVisible%" + view, false);
				SysVar_WriteBool("SysVar_UIVisibility_B_KeyboardVisible%" + view, true);
				SysVar_WriteBool("SysVar_UIVisibility_B_DetailVisible%" + view, false);
			}
			break;
		case 2 : {	// Mode = 2 -> Main Hidden (False), Keyboard Hidden (FALSE), Detail Hidden (TRUE)
				SysVar_WriteBool("SysVar_UIVisibility_B_MainVisible%" + view, false);
				SysVar_WriteBool("SysVar_UIVisibility_B_KeyboardVisible%" + view, false);
				SysVar_WriteBool("SysVar_UIVisibility_B_DetailVisible%" + view, true);
			}
			break;
	}
	
}
/******************************************************************
 * END Misc Functions
 ******************************************************************/




/******************************************************************
 * START Finally get everything running and accommodate Shutdown
 ******************************************************************/
function  CoreCreate()
{
    DebugGeneral("CoreCreate...", g_DEBUG_LEVEL_INFO);

    // No "var" here makes this GLOBAL!!!
    g_SERVER = new Server();
    //g_SERVER.ConstructEverything(); // No need to call this here as it gets called from the Feedback connection maintenance timer!

    // No "var" here makes this GLOBAL!!!
    gLIBRARY = new LibraryManager(g_SERVER);

    // Populate Library List in ALL RTI System Views (depends on Server)
    // No "var" here makes these GLOBAL!!!
    g_VIEW_IDS = Config.Get("SYSTEM::TwoWayDeviceList"); // 1 3 6 11
    g_VIEW_ARRAY = CopyArray_RemoveNullUndefEmptyString(g_VIEW_IDS.split(' ')); // ["1", "3", "6", "11"]
    for (var x = 0; x < g_VIEW_ARRAY.length; x++) {
        var view = g_VIEW_ARRAY[x].trim();
        if (view != '') {
			gLIBRARY.GetBrowser_fromViewID(view); //Library_GetBrowser_fromViewID(view);

			// Initialize Variables
			SysVar_WriteBool("SysVar_Keyboard_B_Display%" + view, false);
			UIVisibility_Set(view, 0);
		}
    }

    CoreStart();
}

function  CoreStart()
{
    DebugGeneral("CoreStart with IP = " + OperationalServerIP(), g_DEBUG_LEVEL_INFO);
    TCPFeedback_Start();    
    KeyboardTimer_Start();
    ServerTimer_Start();
}

function  CoreStop()
{
    DebugGeneral("CoreStop with IP = " + OperationalServerIP(), g_DEBUG_LEVEL_INFO);
    TCPFeedback_Stop();
    KeyboardTimer_Stop();
    ServerTimer_Stop();
}

function  OnSystemShutdown()
{
    DebugGeneral("OnSystemShutdown... stopping feedback et al.", g_DEBUG_LEVEL_TRACE);
    CoreStop();
}
System.OnShutdownFunc = OnSystemShutdown;

SysVar_Write('SysVar_Server_S_InternalDriverVersion', '23.7.12.0');
/******************************************************************
 * END   Finally get everything running and accommodate Shutdown
 ******************************************************************/
