﻿var SelectiveExclusionDropDown = Class.create();

SelectiveExclusionDropDown.DefaultPromptText = "          ";
SelectiveExclusionDropDown.Chains = new Array();

SelectiveExclusionDropDown.prototype = {
    initialize: function(controlId, categoryId, promptTextEnabled, ignoreValues) {
        /// <summary>
        /// Initialize the drop-down behavior
        /// </summary>
        /// <returns />              
        this._isInitialized = false;
        this._isRoot = false;
        this._controlId = controlId;
        this._categoryId = categoryId;
        this._promptTextEnabled = true;
        this._promptText = SelectiveExclusionDropDown.DefaultPromptText;
        this._selectedOption = null;
        this._previousSelectedOption = null;
        this._dataSource = new Array();
        this._ignoreValues = new Array();

        if (typeof (promptTextEnabled) !== "undefined")
            this._promptTextEnabled = promptTextEnabled;

        if (typeof (SelectiveExclusionDropDown.Chains[this._categoryId]) == "undefined") {
            this._isRoot = true; // first field for this categoryId is the root
            SelectiveExclusionDropDown.Chains[this._categoryId] = new Array();
        }

        // add to the dropdown chain for this category (ie RuleId)
        SelectiveExclusionDropDown.Chains[this._categoryId].push(this);

        if (typeof (ignoreValues) !== "undefined") {
            for (var i = 0; i < ignoreValues.length; i++) {
                this.addIgnoreValue(ignoreValues[i]);
            }
        }

        // init this dropdown
        this._doInit();

        // register for dropdown list datasource changes
        if (typeof (CascadingDropDown) !== "undefined") {
            // log("registering cascading list change handler");
            CascadingDropDown.ListChangeHandlers[this._controlId] = this._doInit.bind(this);
            CascadingDropDown.ListChangeHandlersEnabled = true;
        }
    },

    get_element: function() {
        if (this._controlId.length < 1)
            return null;
        else
            return $(this._controlId);
    },

    get_CategoryId: function() {
        return this._categoryId;
    },

    get_isInitialized: function() {
        return this._isInitialized;
    },

    get_isRoot: function() {
        return this._isRoot;
    },

    get_PromptText: function() {
        return this._promptText;
    },
    set_PromptText: function(promptText) {
        this._promptText = promptText;
    },

    get_PromptTextEnabled: function() {
        return this._promptTextEnabled;
    },
    set_PromptTextEnabled: function(promptTextEnabled) {
        this._promptTextEnabled = promptTextEnabled;
    },

    get_SelectedValue: function() {
        if (this._selectedOption != null)
            return this._selectedOption.value;

        return null;
    },
    set_SelectedValue: function(value, text) {
        if (this._selectedOption == null || this._selectedOption.value != value) {
            if (!text) {
                // Initial population by server; look for "value:::text" pair
                var i = value.indexOf(':::');
                if (-1 != i) {
                    text = value.slice(i + 3);
                    value = value.slice(0, i);
                }
            }
            this._previousSelectedOption = this._selectedOption;
            this._selectedOption = { value: value, text: text };
        }
    },

    get_PreviousSelectedValue: function() {
        if (this._previousSelectedOption != null)
            return this._previousSelectedOption.value;

        return null;
    },
    get_PreviousSelectedText: function() {
        if (this._previousSelectedOption != null)
            return this._previousSelectedOption.text;

        return null;
    },

    addIgnoreValue: function(val) {
        val = _stripExternalId(_trim(val));
        this._ignoreValues[val] = true;

        if (val.toLowerCase() != val)
            this._ignoreValues[val.toLowerCase()] = true;
    },

    _doInit: function() {
        // if this dropdown is populated, then we can initialize      
        if (true == this._isPopulated()) {
            // log("initializing dropdown ["+this._controlId+"] ...");

            var el = this.get_element();

            // clear out any selected values in the client state
            this._selectedOption = null;
            this._previousSelectedOption = null;

            // clear out data source in client state to prepare for reload        
            this._dataSource.clear();

            // capture the list so we can reset and filter properly
            for (var i = 0; i < el.options.length; i++) {
                // don't save first dropdown blank item
                if (i == 0 && true == this._promptTextEnabled && _trim(el.options[i].value) == "")
                    continue;

                this._dataSource[this._dataSource.length] = { value: el.options[i].value, text: el.options[i].text };
            }
            // log("captured "+this._dataSource.length+" data source items for dropdown ["+this._controlId+"]");


            if (false == this.get_isRoot() && el.options && el.options.length > 0) {
                this._removeSelectedValuesFromThisDropDown();
            }

            // Record the selected value in the client state
            if (el.options && el.options.length > 0) {
                var optionEl = el.selectedIndex >= 0 ? el.options[el.selectedIndex] : el.options[0];
                this.set_SelectedValue(optionEl.value, optionEl.text);
                // log("set selected value to {value:"+optionEl.value+", text:"+optionEl.text+"}");

                var newValue = this.get_SelectedValue();
                this._removeItemFromAllDropDowns(newValue);
            }

            // Attach change handler to self
            if (!this._isInitialized) {
                // log("finishing initialization ... registering onChange handler for dropdown ["+this._controlId+"]");
                Event.observe(el, 'change', this._onChange.bindAsEventListener(this), false);
                this._isInitialized = true;
            }
        }
    },

    _onChange: function(evt) {
        if (!this.get_isInitialized()) {
            return;
        }

        /// <summary>
        /// Handler for the drop down's change event
        /// </summary>
        /// <returns />

        // get the HTML select element that is tied to this exclusion dropdown js object
        var el = this.get_element();
        // Record the selected value in the client state
        if ((-1 != el.selectedIndex) && !(true == this._promptTextEnabled && (0 == el.selectedIndex))) {
            this.set_SelectedValue(el.options[el.selectedIndex].value, el.options[el.selectedIndex].text);
        } else {
            this.set_SelectedValue('', '');
        }

        // oldValue was the previously selected value in this dropdown ;
        //   this needs to be added back to other dropdowns since it is no longer selected and is now available
        var oldValue = this.get_PreviousSelectedValue();
        var oldText = this.get_PreviousSelectedText();

        // newValue is the newly selected value in this dropdown ;
        //   this needs to be removed from other dropdowns since it is selected and thus no longer available
        var newValue = this.get_SelectedValue();

        // get the exclusion dropdowns and datasource info
        var isBlankItem = _trim(this.get_SelectedValue()) == "";
        var dropDowns = SelectiveExclusionDropDown.Chains[this._categoryId];

        // if the root exclusion list is set to blank, then everything gets reset
        if (true == this.get_isRoot() && true == isBlankItem) {
            SelectiveExclusionDropDown.resetAll(dropDowns);
            return;
        }

        // otherwise we selected a value in either the parent or child lists ;
        //   in this case we need to remove the newly selected value and add back the previous selection
        //   to all the other exclusion lists            
        for (var i = 0; i < dropDowns.length; i++) {
            var otherDDObj = dropDowns[i];

            // log("checking dropdown ["+otherDDObj._controlId+"]");

            // check if we're "resetting" this dropdown (then reset its original datasource)
            if (true == isBlankItem && this._controlId == otherDDObj._controlId) {
                otherDDObj.reset(true);
            } else if (this._controlId == otherDDObj._controlId)
                continue; // otherwise skip filter logic for this dropdown                               

            // remove newly selected value from the other dropdown
            otherDDObj._removeItem(newValue);

            // add back the previous selection to the other dropdown
            otherDDObj._addItem(oldValue, oldText);
        }
    },

    _clearItems: function() {
        var e = this.get_element();
        while (0 < e.options.length) {
            e.remove(0);
        }
    },

    _removeItem: function(newValue) {
        if (true == this._ignoreValue(newValue)) {
            return;
        }

        // get the HTML select element that is tied to the exclusion dropdown js object
        var el = this.get_element();

        // remove newly selected value from the other dropdown        
        for (var i = 0; i < el.options.length; i++) {
            if (el.options[i].value == newValue) {
                // log("removing "+newValue+" from dropdown "+this._controlId);
                el.remove(i);
                return true;
            }
        }

        return false;
    },

    _removeItemFromAllDropDowns: function(newValue) {
        if (true == this._ignoreValue(newValue)) {
            // log("nothing to remove from all dropdowns - value is empty");
            return;
        }

        var dropDowns = SelectiveExclusionDropDown.Chains[this._categoryId];
        for (var i = 0; i < dropDowns.length; i++) {
            var otherDDObj = dropDowns[i];

            // log("checking dropdown [" + otherDDObj._controlId + "]");

            // check if we're "resetting" this dropdown (then reset its original datasource)
            if (this._controlId == otherDDObj._controlId)
                continue; // otherwise skip filter logic for this dropdown                               

            // remove newly selected value from the other dropdown
            // log("removing value [" + newValue + "] from dropdown [" + otherDDObj._controlId + "]");
            otherDDObj._removeItem(newValue);
        }
    },

    _addItem: function(oldValue, oldText) {
        if (true == this._ignoreValue(oldValue)) {
            return;
        }

        // get the HTML select element that is tied to the exclusion dropdown js object
        var el = this.get_element();

        // add back the previous selection to the other dropdown        
        var addItem = false;

        // first ensure that this old item is available in the other dropdown's data source
        for (var i = 0; i < this._dataSource.length; i++) {
            if (this._dataSource[i].value == oldValue) {
                addItem = true;
                break;
            }
        }

        // next ensure this item is not already displayed
        if (true == addItem) {
            for (var i = 0; i < el.options.length; i++) {
                if (el.options[i].value == oldValue) {
                    addItem = false;
                    break;
                }
            }
        }

        if (true == addItem) {
            // log("adding "+oldValue+" back to dropdown "+this._controlId);

            var optionEl = new Option(oldText, oldValue);
            el.options[el.options.length] = optionEl;
        }

        return addItem;
    },

    _isPopulated: function() {
        /// <summary>
        /// Determine whether the drop down has any items
        /// </summary>
        /// <returns type="Boolean">
        /// Whether the drop down has any items
        /// </returns>        
        var items = this.get_element().options.length;
        if (true == this._promptTextEnabled) {
            return items > 1;
        } else {
            return items > 0;
        }
    },

    _ignoreValue: function(value) {
        if (value == null || value.length == 0)
            return true;

        var normalizedVal = _stripExternalId(_trim(value));
        var result = typeof (this._ignoreValues[normalizedVal]) !== "undefined"
                || typeof (this._ignoreValues[normalizedVal.toLowerCase()]) !== "undefined";

        return result;
    },

    _getSelectedValues: function() {
        var selectedValues = new Object();
        var otherDropDowns = SelectiveExclusionDropDown.Chains[this._categoryId];
        for (var i = 0; i < otherDropDowns.length; i++) {
            var otherDropDown = otherDropDowns[i];
            // don't check for selected value in this dropdown
            if (otherDropDown._controlId == this._controlId)
                continue;

            var otherValue = otherDropDown.get_SelectedValue();
            if (!this._ignoreValue(otherValue)) {
                selectedValues[otherValue] = otherDropDown._controlId;
                //// log("adding selectedValue "+otherValue+" for dropdown "+otherDropDown._controlId);
            }
            //else
            //  // log("ignoring value "+otherValue+" for dropdown "+otherDropDown._controlId);
        }
        return selectedValues;
    },

    _removeSelectedValuesFromThisDropDown: function() {
        var el = this.get_element();

        if (false == this.get_isRoot() && el.options && el.options.length > 0) {
            var selectedValue;
            var selectedValues = this._getSelectedValues(false);
            for (var selectedValue in selectedValues) {
                // log("removing value [" + selectedValue + "] from dropdown [" + this._controlId + "]");
                this._removeItem(selectedValue);
            }
        }
    },

    reset: function(removeSelectedOptions) {
        // log("resetting dropdown ["+this._controlId+"] ...");

        var el = this.get_element();

        // 1. clear out child list
        // log("clearing items");
        this._clearItems();

        // 2. clear out the selected values in the client state
        // log("resetting selected values in the client state");
        this._selectedOption = null;
        this._previousSelectedOption = null;

        // 3. Populate prompt text (if available)           
        if (true == this._promptTextEnabled) {
            // log("populating prompt text");
            var optionElement = new Option(this._promptText, "");
            el.options[el.options.length] = optionElement;
        }

        // 4. capture all the selected values for this chain of dropdowns ;
        //    these may not be available to this dropdown
        var selectedValues;
        if (true == removeSelectedOptions) {
            // log("removing selected values from this dropdown");
            selectedValues = this._getSelectedValues();
        }

        // 5. Add options from datasource
        // log("adding ["+this._dataSource.length+"] datasource items to dropDown "+this._controlId);
        for (var i = 0; i < this._dataSource.length; i++) {
            var addItem = true;
            var dsValue = this._dataSource[i].value;
            var dsText = this._dataSource[i].text;

            if (true == removeSelectedOptions) {
                if (typeof (selectedValues[dsValue]) !== "undefined") {
                    addItem = false;
                    // log("Not adding item "+dsValue+" since it is selected in another dropdown "+selectedValues[dsValue]);
                }
            }

            if (true == addItem) {
                // log("  adding item#"+el.options.length+" -- " + dsValue);
                var optionElement = new Option(dsText, dsValue);
                if (0 == el.options.length)
                    optionElement.selected = true;

                el.options[el.options.length] = optionElement;
            }
        }

        if (el.options.length > 0) {
            // log("Recording selected value in the client state");
            // this._removeSelectedValuesFromThisDropDown();
            this.set_SelectedValue(el.options[el.selectedIndex].value, el.options[el.selectedIndex].text);

            var newValue = this.get_SelectedValue();
            //// log("remove "+newValue +" from all dropdowns");
            this._removeItemFromAllDropDowns(newValue);
        }
    }
}

SelectiveExclusionDropDown.resetAll = function(dropDowns) {
    for (var i = 0; i < dropDowns.length; i++) {
        var dropDown = dropDowns[i];
        dropDown._clearItems();
        dropDown._selectedOption = null;
        dropDown._previousSelectedOption = null;
    }

    for (var i = 0; i < dropDowns.length; i++) {
        var dropDown = dropDowns[i];
        dropDown.reset(true);
    }
};

function _trim(stringToTrim) {
    return stringToTrim.replace(/^\s+|\s+$/g, "");
}
function _ltrim(stringToTrim) {
    return stringToTrim.replace(/^\s+/, "");
}
function _rtrim(stringToTrim) {
    return stringToTrim.replace(/\s+$/, "");
}