

function CallbackBus(action, func)
{
    var gameName = $("gameRoomTxtBox").value;
    var playerName = $("usernameTxtBox").value;

    CallbackText(root+"ServerBus.aspx", action+"\n"+gameName+"^"+playerName, func);
}

function CallbackBusSync(action)
{
    var gameName = $("gameRoomTxtBox").value;
    var playerName = $("usernameTxtBox").value;

    return CallbackSync(root+"ServerBus.aspx", action+"\n"+gameName+"^"+playerName);
}

function CallbackBusEx(action, request, func)
{
    var gameName = $("gameRoomTxtBox").value;
    var playerName = $("usernameTxtBox").value;

    CallbackText(root+"ServerBus.aspx", action+"\n"+gameName+"^"+playerName+"\n"+request, func);
}

function CallbackBusSyncEx(action, request)
{
    var gameName = $("gameRoomTxtBox").value;
    var playerName = $("usernameTxtBox").value;

    return CallbackSync(root+"ServerBus.aspx", action+"\n"+gameName+"^"+playerName+"\n"+request);
}


/*
    For users to be in constant communication, a pseudo connection must be made.
    Browsers cannot open direct network connections to each other.  But we
    can create a middle-man relay (ServerBus.aspx) and have all browsers 
    send their state information and receieve other browser's state information
    on a continuous 2-second schedule.  I dub this a "bus" since its a bit like
    a reliable Germany inner-city transit system, and its a lot like the the
    computer BUS, which is a clock-controlled exchange of data between the CPU
    and Memory Sticks.  The latter term is actually an acronym that stands for:
        Bidirectional   (yes, defintely)
        Universal       (well, if you mean "doesn't care about what kind of data its shuttling along", then yes)
        Switch          (relay,switch,whatever)

    The BUS facilities a "Message Pump" that gives the illusion of a direct
    connection.

    FYI, I started this at a small hotel in Cologne, Germany and finished it 
    two weeks latter on a plane from Dallas to Denver.  I wrote it for Celia.
 */


var bus;

function IsConnected()
{
    return (bus != null);
}

function ConnectDisconnect()
{
    $("connectButton").disabled = true;

    if (!bus)
    {
        $("usernameTxtBox").disabled = true;
        $("gameRoomTxtBox").disabled = true;

        var gameName = $("gameRoomTxtBox").value;
        var playerName = $("usernameTxtBox").value;

        $("statusDiv").innerText = "Connecting...";
 
        CallbackBus("joinGame", function(txt)
        {
            var errorPrefix = "================= ERROR ===================: ";
            if (txt.length > errorPrefix.length && txt.substr(0, errorPrefix.length) == errorPrefix)
            {
                alert( txt.substr(errorPrefix.length) );
                $("statusDiv").innerText = "Failed to Connect";
                $("usernameTxtBox").disabled = false;
                $("gameRoomTxtBox").disabled = false;
                $("connectButton").disabled = false;
                return;
            }

            Player.ClearAll();

            if (txt.length > 0)
            {
                // First create the other players (in order)
                var players = txt.split(/\n/);
                for (var inx = 0; inx < players.length; inx++)
                    Player.Create( players[inx] ).Init( /*isCreator*/inx == 0 );

                // Then create you.
                whoAmI = Player.Create( playerName );
                whoAmI.Init();
            }
            else
            {
                whoAmI = Player.Create( playerName );
                whoAmI.Init(true);
            }

            mainMenu.Select(1);

            lastSendDate = new Date();

            // Start the Message Pump, for a given Game Name and User Name 
            // (which is why those 2 TextBoxes msut be disabled, don't want to change them in the middle of the pump.)
            bus = new Bus( root+"ServerBus.aspx", onSend, onReceive );
            $("connectButton").innerText = "Disconnect";
            $("connectButton").disabled = false;
        });
    }
    else
    {
        // Stop the pump, and let the server know we left.
        bus.stop();
        bus = null;

        CallbackBus("leaveGame", function()
        {
            Player.ClearAll();

            $("usernameTxtBox").disabled = false;
            $("gameRoomTxtBox").disabled = false;
            $("connectButton").disabled = false;
            $("connectButton").innerText = "Connect";
        });
    }
}



function onSend()
{
    lastSendDate = new Date();

    var msg = $("gameRoomTxtBox").value +"^"+ $("usernameTxtBox").value;
    var len = actions.length;
    for (var inx = 0; inx < len; inx++)
    {
        var a = actions.shift();
        msg += "\n"+a.Text();
    }
    return msg;
}

function onReceive( responseText )
{
    $("statusDiv").innerText = "Connected";

    if (responseText.length==0)
        return;

    var messageId = _nextMessageId++;

    var array = responseText.split(/\n/);
    var uid = array[0];

    var actions = new Array();
    for (var inx = 0; inx < array.length; inx++)
    {
        var a = Action.FromText( array[inx] );
        actions.push(a);
    }

    var catchUp = false;
    if ( actions.length > 1)
    {
        var timeSpan = actions[actions.length-1].millisec - actions[0].millisec;
        
        // If more then 6 secs worth of activety needs to be played, then either 
        // there was some network lag, or we just connected and there's a backlog 
        // of activety from the other user.  In either case, we need to catch-up
        // and play out all this activety quickly (immediate/sequential).
        if ( timeSpan > 6 * 1000 ) 
            catchUp = true;
    }

    PlayMessage_WhenItsYourTurn( messageId, actions, (catchUp) ? PlayMessage_Immediately : PlayMessage_RealTime );
}


// Damn I'm good.
function PlayMessage_WhenItsYourTurn( messageId, actions, func )
{
    // Messages have to be played synchronosly.  We cannot start
    // a message while another is still executing its actions.
    // Wait until its you're turn. (this is the best way I can think of to implement sleep in jscript)
    var timerId = setInterval(
            function()
            {
                if ( _currentlyPlaying+1 == messageId && _pendingActionsFromPreviousMessage == 0 )
                {
                    clearInterval( timerId );
                    func( messageId, actions  );
                }
            }
            ,40 
        );
}
    var _nextMessageId = 0;
    var _currentlyPlaying = -1;
    var _pendingActionsFromPreviousMessage = 0;


function PlayMessage_Immediately( messageId, actions )
{
    _pendingActionsFromPreviousMessage = actions.length;
    _currentlyPlaying = messageId;

    for (var inx = 0; inx < actions.length; inx++)
    {
        var a = actions[inx];
        a.Play( false );
    }

    _pendingActionsFromPreviousMessage = 0;
}


function PlayMessage_RealTime( messageId, actions  )
{
    // Sequence our actions.  (We should have exclusive access to _nextActionID).
    for (var inx = 0; inx < actions.length; inx++)
    {
        var a = actions[inx];
        a.actionID = _nextActionID++;
    }

    // Start this message.
    _pendingActionsFromPreviousMessage = actions.length;
    _currentlyPlaying = messageId;

    for (var inx = 0; inx < actions.length; inx++)
    {
        var a = actions[inx];
        setTimeout( 
            function(a)
            {
                return function()
                {
                    // Wait until its you're turn.
                    var timerId = setInterval(
                            function()
                            {
                                if ( _completedAction+1 == a.actionID )
                                {
                                    clearInterval( timerId );
                                    _PlayMessage_RealTime_ActualPlay( a );
                                }
                            }
                            ,10
                        );
                }
            }(a)
            ,a.millisec
        );
    }
}
    var _nextActionID = 0;

    function _PlayMessage_RealTime_ActualPlay(a)
    {
        a.Play( false );
        _pendingActionsFromPreviousMessage--;
    }




