// @ts-check
// Fusion Research Driver - Music Module - Sync Management
//
// Copyright 2013-2021 Fusion Research, Inc.
// by Alexa Jade Fanti
//
// Comments:
//   This contains global variables and functions to support discovering the server based on
//   the hardware serial number specified by the integrator in the configuration and determining
//   the IP address of the server.
//
//   In the future we may want to update this to continuously discover the server so if the 
//   IP address were to change we could respond by using a different IP address and re-loading 
//   or re-configuring parts of the driver (i.e. feedback connections) to continue operation 
//   even though the I_P address changed in real time. This would require continuous multicast 
//   effort though and seems unlikely to be productive.
//
// Depends: 
//   fusion_debugging.js
//
//System.Print("Fusion Driver - Music Module - Server Discovery Tool loaded...\r\n");


//*************** START  Utilities ***************
// This utility funcs servers on the subnet and gets the 
// IP of the server as specified by hardware serial number.
//


/******************************************************************
 * START Server Global Variables and Functions
 ******************************************************************/
// This is the IP address of the server we discover using UDP broadcast!
var G_DiscoveredServerIP = "";


// This is the function we provide for ANYONE wanting to know the IP Address
// of the music server. It will return the configuration's IP address if that
// was set, otherwise the IP address discovered via UDP.
function  OperationalServerIP()
{
    if (G_DiscoveredServerIP != '') return G_DiscoveredServerIP; // If we have it... return it!

    var ip = ReadConfig_AsString("ServerIPAddress", "");
    if (ip != '') return ip;
    // Else return what we have!
    return G_DiscoveredServerIP;
}


// This is a global timer to "discover" the servers on the subnet.
var G_DiscoverTimer = new Timer();

// This is the UDP multicast component we'll use to make a broadcast and receive responses
var G_DiscoverUDP = new MulticastUDP(onDiscoverUDP_RX, MakeBCastIP(System.IPNetMask, System.IPAddress), 9720);

// This is the event handler called when the UDP component receives a packet.
// We parse it looking for the hardware serial number of the server and if found
// we set the global IP address...
function onDiscoverUDP_RX(ip, port, data) {
    DebugServerDiscovery("onDiscoverUDP_RX received packet from " + ip + ":" + port + "...", g_DEBUG_LEVEL_TRACE);

    // dbg("\n\n -------------------------------------------- onUDPRx()");
    // dbg("ip = " + ip);
    // dbg("port = " + port);
    // dbg(Util.toString(data, data.length, "HEXDUMPLOWER"));

    // Make the xml
    var xData = new XML(data.substr(data.indexOf("?>") + 2));
    var thisPlayerSN = (xData.discovered_oms.@hardware_serial_number);

    var target = ReadConfig_AsString("ServerHWSerialNo", "");
    DebugServerDiscovery("onDiscoverUDP_RX looking for " + target, g_DEBUG_LEVEL_TRACE);

    if (thisPlayerSN == target) {
        DebugServerDiscovery("Found target server (HWSN = " + thisPlayerSN + ") at " + ip, g_DEBUG_LEVEL_TRACE);

        if (G_DiscoveredServerIP == '') {
            // We have no IP so we're just starting...
            G_DiscoveredServerIP = ip; // Record IP of desired server
            DebugServerDiscovery("Starting system with server IP of " + G_DiscoveredServerIP, g_DEBUG_LEVEL_INFO);
            CoreCreate();
        } else if (G_DiscoveredServerIP != ip) {
            // We're changing IP addresses!
            CoreStop();
            G_DiscoveredServerIP = ip; // Record IP of desired server
            DebugServerDiscovery("RE-Starting system with server IP of " + G_DiscoveredServerIP, g_DEBUG_LEVEL_INFO);
            CoreStart();
        }
    } else if (thisPlayerSN != '') {
        DebugServerDiscovery("Found '" + thisPlayerSN + "' at " + ip, g_DEBUG_LEVEL_TRACE);
    }
}

function DiscoverServer() {
    var discoveryPacket = "Discover";
    DebugServerDiscovery('Sending discover packet...', g_DEBUG_LEVEL_TRACE);
    G_DiscoverUDP.Write(discoveryPacket);

    // try again soon
    G_DiscoverTimer.Start(DiscoverServer, 30 * 1000);
}


// This us a network utility to determine the network broadcast address 
// from the controller's IP address and network mask.
// Provided by Paut @ RTI
function MakeBCastIP(mask, address) {
    var maskArray = mask.split(".");
    var addrArray = address.split(".");
    var rtnArray = [];

    for (var quad in maskArray) {
        rtnArray[quad] = (addrArray[quad] | (~maskArray[quad]) + 256).toString();
    }

    return rtnArray[0] + "." + rtnArray[1] + "." + rtnArray[2] + "." + rtnArray[3];
}
