﻿// =========================================================================
// THIS CLASS IS USED TO WRAP ALL SERVER CALLS REQUIRING / PUSHING CONTENT
// TO / FROM THE SERVER.
//
// IT REQUIRES:
//
//   1. A NAME FOR THE REQUEST TYPE (FOR DISPLAYING IN ERROR MESAGES)
//
//   2. AN ARRAY OF Chat_DataRetrievals.DataRequestParameter OBJECTS TO
//      CONTAIN THE DATA TO PASS TO THE SERVER
//
//   3. A CALLBACK FUNCTION (SEE BELOW FOR ARGUMENTS THAT WILL BE PASSED)
//
//   4. MAX. No. OF ATTEMPTS TO MAKE FOR REQUEST
//
//   5. OPTIONALLY SPECIFY WHETHER OR NOT TO PERFORM AN ASYNCHRONOUS DATA
//      RETRIEVAL (DEFAULTS TO TRUE IF NOT SPECIFIED)
//
// THERE ARE LIMITS ON THE SERVER FOR THE NUMBER OF CONNECTIONS FROM ANY
// PARTICULAR IP ADDRESS OVER A PERIOD OF TIME. IF A REQUEST FALLS IN THIS
// "REFUSAL TIME" AND IF THE MAX. No. OF ATTEMPTS IS SPECIFIED > 1 THEN
// THE CLASS WILL WAIT A SHORT TIME AND THEN REPEAT. IF IT REPEATEDLY
// FAILS,
// =========================================================================

var Chat_DataRetrievals = new Object();

