/**********************************************************************
   $Author: greynold $
   $Date: 2006/07/14 20:56:15 $
   $Revision: 1.4 $


   Copyright 2001 - 2004. Surgient, Inc. All rights reserved. This 
   software code is an unpublished work and contains trade secrets of 
   Surgient, Inc. and is distributed only under license restrictions. No 
   part of this software code may be displayed, used, reproduced, stored 
   on a retrieval system, distributed, or transmitted without the 
   express written consent of Surgient, Inc. Violation of the provisions 
   contained herein may result in severe civil and criminal penalties, 
   and any violators will be prosecuted to the maximum extent possible 
   under the law. 
   
   "Surgient", "Virtualization Control Server" and "Surgient VCS" are 
   trademarks or registered trademarks of Surgient, Inc. in the United 
   States and foreign countries. 
**********************************************************************/
/// <summary>
/// crt.js contains the page level script objects needed to create and execute the 
/// crt test components.
/// </summary>
/// create a global logger instance
/// *** UNLESS YOU WISH TO CREATE A NEW LOGGER, JUST LET THE DEFAULT GLOBAL LOG INSTANCE
/// WORK:  log ***
log = LogFactory.getLogger(LogLevel.INFO);

/// <summary>
/// Global variable crtPath is similar to the appPath variable in URA only specific to 
/// the CRT web application.  Change this variable if the virtual directory for the CRT
/// application is different from the current value.
/// </summary>
var crtPath = "/CRT";

