﻿var SelectiveExclusionDropDown = Class.create();

SelectiveExclusionDropDown.DefaultPromptText = "          ";
SelectiveExclusionDropDown.Chains = new Array();
SelectiveExclusionDropDown.PendingDataSources = new Array();
SelectiveExclusionDropDown.PendingRemoteCalls = 0;
SelectiveExclusionDropDown.IsRemoteCallHandlerInitialized = false;

SelectiveExclusionDropDown.prototype = {
    initialize: function (controlId, categoryId, promptTextEnabled, isImageGalleryOnly, 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();
        this._isImageGalleryOnly = false;
        this._isPendingDataSourceFilter = false;

        if (typeof (promptTextEnabled) !== "undefined")
            this._promptTextEnabled = promptTextEnabled;
        if (typeof (isImageGalleryOnly) !== "isImageGalleryOnly")
            this._isImageGalleryOnly = isImageGalleryOnly;

        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") {
            CascadingDropDown.CustomEventsEnabled = true;
            var onChangeEventId = CascadingDropDown.CustomEventsNamespace + this._controlId;
            Event.observe(document, onChangeEventId, this._doInitOnCascadingDropDownChange.bind(this));

            if (!SelectiveExclusionDropDown.IsRemoteCallHandlerInitialized) {
                SelectiveExclusionDropDown.IsRemoteCallHandlerInitialized = true;
                var completedEventId = "CascadingDropDown:RemoteContentCallCompleted";
                Event.observe(document, completedEventId, SelectiveExclusionDropDown.remoteCallCompleted);
            }
        }
    },

    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 () {        
        // log("initializing dropdown [" + this._controlId + "] ...");
        var el = this.get_element();

        // if this dropdown is populated, then we can setup the data source
        if (true == this._isPopulated()) {            

            // capture the list so we can reset and filter properly
            this._captureDataSource(el);

            // remove selected values from dropdowns and record client state
            this._filterDataSource(el);
        }

        // 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;

            // check for image gallery only fields
            if (this._isImageGalleryOnly && this._selectedOption != null) {
                var prefillLinkEl = $(el.id + "__uploadImageNoteLink");
                var textEl = prefillLinkEl != null ? prefillLinkEl.down("span") : null;
                if (textEl != null && textEl.innerHTML != this._selectedOption.text)
                    textEl.innerHTML = ShortenString(this._selectedOption.text);
            }
        }
    },


    _doInitOnCascadingDropDownChange: function () {
        // log("_onCascadingDropDownsChangeInit: for dropdown [" + this._controlId + "]");

        // if this dropdown is populated, then we can initialize
        if (true == this._isPopulated()) {
            var el = this.get_element();

            // capture the list so we can reset and filter properly
            this._captureDataSource(el);

            // we don't filter yet as other dropdowns haven't been loaded (we want to load all the ajax lists first).
            // then at the end, we'll rip through everything that was updated and filter appropriately.
            
            // this is like a work queue that tells the post processor to call a cleanup method at the end
            SelectiveExclusionDropDown.PendingDataSources.push(this);
        }
    },    

    _captureDataSource: function (el) {
        this._isPendingDataSourceFilter = true;

        // 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 + "]");
    },

    _filterDataSource: function (el) {
        this._isPendingDataSourceFilter = false;

        if (arguments.length == 0) {
            el = this.get_element();
        }

        // remove selected values from this dropdown
        if (false == this.get_isRoot() && el.options && el.options.length > 0) {
            // log("_filterDataSource: removing selected values from this dropdown");
            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("_filterDataSource: set selected value to {value:" + optionEl.value + ", text:" + optionEl.text + "}");

            var newValue = this.get_SelectedValue();
            this._removeItemFromAllDropDowns(newValue);
        }
    },  

    _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))) {
            // log("setting selected value to: [" + el.options[el.selectedIndex].value + "]");
            this.set_SelectedValue(el.options[el.selectedIndex].value, el.options[el.selectedIndex].text);
        } else {
            // log("setting selected value to blank");
            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 + "] - oldvalue=" + oldValue);

            // 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("_removeItem: removing " + newValue + " from dropdown " + this._controlId + ", CascadingDropDown.RemoteContentCallCounter==" + CascadingDropDown.RemoteContentCallCounter);
                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("_removeItemFromAllDropDowns: 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);               
    }       
};

SelectiveExclusionDropDown.remoteCallCompleted = function() {
    // log("_cascadingDropDownsRemoteContentCallCompleted");
    if (SelectiveExclusionDropDown.PendingRemoteCalls > 0)
        return;

    SelectiveExclusionDropDown.PendingRemoteCalls++;
    var dropDowns = SelectiveExclusionDropDown.PendingDataSources;
    for(var i = 0; i < dropDowns.length; i++) {
        var dropDown = dropDowns[i];

        if (dropDown._isPendingDataSourceFilter) {
            // log("_cascadingDropDownsRemoteContentCallCompleted: filtering dropdown [" + dropDown._controlId + "]");
            dropDown._filterDataSource();
        }
    }

    SelectiveExclusionDropDown.PendingDataSources.clear();
    SelectiveExclusionDropDown.PendingRemoteCalls--;
};

function _trim(stringToTrim) {
    return stringToTrim.replace(/^\s+|\s+$/g, "");
}
function _ltrim(stringToTrim) {
    return stringToTrim.replace(/^\s+/, "");
}
function _rtrim(stringToTrim) {
    return stringToTrim.replace(/\s+$/, "");
}

//var logInitialized = false;
//function // log(msg) {
//    if (!logInitialized) {
//        jQuery('body').append('<div id="divLog" style="height:200px;scroll:auto;"></div>');
//        logInitialized = true;
//    }
//    $("divLog").innerHTML += (msg + "<br />");
//}
