////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////

var __CFMULTISELECT_DELIMITER = '\n';

var __CFMULTISELECT_ERROR_ID = "invalid CFMultiSelect id";
var __CFMULTISELECT_ERROR_VALUE = "invalid multi-select value";
var __CFMULTISELECT_WARNING_VALUE = __CFMULTISELECT_ERROR_VALUE;

////////////////////////////////////////////////////////////////////////////////
// Static Variables
////////////////////////////////////////////////////////////////////////////////

var __cfMultiSelectMap = {};

////////////////////////////////////////////////////////////////////////////////
// Classes
////////////////////////////////////////////////////////////////////////////////

function CFMultiSelect(id, selectId, subSelectData, hiddenId, selectValue,
                       subSelectValue)
{
    CFWidget.call(this, id);
    var selectBox = cfElementGet(selectId);
    var subSelectMap = {};
    for (var key in subSelectData) {
        var subSelectId = subSelectData[key];
        subSelectMap[key] = subSelectId ? cfElementGet(subSelectId) : undefined;
    }
    this.__hiddenInput = cfElementGet(hiddenId);
    this.__selectBox = cfElementGet(selectId);
    this.__subSelectMap = subSelectMap;
    this.__visibleSubSelectBox = undefined;
    if (! this.setValue(selectValue, subSelectValue, true)) {
        cfWarningTrigger("CFMultiSelect: ('" + selectValue + "', '" +
                         subSelectValue + "'): " +
                         __CFMULTISELECT_WARNING_VALUE);
        this.setValue('', '');
    }
    __cfMultiSelectMap[id] = this;
    var f = cfEventHandlerCreate(this.__handleSelectBoxUpdate.bind(this));
    cfSelectSetChangeCallback(selectBox, f);
    f = cfEventHandlerCreate(this.__handleSubSelectBoxUpdate.bind(this));
    for (var key in subSelectMap) {
        var subSelectBox = subSelectMap[key];
        if (subSelectBox) {
            cfSelectSetChangeCallback(subSelectBox, f);
        }
    }
}

CFMultiSelect.extendClasses(CFWidget);

CFMultiSelect.prototype.__handleSelectBoxUpdate = function()
{
    var selectValue = cfSelectGetValue(this.__selectBox);
    var subSelectValue;
    if (! selectValue) {
        subSelectValue = undefined;
    } else {
        var subSelectBox = this.__subSelectMap[selectValue];
        subSelectValue = subSelectBox ? cfSelectGetValue(subSelectBox) :
                         undefined;
    }
    this.setValue(selectValue, subSelectValue);
    this.__triggerEvent(CFWIDGET_EVENT_USER_UPDATE);
}

CFMultiSelect.prototype.__handleSubSelectBoxUpdate = function()
{
    this.setValue(this.__selectValue,
                  cfSelectGetValue(this.__visibleSubSelectBox));
    this.__triggerEvent(CFWIDGET_EVENT_USER_UPDATE);
}

CFMultiSelect.prototype.__triggerValueError = function(value, subValue)
{
    return cfErrorTrigger("CFMultiSelect::__triggerValueError: (" + value +
                          ", " + subValue + "): " +
                          __CFMULTISELECT_ERROR_VALUE);
}

CFMultiSelect.prototype.disable = function()
{
    this.__selectBox.dislabed = true;
    var subSelectBox = this.__visibleSubSelectBox;
    if (subSelectBox) {
        subSelectBox.disabled = true;
    }
    CFWidget.prototype.disable.call(this);
}

CFMultiSelect.prototype.enable = function()
{
    this.__selectBox.disabled = false;
    var subSelectBox = this.__visibleSubSelectBox;
    if (subSelectBox) {
        subSelectBox.disabled = false;
    }
    CFWidget.prototype.enable.call(this);
}

CFMultiSelect.prototype.getValue = function()
{
    return {
        select: this.__selectValue,
        subSelect: this.__subSelectValue
    };
}

CFMultiSelect.prototype.setValue = function(selectValue, subSelectValue,
                                            ignoreError)
{
    var selectBox = this.__selectBox;
    var subSelectBox;
    var visibleSubSelectBox = this.__visibleSubSelectBox;
    if (! selectValue) {
        if (subSelectValue) {
            if (ignoreError) {
                return false;
            }
            return this.__triggerValueError(selectValue, subSelectValue);
        }
        cfSelectSetValue(selectBox, '', false);
        subSelectBox = undefined;
        if (visibleSubSelectBox) {
            cfElementAddClass(visibleSubSelectBox, CFELEMENT_HIDDEN_CLASS);
        }
    } else {
        cfSelectSetValue(selectBox, selectValue, false);
        var subSelectBox = this.__subSelectMap[selectValue];
        if (typeof(subSelectBox) == "undefined") {
            if (subSelectValue) {
                if (ignoreError) {
                    return false;
                }
                return this.__triggerValueError(selectValue, subSelectValue);
            }
            if (visibleSubSelectBox) {
                cfElementAddClass(visibleSubSelectBox, CFELEMENT_HIDDEN_CLASS);
            }
        } else {
            var oldSubIndex = subSelectBox.selectedIndex;
            var oldSubValue = (oldSubIndex == -1) ? undefined :
                              subSelectBox.options[oldSubIndex].value;
            if (! subSelectValue) {
                subSelectValue = '';
            } else if (! cfSelectSetValue(subSelectBox, subSelectValue, true)) {
                cfSelectSetValue(selectBox, this.__selectValue, true);
                cfSelectSetValue(subSelectBox, oldSubValue, true);
                if (ignoreError) {
                    return false;
                }
                return this.__triggerValueError(selectValue, subSelectValue);
            }
            cfSelectSetValue(subSelectBox, subSelectValue, false);
            if (selectValue != this.__selectValue) {
                if (visibleSubSelectBox) {
                    cfElementAddClass(visibleSubSelectBox,
                                      CFELEMENT_HIDDEN_CLASS);
                }
                cfElementRemoveClass(subSelectBox, CFELEMENT_HIDDEN_CLASS);
            }
        }
    }
    this.__hiddenInput.value = selectValue + __CFMULTISELECT_DELIMITER +
                               subSelectValue;
    this.__selectValue = selectValue || '';
    this.__subSelectValue = subSelectValue || '';
    this.__visibleSubSelectBox = subSelectBox;
    return true;
}

////////////////////////////////////////////////////////////////////////////////
// Public API
////////////////////////////////////////////////////////////////////////////////

function cfMultiSelectCreate(arg)
{
    return new CFMultiSelect(arg.id, arg.selectId, arg.subSelectData,
                             arg.hiddenId, arg.selectValue, arg.subSelectValue);
}

function cfMultiSelectGet(id)
{
    var select = __cfMultiSelectMap[id];
    if (! select) {
        return cfErrorTrigger("cfMultiSelectGet: '" + id + "': " +
                              __CFMULTISELECT_ERROR_ID);
    }
    return select;
}