/// <summary>
/// Implementation class for the NetTest ActiveX object.  While not technically 
/// a TermClient, this class will implement most of the TermClient methods.
/// </summary>
/// <param name="id" type="string">the id of the TermClient instance</param>
function NetTest(id){
/// region : inheritance --------------------------------------------
    this.base = TermClient;
    this.base(id);
    log.info("NetTest object created.");

/// region : private fields -----------------------------------------
    this._ctlSuffix = "_ax";

/// region : Properties ---------------------------------------------

    /// <summary>
    /// Gets/sets the protocol scheme for this TermClient.
    /// </summary>
    this.Protocol = "tcp";

    /// <summary>
    /// Gets/sets the uprate bytes/sec for the NetTest instance.
    /// </summary>
    this.UpRateBps = null;

    /// <summary>
    /// Gets/sets the downrate bytes/sec for the NetTest instance.
    /// </summary>
    this.DownRateBps = null;

    /// <summary>
    /// Gets/sets the test interval the NetTest instance.
    /// </summary>
    this.RttInterval = null;

    /// <summary>
    /// Gets/sets the session count for the NetTest instance.
    /// </summary>
    this.SessionCount = null;

    /// <summary>
    /// Gets/sets the test duration for the NetTest instance in milliseconds.
    /// </summary>
    this.TestDuration = null;

    /// <summary>
    /// Gets/sets the TestResults for the CRTCoordinator.
    /// </summary>
    this.TestResults = null;

/// region : Methods ------------------------------------------------
    /// <summary>
    /// Method tells the TermClient to attempt to start the remote session. Raises 
    /// an OnConnect event.
    /// </summary>
    this.connect = _connect;
    function _connect(){
        try {
            // find our control and bind to it
            this._control = this.Element.getElementById(this.Id + this._ctlSuffix);
            if(this._control == null){
                log.error("NetTest unable to find rendered object tag in document.");
                throw this._newError("No download error.");
            }
            log.debug("NetTest._control = " + this._control);
            // now start the test
            if(this.getControl() != null && this.isConnected()){
                this._control.EnableFileLogging("c:\\temp\\Surgient_NetTest.log");
                this.startTest();
                // raise the OnConnect event
                //log.info("NetTest successfully connected and started test on host: " + this._hostURL);
                //this._raiseEvent("OnConnect", null);
            } else {
                if(this._connectCount < 40){
                    this._connectCount++;
                    window.setTimeout(this.Id + ".connect()", 1000);
                } else {
                    log.fatal("NetTest control failed to load.");
                    this._raiseEvent("OnLoadError", null);
                }
            }
        }
        catch (e)
        {
            if(e.message == "No download error."){
                log.fatal("NetTest control unable to initialize due to download or installation error.");
            } else {
                log.fatal("NetTest control suffered a fatal initialization error: " + e.message);
            }
            this._raiseEvent("OnConnectError", null);
        }
    }

    /// <summary>
    /// Callback event handler for the TermClient control if it supports the OnConnect event.
    /// </summary>
    this.controlConnectHandler = _controlConnectHandler;
    function _controlConnectHandler(){
        log.info("[" + this.Id + "]:  NetTest client received connect event from the ActiveX control.");
        // raise the OnConnect event
        this._raiseEvent("OnConnect", null);
    }

    /// <summary>
    /// Method tells the TermClient to attempt to disconnect from the remote session.
    /// </summary>
    this.disconnect = _disconnect;
    function _disconnect(args) {
        // raise the OnDisconnect event, passing the results up the chain
        try
        {
            var resultsxml = this._control.ResultsXml;
            resultsxml = resultsxml.replace(/\"/g, "&quot;");
            resultsxml = resultsxml.replace(/\r\n/g, "");
            resultsxml = resultsxml.replace(/\t/g, "");
            log.info("NetTest control successfully disconnected from: " + this._hostURL);
            this._raiseEvent("OnDisconnect", resultsxml);
        }
        catch (e)
        {
            log.error("NetTest control sufferred a fatal error attempting to retrieve ResultsXml: " + e.message);
            log.fatal("NetTest control was unable to retrieve sufficient data, indicating that the connection failed.");
            this._raiseEvent("OnConnectError", null);
        }
    }

    /// <summary>
    /// Method to conclude the test.
    /// </summary>
    this.concludeTest = _concludeTest;
    function _concludeTest(){
        this._control.ConcludeTest();
        log.info("NetTest successfully concluded test on host: " + this._hostURL);
    }

    /// <summary>
    /// Method called by the TermClient instance when a connection error has occurred.  Raises 
    /// an OnConnectError event.
    /// </summary>
    this.connectError = _connectError;
    function _connectError(error) {
        if(!this._isErrorState){
            // set the _isErrorState flag to true so that additional connect error messages are suppressed
            this._isErrorState = true;
            // raise the OnConnectError event
            log.error("NetTest failed to connect to host: " + this._hostURL + " with error: " + error);
            this._raiseEvent("OnConnectError", error);
        } else {
            log.info("NetTest suppressed additional connectError message from ActiveX control.");
        }
    }


    /// <summary>
    /// Method renders the TermClient's HTML to the target frame's document.  The method 
    /// of render varies for each implementation.
    /// </summary>
    this.renderHtml = _renderHtml;
    function _renderHtml() {
        log.debug("Enter NetTest.renderHtml()");
        var ctlid = this.Id + this._ctlSuffix;
        return '<object id="' + ctlid + '"' +
            'classid="CLSID:68D2D87D-8924-4EAE-8E98-9449D403E0EA"' +
            'codebase="' + crtPath + '/lib/nettest.cab#Version=1,0,0,1" viewastext>' +
            '</object>' +
            '<!-- event handler for nettest.OnStart() -->' +
            '<script type="text/javascript" for="' + ctlid + '" event="OnStart()">' +
            'log.debug("Enter NetTest.OnStart callback");' +
            'window.setTimeout("' + this.Id + '.concludeTest()", ' + this.TestDuration + ');' +
            this.Id + '.controlConnectHandler()' + 
            '</script>' +
            '<!-- event handler for nettest.OnError() -->' +
            '<script type="text/javascript" for="' + ctlid + '" event="OnError(what)">' +
            'log.debug("Enter NetTest.OnError callback");' +
            this.Id + '.connectError("Error from network test client " + what);' +
            '</script>' +
            '<script type="text/javascript" for="' + ctlid + '" event="OnStop()">' +
            'log.debug("Enter NetTest.OnComplete callback");' +
            this.Id + '.disconnect();' +
            '</script>';
    }

    /// <summary>
    /// Implementation of the init method from TermClient.  Sets up the properties 
    /// of the NetTest.
    /// </summary>
    /// <param name="upRateBps" type=""></param>
    /// <param name="downRateBps" type=""></param>
    /// <param name="rttInterval" type=""></param>
    /// <param name="sessionCount" type=""></param>
    /// <param name="testDuration" type=""></param>
    this.init = _init;
    function _init(upRateBps, downRateBps, rttInterval, sessionCount, testDuration){
        this.UpRateBps = upRateBps;
        this.DownRateBps = downRateBps;
        this.RttInterval = rttInterval;
        this.SessionCount = sessionCount;
        this.TestDuration = testDuration;
        log.info("NetTest initialized with UpRateBps:" + upRateBps + " DownRateBps:" + downRateBps + 
            " RttInterval:" + rttInterval + " SessionCount:" + sessionCount + " TestDuration:" + testDuration);
    }

    /// <summary>
    /// Method determines if the control is actually connected or not, helpful for polling 
    /// exercises and error checking in the URACoordinators.
    /// </summary>
    this.isConnected = _isConnected;
    function _isConnected(){
        if(typeof(this._control.StartSessions) != "undefined"){
            log.info("NetTest.isConnected returned: true");
            return true;
        } else {
            log.info("NetTest.isConnected returned: false");
            return false;
        }
    }

    /// <summary>
    /// Method actually attempts to start the NetTest.
    /// </summary>
    this.startTest = _startTest;
    function _startTest(){
        // now that the test has been properly loaded, run the tests
        this._control.DefaultDstAddr = this.HostAddress;
        log.info("NetTest host address set to: " + this.HostAddress);
        this._control.DefaultDstPort = this.HostPort;  
        log.info("NetTest host port set to: " + this.HostPort);
        if(this.HostPort == "") {
            this._hostURL = this.Protocol + "://" + this.HostAddress;
        } else {
            this._hostURL = this.Protocol + "://" + this.HostAddress + ":" + this.HostPort;
        }
        this._control.InitTest(this.UpRateBps, this.DownRateBps, this.RttInterval);
        log.info("NetTest InitTest called with: " + this.UpRateBps + ", " + this.DownRateBps + ", " + this.RttInterval);
        this._control.StartSessions(this.SessionCount);    
        log.info("NetTest StartSessions called with: " + this.SessionCount);
    }
}
// add the NetTest TermClient implementation to the TermClientType struct.  Please note that 
// unless the TermClient implementation is a one-off like NetTest, this approach should not be used.
// Rather, the actual struct should be updated with the new TermClientType.
TermClientType.NetTest = 99;
// add the NetTest TermClient protocol scheme to the TermClientProtocol struct.  Please note that 
// unless the TermClient implementation is a one-off like NetTest, this approach should not be used.
// Rather, the actual struct should be updated with the new TermClientProtocol.
TermClientProtocol.NetTest = "tcp";
// register this instance with the TermClientFactory
try{
    termClientFactory.registerTermClient(ControlPlatform.ActiveX, TermClientType.NetTest, "NetTest");
} catch (e) {
    alert('Error adding TermClient, listing all registered TermClients: ' + e.message);
}

//#######################################################################################################################
//#######################################################################################################################
//#######################################################################################################################

/// <summary>
/// CRTCoordinator is the page level mediator pattern which controls 
/// the interactions between the NetTest client and the URACoordinator + 
/// LLP objects.  It has event handlers for the various 
/// </summary>
/// <param name="id" type="string">the id for the instance</param>
function CRTCoordinator(id){
/// region : inheritance --------------------------------------------
    this.base = BaseObject;
    this.base(id);
    log.info("CRTCoordinator object created.");

/// region : private fields -----------------------------------------
    this._activeTermClientConfig = null;
    this._activeTunnelConfigSetting = null;
    this._configSettings = null;
    this._currentConfigSetting = 0;
    this._hostAddress = "";
    this._hostPort = "";
    this._hostURL = "";
    this._gatewayAddress = "";
    this._gatewayPort = "";
    this._gatewayURL = "";
    this._isInit = false;
    this._netTestArgs = new Array();
    this._loadFailSafe = 60000; // number of milliseconds to wait for a control to connect before it is re-rendered
    // dhtml fields
    this._termClientTargetId = "__tmc";
    this._tunnelConfigTargetId = "__tc";
    this._termClientTarget = null;
    this._tunnelConfigTarget = null;

/// region : Properties ---------------------------------------------

    /// <summary>
    /// Gets/sets the active TermClient.
    /// </summary>
    this.ActiveTermClient = null;
    
    /// <summary>
    /// Gets/sets the active TunnelConfig.
    /// </summary>
    this.ActiveTunnelConfig = null;

    /// <summary>
    /// Gets/sets the name of the target frame to render to.
    /// </summary>
    this.TargetFrameName = null;
    
    /// <summary>
    /// Gets/sets the form field used to postback the results of the test back to the server.
    /// </summary>
    this.PostBackFieldId = "";

/// region : Methods ------------------------------------------------

    /// <summary>
    /// Sets the array of URAConfigSettings.
    /// </summary>
    /// <param name="configSettings">array of <see cref="URAConfigSetting"/> settings to use</param>
    this.setConfigSettings = _setConfigSettings;
    function _setConfigSettings(configSettings){
        log.debug("CRTCoordinator configSettings set.");
        this._configSettings = configSettings;
    }

    /// <summary>
    /// Sets up the required properties of the CRTCoordinator for performing 
    /// all tests.
    /// <summary>
    this.setup = _setup;
    function _setup(upRateBps, downRateBps, rttInterval, 
        sessionCount, testDuration, postBackField){
        log.debug("CRTCoordinator setup with test values.");
        this.PostBackFieldId = postBackField;

        this._netTestArgs[0] = upRateBps;
        this._netTestArgs[1] = Math.round(parseInt(downRateBps) * 1.10);
        this._netTestArgs[2] = rttInterval;
        this._netTestArgs[3] = sessionCount;
        this._netTestArgs[4] = testDuration;

        this._isInit = true;
    }

    /// <summary>
    /// Gets the instance of the NetTest control and sets up the 
    /// CRTCoordinator to handle controlling the NetTest.
    /// </summary>
    this.init = _init;
    function _init(gatewayAddress, gatewayPort){
        // set the private properties
        this._gatewayAddress = gatewayAddress;
        if(typeof(gatewayPort) != "undefined"){
            if(gatewayPort != ""){
                this._gatewayPort = gatewayPort;
            }
        }
        log.info("CRTCoordinator initialized using GatewayAddress: " + this._gatewayAddress + 
            " and GatewayPort: " + this._gatewayPort);
        // assign the readyCount to 0 here
        this.readyCount = 0;
    }

    /// <summary>
    /// Noop function in CRTCoordinator.  Included for parity with URACoordinator.
    /// </summary>
    this.connect = _connect;
    function _connect(){
        log.info("CRTCoordinator attempting new connection.");
        try {
            // try to instantiate the controls based on the 
            // current config settings
            var config = this._configSettings[this._currentConfigSetting];

            // get the host address and port from the config setting
            this._hostAddress = config.HostAddress;
            this._hostPort = config.HostPort;

            
            // instantiate the TermClient (NetTest) control and bind its events
            this.ActiveTermClient = termClientFactory.getTermClient(this.Id + "_" + this._currentConfigSetting + "_tc", config.TermClientType, config.ControlPlatform);
            this.ActiveTermClient.OnConnect = this._getHandler("termClientConnectHandler");
            this.ActiveTermClient.OnDisconnect = this._getHandler("termClientDisconnectHandler");
            this.ActiveTermClient.OnConnectError = this._getHandler("termClientConnectErrorHandler");
            this.ActiveTermClient.OnLoadError = this._getHandler("termClientLoadErrorHandler");
            log.info("CRTCoordinator created new " + this.ActiveTermClient.getClass());

            //ActiveTermClient.init(upRateBps, downRateBps, rttInterval, sessionCount, testDuration)
            this.ActiveTermClient.init(this._netTestArgs[0], this._netTestArgs[1], this._netTestArgs[2], this._netTestArgs[3], this._netTestArgs[4]);        
            
            // instantiate the TunnelConfig control and bind its events
            this.ActiveTunnelConfig = tunnelConfigFactory.getTunnelConfig(this.Id + "_" + this._currentConfigSetting + "_tnl", config.TunnelConfigType, config.ControlPlatform);
            this.ActiveTunnelConfig.OnConnect = this._getHandler("tunnelConfigConnectHandler");
            this.ActiveTunnelConfig.OnDisconnect = this._getHandler("tunnelConfigDisconnectHandler");
            this.ActiveTunnelConfig.OnConnectError = this._getHandler("tunnelConfigConnectErrorHandler");
            this.ActiveTunnelConfig.OnLoadError = this._getHandler("tunnelConfigLoadErrorHandler");
            log.info("CRTCoordinator created new " + this.ActiveTunnelConfig.getClass());

            // set the connection properties on the TunnelConfig
            log.info("CRTCoordinator initializing the TunnelConfig and TermClient");
            this.ActiveTunnelConfig.GatewayAddress = this._gatewayAddress;
            this.ActiveTunnelConfig.HostAddress = this._hostAddress;
            this.ActiveTunnelConfig.HostPort = this._hostPort;
            if(this._gatewayPort != ""){
                this.ActiveTunnelConfig.GatewayPort = this._gatewayPort;
            }
            this.ActiveTunnelConfig.HostProtocol = this.ActiveTermClient.Protocol;
            

            // if the target elements to render the controls into don't exist yet, append them to the body
            if(this._termClientTarget == null){
                log.debug("CRTCoordinator appending TermClient target node.");
                var node = document.createElement("DIV");
                node.id = this._termClientTargetId;
                document.body.appendChild(node);
                this._termClientTarget = node;
            }
            if(this._tunnelConfigTarget == null){
                log.debug("CRTCoordinator appending TunnelConfig target node.");
                var node = document.createElement("DIV");
                node.id = this._tunnelConfigTargetId;
                document.body.appendChild(node);
                this._tunnelConfigTarget = node;
            }

            window.setTimeout(this.Id + ".pageReadyHandler()", 300);
        
        } catch (e) {
            log.fatal("CRTCoordinator sufferred a fatal error during the connect() method: " + e.message);
        }
    }

    /// <summary>
    /// Method called after a timeout once the DIV for containing the controls is written 
    /// to the page so that the browser has an opportunity to recognize it as a complete HTMLElement.
    /// </summary>
    this.pageReadyHandler = _pageReadyHandler;
    function _pageReadyHandler(){
        // clear any old controls in the target element
        this._tunnelConfigTarget.innerHTML = "";
        this._termClientTarget.innerHTML = "";

        this.ActiveTunnelConfig.Element = document;
        this.ActiveTermClient.Element = document;
        
        // write the HTML for the TunnelConfig
        var srcHtml = this.ActiveTunnelConfig.renderHtml();
        this._tunnelConfigTarget.innerHTML = srcHtml;
        log.info("CRTCoordinator successfully rendered TunnelConfig type: " + this.ActiveTunnelConfig.getClass() + " to page.");
        
        // initialize the TunnelConfig, once this has been initialized CRTCoordinator will
        // attempt to initialize the TermClient
        window.setTimeout(this.Id + "._initTunnelConfig()", 500);
    }

    /// <summary>
    /// Private method that is called after a fixed interval unless the URACoordinator receives an OnConnect or 
    /// OnConnectError event from the TermClient.  This is to fix an Win SP1 bug where ActiveX control event 
    /// wireup does not happen automatically after the control is initially downloaded.
    /// </summary>
    this._failSafeLoadHandler = _failSafeLoadHandler;
    function _failSafeLoadHandler(){
        // no activity was registered by the TermClient control so we can assume that there was a connectError event
        this._cancelInterval();
        log.info("[" + this.Id + "]:  CRTCoordinator failed to receive a timely OnConnect event from " + this.ActiveTermClient.getClass() + 
            " forcing OnConnectError event for the TermClient");
        this.termClientConnectErrorHandler();
    }

    /// <summary>
    /// Private method to handle the initialization of the TunnelConfig.
    /// </summary>
    this._initTunnelConfig = _initTunnelConfig;
    function _initTunnelConfig(){
        try{
            log.info("CRTCoordinator calling TunnelConfig.connect()");
            this.ActiveTunnelConfig.connect();
        } catch (e){
            log.error("CRTCoordinator could not call TunnelConfig.connect() due to error: " + e.message);
            this._raiseEvent("OnConnectError", null);
        }
    }

    /// <summary>
    /// Private method to initialize the TermClient control for the active URAConfigSetting.
    /// </summary>
    this._initTermClient = _initTermClient;
    function _initTermClient(){
        try{
            this.ActiveTermClient.HostAddress = this.ActiveTunnelConfig.getDestinationIPAddress();
            this.ActiveTermClient.HostPort = this.ActiveTunnelConfig.getDestinationPort();
            log.info("CRTCoordinator set TermClient HostAddress: " + this.ActiveTermClient.HostAddress + 
                " and HostPort: " + this.ActiveTermClient.HostPort);

            // generate the HTML for the TermClient and write it into the document
            var srcHtml = this.ActiveTermClient.renderHtml();
            this._termClientTarget.innerHTML = srcHtml;
            log.info("CRTCoordinator successfully rendered TermClient type: " + this.ActiveTermClient.getClass() + " to page.");

            window.setTimeout(this.Id + "._connectTermClient()", 500);
        } catch (e){
            log.error("CRTCoordinator could not render TermClient due to error: " + e.message);
            this._raiseEvent("OnConnectError", null);
        }
    }

    /// <summary>
    /// Private method that tries to make the ActiveTermClient control connect.
    /// </summary>
    this._connectTermClient = _connectTermClient;
    function _connectTermClient(){
        try {
            log.info("CRTCoordinator calling TermClient.connect()");
            this._setInterval(this.Id + "._failSafeLoadHandler()", this._loadFailSafe);
            this.ActiveTermClient.connect();
        } catch (e){
            log.error("CRTCoordinator could not call TunnelConfig.connect() due to error: " + e.message);
            this._raiseEvent("OnConnectError", null);
        }
    }

    /// <summary>
    /// Force aborts all CRT tests currently running.
    /// </summary>
    this.abort = _abort;
    function _abort(){
        log.info("CRTCoordinator aborting test.");
        this.ActiveTermClient.concludeTest();
        log.info("CRTCoordinator successfully called TermClient.concludeTest()");
        this.ActiveTunnelConfig.disconnect(null);
        log.info("CRTCoordinator successfully called TunnelConfig.disconnect()");
    }

/// region : Event Delegates ----------------------------------------

    /// <summary>
    /// Event delegate for the Connect event.
    /// </summary>
    this.OnConnect = null;

    /// <summary>
    /// Event delegate for the ConnectError event.
    /// </summary>
    this.OnConnectError = null;

    /// <summary>
    /// Event delegate for the Disconnect event.
    /// </summary>
    this.OnDisconnect = null;

    /// <summary>
    /// Event delegate for the LoadError event.
    /// </summary>
    this.OnLoadError = null;

/// region : Event Handlers -----------------------------------------
    /// <summary>
    /// Event handler for the TunnelConfig OnConnect event.
    /// </summary>
    /// <param name="sender" type="object">object raising the event</param>
    /// <param name="args" type="object">any event arguments</param>
    this.tunnelConfigConnectHandler = _tunnelConfigConnectHandler;
    function _tunnelConfigConnectHandler(sender, args){
        log.info("CRTCoordinator received OnConnect event from " + this.ActiveTunnelConfig.getClass());
        // if the TunnelConfig connected safely then try to initialize and connect the TermClient
        this._initTermClient();
    }

    /// <summary>
    /// Event handler for the TunnelConfig OnDisconnect event.
    /// </summary>
    /// <param name="sender" type="object">object raising the event</param>
    /// <param name="args" type="object">any event arguments</param>
    this.tunnelConfigDisconnectHandler = _tunnelConfigDisconnectHandler;
    function _tunnelConfigDisconnectHandler(sender, args){
        log.info("CRTCoordinator received OnDisconnect event from " + this.ActiveTunnelConfig.getClass());
        this._raiseEvent("OnDisconnect", null);
    }

    /// <summary>
    /// Event handler for the TunnelConfig OnConnectError event.
    /// </summary>
    /// <param name="sender" type="object">object raising the event</param>
    /// <param name="args" type="object">any event arguments</param>
    this.tunnelConfigConnectErrorHandler = _tunnelConfigConnectErrorHandler;
    function _tunnelConfigConnectErrorHandler(sender, args){
        // if the TunnelConfig did not connect, then cycle to the next TunnelConfig
        // available in the system and try to connect on that.
        log.info("CRTCoordinator received OnConnectError event from " + this.ActiveTunnelConfig.getClass());
        this._currentConfigSetting++;
        if(this._currentConfigSetting < this._configSettings.length){
            log.debug("CRTCoordinator connection failed, calling next URAConfigSetting");
            // force abort any controls and remove the references to the Javascript Objects after 
            // connect error to prevent any additional callbacks from breaking the thread
            this._tunnelConfigTarget.innerHTML = "";
            this._termClientTarget.innerHTML = "";
            this.ActiveTermClient = null;
            this.ActiveTunnelConfig = null;
            this.init(this._gatewayAddress, this._gatewayPort);
            this.connect();
        } else {
            // there are no more configuration settings to try so message a connect error
            log.error("CRTCoordinator failed to connect any TunnelConfigs successfully.");
            this._raiseEvent("OnConnectError", null);
        }
    }

    /// <summary>
    /// Event handler for the TunnelConfig LoadError event.  Noop in CRTCoordinator
    /// </summary>
    /// <param name="sender" type="object">object raising the event</param>
    /// <param name="args" type="object">any event arguments</param>
    this.tunnelConfigLoadErrorHandler = _tunnelConfigLoadErrorHandler;
    function _tunnelConfigLoadErrorHandler(sender, args){
        log.info("CRTCoordinator received OnLoadError event from " + this.ActiveTunnelConfig.getClass());
        this._raiseEvent("OnLoadError", null);
    }

    /// <summary>
    /// Event handler for the TermClient (NetTest) OnConnect event.
    /// </summary>
    /// <param name="sender" type="object">object raising the event</param>
    /// <param name="args" type="object">any event arguments</param>
    this.termClientConnectHandler = _termClientConnectHandler;
    function _termClientConnectHandler(sender, args) {
        log.info("CRTCoordinator received OnConnect event from " + this.ActiveTermClient.getClass()); 
        this._cancelInterval();
        this._raiseEvent("OnConnect", null);
    }

    /// <summary>
    /// Event handler for the TermClient (NetTest) OnDisconnect event.
    /// </summary>
    /// <param name="sender" type="object">object raising the event</param>
    /// <param name="args" type="object">any event arguments</param>
    this.termClientDisconnectHandler = _termClientDisconnectHandler;
    function _termClientDisconnectHandler(sender, args){
        log.info("CRTCoordinator received OnDisconnect event from " + this.ActiveTermClient.getClass()); 
        try {
            log.info("CRTCoordinator attempting to disconnect TunnelConfig: " + this.ActiveTunnelConfig.getClass());
            this.ActiveTunnelConfig.disconnect();
        }
        catch (e)
        {
            log.error("CRTCoordinator failed to disconnect TunnelConfig: " + this.ActiveTunnelConfig.getClass() + " with error: " + e.message);
        }

        // process the results for this page by taking the event args (test results)
        // and putting them in the hidden form field and submitting the form
        var elm = document.getElementById(this.PostBackFieldId);
        try {
            elm.value = args;
            log.info("CRTCoordinator successfully set test results field.");
        } catch (e) {
            log.error("CRTCoordinator failed to set test results field with error: " + e.message);
            return;
        }
        this._raiseEvent("OnDisconnect", "NetTest");
    }

    /// <summary>
    /// Event handler for the TermClient (NetTest) OnConnectError event.
    /// </summary>
    /// <param name="sender" type="object">object raising the event</param>
    /// <param name="args" type="object">any event arguments</param>
    this.termClientConnectErrorHandler = _termClientConnectErrorHandler;
    function _termClientConnectErrorHandler(sender, args){
        // if the TermClient did not connect, then cycle to the next TermClient
        // available in the system and try to connect on that.
        log.info("CRTCoordinator received OnConnectError event from " + this.ActiveTermClient.getClass());
        this._cancelInterval();
        this._currentConfigSetting++;
        if(this._currentConfigSetting < this._configSettings.length){
            log.debug("CRTCoordinator connection failed, calling next URAConfigSetting");
            // force abort any controls and remove the references to the Javascript Objects after 
            // connect error to prevent any additional callbacks from breaking the thread
            this._tunnelConfigTarget.innerHTML = "";
            this._termClientTarget.innerHTML = "";
            this.ActiveTermClient = null;
            this.ActiveTunnelConfig = null;
            this.init(this._gatewayAddress, this._gatewayPort);
            this.connect();
        } else {
            // there are no more configuration settings to try so message a connect error
            log.error("CRTCoordinator failed to connect any TermClients successfully.");
            this._raiseEvent("OnConnectError", null);
        }
    }

    /// <summary>
    /// Event handler for the TermClient LoadError event.  Noop in CRTCoordinator.
    /// </summary>
    /// <param name="sender" type="object">object raising the event</param>
    /// <param name="args" type="object">any event arguments</param>
    this.termClientLoadErrorHandler = _termClientLoadErrorHandler;
    function _termClientLoadErrorHandler(sender, args){
        log.info("CRTCoordinator received OnLoadError event from " + this.ActiveTermClient.getClass()); 
        this._raiseEvent("OnLoadError", null);
    }
}

// ------------------------------------------------------------------
// create a global instance of CRTCoordinator
var crtCoordinator = new CRTCoordinator("crtCoordinator");
