var     commandHandlers = { };
var		recalcFunctions = { };
var		postRecalcEventHandlers = [];

commandHandlers["setValue"] = function (aCommand) {
        $("#" + aCommand.id).html (aCommand.value);
    };

commandHandlers["addClass"] = function (aCommand) {
        $("#" + aCommand.id).addClass (aCommand.className);
    };

commandHandlers["removeClass"] = function (aCommand) {
        $("#" + aCommand.id).removeClass (aCommand.className);
    };

commandHandlers["setClass"] = function (aCommand) {
        $("#" + aCommand.id).each (function (index, domElement) { 
                domElement.className = aCommand.className; 
            });
    };

commandHandlers["rebuildSelect"] = function (aCommand, commandContext) {
        var $theSelect = $("#" + aCommand.id);
        var theVal = $theSelect.val();
        var optionsByGroup = aCommand.optionsByGroup;
        
        $theSelect.find ("*").remove ();
        
        for (var groupName in optionsByGroup)
        {
            var options = optionsByGroup[groupName];
            var $addOptionsTo = $theSelect;
            
            if (groupName != "")
            {
                $addOptionsTo = $("<optgroup label='" + groupName + "'></optgroup>").appendTo ($theSelect);
            }
            
            for (var x = 0 ; x < options.length ; ++x)
            {
                var optionVal = options[x];
                
                $addOptionsTo.append ("<option value='" + optionVal.val + "'>" + optionVal.desc + "</option>");
            }
        }            
    
        $theSelect.val(theVal);
        
        if ($theSelect.val() != theVal) // The value must no longer be valid, which means the select will have changed.  Force another recalc.
    	{
    	    commandContext.recalcAgain = $theSelect;
    	}
    };


commandHandlers["alert"] = function (aCommand) {
        window.alert (aCommand.msg);
    };


/*
 * Add a JS function to be called after recalc processing is complete.
 * There is one parameter, the jquery of the field that triggered the recalc.
 * For example:
 * addPostRecalcHandler (function ($fieldChanged) {
 *       if ($fieldChanged.has (".flightSelector"))
 *       {
 *           // Copy the recalc into the edit field
 *           var   defaultTime = $("div#defaultTime").text ();
 *           
 *           $(".transferTime").val (defaultTime);
 *       }
 *   });
 */
function addPostRecalcHandler (recalcHandler)
{
	postRecalcEventHandlers.push (recalcHandler);
}


function executeCommands (commands, commandContext)
{
    for (var x = 0 ; x < commands.length ; ++x)
    {
        var aCommand = commands[x];
        var commandType = aCommand.type;

        commandHandlers[commandType] (aCommand, commandContext);
    }            
}

function setupRecalc (selector, options)
{
    options = $.extend ({ page: contextRoot + '/genericRecalc.jsp' }, options);
    
    var     $theForm = $(selector).closest ('form');
    var     recalcFunction;
    
    recalcFunction = function ($fieldThatChanged) { performRecalc (options.page, selector, $fieldThatChanged); };
    recalcFunctions[$theForm.attr("id")] = recalcFunction;

    $theForm.delegate("input, select, textarea", 'change autocompletechange', function (e) {
    	if (!$(this).hasClass('norecalc'))
    	{
    		recalcFunction($(this));
    	}
    });    
        
    addPostAjaxAddHTMLModifiers (function (jqHtmlAdded) {   // This is called with the JQ of the elements added
            var $formAddedInto = jqHtmlAdded.closest ('form');
            
            if ($formAddedInto.get(0) == $theForm.get(0))
            {
                jqHtmlAdded.find("input, select, textarea").change (function () {
                        if (!$(this).hasClass('norecalc'))
                        {
                            recalcFunction();
                        }
                    });
            }
        });    
    
    return recalcFunction;
}


function performRecalc (page, selector, $fieldThatChanged)
{
    log("performRecalc", selector);
    
    // lockout to prevent parallel ajax requests
    if (formSubmitting)
    {
        log("performRecalc form was submitting");
        return;
    }
    else if(isAJAXInProgress ())
    {
        log("performRecalc AJAX was in progress");
        
        // Defer this request and start it later
        deferAJAXCall (function ()  { performRecalc (page, selector, $fieldThatChanged); });
        return;
    }
    
    startAJAXCall ();
    
    var reqParams = getAllFields(selector, { FAIL_VALIDATION_ERRORS: "false", PROCESS_FAIL_ROLLBACK: "true" });
    
    ajaxParams (selector, reqParams);

    $.ajaxSetup({"error":function() 
        {
            endAJAXCall ();
            return;
        }});

    $.getJSON (page, reqParams, function (data)
        {
            if (data && data.stage)
            {
                var commands = data.additional.commands;
                var procStage   = data.stage;
                var aForm = $(selector).closest('form');
                var commandContext = {};

                //aForm.find("input[name = 'proc.stage']").val (procStage);

                executeCommands (commands, commandContext);

                if (commandContext.recalcAgain)
                {
                    performRecalc (page, selector, commandContext.recalcAgain);
                }

                for (var x = 0 ; x < postRecalcEventHandlers.length ; ++x)
                {
                    postRecalcEventHandlers[x]($fieldThatChanged);
                }
            }
            else
            {
                if (window.console)
                {
                    window.console.log("Error processing recalc");
                }
            }
            
            endAJAXCall ();
        });
            
    return false;
}