// =========================================================================
// WRAP SERVER CALLS
//  Callback Function receives following arguments:
//   - SuccessServerCall False if unable to communicate with server
//   - ErrCode           Null if above false, otherwise type Chat.ErrorCode
//   - Id                Connection Id (if successful)
//   - Info              String, may have error info or retrieved data
//   - RetVal            Int, may be null depending upon retrieval type
// =========================================================================
Chat_DataRetrievals.DataRequest = function(strRequestName, arrParameters, fncCallback, intMaxAttempts, bAsync)
{
    // Ensure request name specified
    if (!ObjectDetection.IsString(strRequestName))
    {
        alert("Chat_DataRetrievals.DataRequest: Invalid Request Name");
        return;
    }
    
    // Ensure parameters specified and valid
    var bValidParameters = true;
    if (!ObjectDetection.IsArray(arrParameters))
        bValidParameters = false;
    else
    {
        if (arrParameters.length == 0)
            bValidParameters = false;
        for (var intLoop = 0; intLoop < arrParameters.length; intLoop++)
        {
            if (!ObjectDetection.IsType(arrParameters[intLoop], Chat_DataRetrievals.DataRequestParameter))
            {
                bValidParameters = false;
                break;
            }
        }
    }
    if (!bValidParameters)
    {
        alert("Chat_DataRetrievals.DataRequest [" + strRequestName + "]: Invalid Parameter List");
        return;
    }
    
    // Ensure callback method specified
    if (!ObjectDetection.IsFunction(fncCallback))
    {
        alert("Chat_DataRetrievals.DataRequest [" + strRequestName + "]: Invalid fncCallback");
        return;
    }
    
    // Ensure max. attempts either valid or unspecified (in which case assume 1)
    if (ObjectDetection.IsUndefined(intMaxAttempts) || ObjectDetection.IsNull(intMaxAttempts))
        intMaxAttempts = 1;
    if ((!ObjectDetection.IsInteger(intMaxAttempts)) || (intMaxAttempts < 1))
    {
        alert("Chat_DataRetrievals.DataRequest [" + strRequestName + "]: Invalid Attempt Count");
        return;
    }

    // Optional parameter bAsync (perform asynchronous data retrieval) - default is true
    if (!ObjectDetection.IsBoolean(bAsync))
        bAsync = true;

    // Initiate a new connection attempt (in a new instance so it
    // can keep track of its own no. of request failures)
    new CConnectionRequest(strRequestName, arrParameters, fncCallback, intMaxAttempts, bAsync);

    function CConnectionRequest(strRequestName, arrParameters, fncCallback, intMaxAttempts, bAsync)
    {
        var _strRequestName = strRequestName;
        var _arrParameters = arrParameters;
        var _intMaxAttempts = intMaxAttempts;

        var _intFailures = 0;

        // Need a wrapper function around InitiateRequest so we can call repeat
        // the call - with bAsync value included - from a timer if we have to
        // resort to the "max attempts" timer
        var _InitiateRequestWithAsyncParameter = function()
                                                 {
                                                     InitiateRequest(bAsync);
                                                 };
        _InitiateRequestWithAsyncParameter();

        function InitiateRequest(bAsync)
        {
            var DataRequest = new Ajax.RequestHandler();
            DataRequest.SpecifyUrl("/OnlineChat/Main/ChatInterface.aspx");
            for (var intLoop = 0; intLoop < _arrParameters.length; intLoop++)
            {
                var strName = _arrParameters[intLoop].GetName();
                var strValue = _arrParameters[intLoop].GetValue();
                DataRequest.AddParameter(strName, strValue);
            }
            DataRequest.SpecifySuccessCallback(ServerCallComplete_Success);
            DataRequest.SpecifyFailureCallback(ServerCallComplete_Failure);
            DataRequest.MakeRequest(bAsync);
        }

        function ServerCallComplete_Success(xmlSrc)
        {
            // Try to translate returned data into javascript data object
            var objData = null;
            try
            {
                eval("objData = " + xmlSrc.responseText);
            }
            catch(e)
            {
                ServerCallComplete_Failure(xmlSrc, "Invalid content received from Server.");
                return;
            }
            
            // If ErrCode is not zero (and so an error occured), try up
            // to three times before giving up
            if ((!ObjectDetection.IsInteger(objData.ErrCode) || (objData.ErrCode != 0)))
            {
                // Keep trying if failed on ErrorCode.TimeLimit less than the 
                // the specified no. of repeats
                if ((objData.ErrCode == Chat.ErrorCode.TimeLimit)
                 && (_intFailures < _intMaxAttempts))
                {
                    _intFailures++;
                    setTimeout(_InitiateRequestWithAsyncParameter, 500);
                    return;
                }
                // If exceeded maximum attempts, return failure
                // (Return success for Server call, but return ErrorCode to describe
                // failure. If none specified, assume TimeLimit).
                if (!ObjectDetection.IsInteger(objData.ErrCode))
                    objData.ErrCode = Chat.ErrorCode.TimeLimit;
                fncCallback(true, objData.ErrCode, null, null, null);
                return;
            }

            // If got to here, then we must have successfully obtained a connection
            fncCallback(true, Chat.ErrorCode.None, objData.Id, objData.Content, objData.RetVal);
        }
 
        function ServerCallComplete_Failure(xmlSrc, strError)
        {
            // SuccessServerCal, ErrorCode, Id, Info, RetVal
            var strErrMsg = _strRequestName + ": Server Connection failed.\n\n" + strError;
            fncCallback(false, null, null, strErrMsg, null);
        }
    }
}

// =========================================================================
// OBJECT USED IN ABOVE WRAPPER -
// SPECIFY DATA REQUEST PARAMETERS
// =========================================================================
Chat_DataRetrievals.DataRequestParameter = function(strName, strValue)
{
    // Ensure input values specified
    if (!ObjectDetection.IsString(strName))
    {
        alert("Chat_DataRetrievals.DataRequestParameter: Invalid Name");
        return;
    }
    if (ObjectDetection.IsUndefined(strValue) || ObjectDetection.IsNull(strValue))
    {
        alert("Chat_DataRetrievals.DataRequestParameter: Invalid Value");
        return;
    }
    
    // Record values 
    var _Name = strName;
    var _Value = strValue;
    
    // Allow retrieval of info
    this.GetName  = function() { return _Name; };
    this.GetValue = function() { return _Value; };
}
