/*globals getImageDir */
/*jslint forin:true, undef:true, widget:true, browser:true */

/**
 * This class defines the controller for the list picker widget.  This widget allows
 * a user to pick multiple values from a list of values.
 *
 * @param baseName the unique name for the instance of the list picker.
 */
function ListPicker(baseName)
{
    this.baseName = baseName;

    // Obtain references to the elements that will be used in this control
    this.availableOptions = document.getElementById("all_" + this.baseName);
    this.selectedOptions = document.getElementById("selected_" + this.baseName);
    this.rightToLeftButton = document.getElementById("rtl_" + this.baseName);
    this.leftToRightButton = document.getElementById("ltr_" + this.baseName);
    this.upButton = document.getElementById("up_" + this.baseName);
    this.downButton = document.getElementById("down_" + this.baseName);
    this.alphaButton = document.getElementById("alpha_" + this.baseName);

    this.selectedOptions.onchange = this.createSelectOnChangeHandler(this);
    this.availableOptions.onchange = this.createSelectOnChangeHandler(this);
    this.mediator();
    this.onchange = null;

    ListPicker.instances[baseName] = this;
}

/**
 * defines the images used in the buttons on the page 
 */
ListPicker.rightArrowImage = getImageDir() + "/listPicker/1rightarrow.png";
ListPicker.rightArrowImageDisabled = getImageDir() + "/listPicker/1rightarrow_disabled.png";
ListPicker.leftArrowImage = getImageDir() + "/listPicker/1leftarrow.png";
ListPicker.leftArrowImageDisabled = getImageDir() + "/listPicker/1leftarrow_disabled.png";
ListPicker.upArrowImage = getImageDir() + "/listPicker/1uparrow.png";
ListPicker.upArrowImageDisabled = getImageDir() + "/listPicker/1uparrow_disabled.png";
ListPicker.downArrowImage = getImageDir() + "/listPicker/1downarrow.png";
ListPicker.downArrowImageDisabled = getImageDir() + "/listPicker/1downarrow_disabled.png";
ListPicker.alphabetizeImage = getImageDir() + "/listPicker/2downarrow.png";
ListPicker.alphabetizeImageDisabled = getImageDir() + "/listPicker/2downarrow_disabled.png";

/**
 * A map of all of the instances of the list picker
 */
ListPicker.instances = [];

/**
 * Generates an onclick handler for the option elements
 * @param listPicker the list picker object used in the onclick handler
 */
ListPicker.prototype.createSelectOnChangeHandler = function(listPicker)
{
    return function(event)
    {
        listPicker.onchange_select(listPicker);
    };
};
/**
 * Event handler for clicking on options in the select boxes.  It calls
 * the mediator method to enable or disable the buttons.
 * @param event the event object, not used
 * @param listPicker the list picker object.
 */
ListPicker.prototype.onchange_select = function(listPicker)
{
    listPicker.mediator();
};


/**
 * Determines if the select box has any selections
 * @param selEle the select Box Element
 */
ListPicker.prototype.hasSelections = function(selEle)
{
    return this.countSelected(selEle) > 0;
};

/**
 * Counts the numver of selections in the given select box
 * @param selEle
 */
ListPicker.prototype.countSelected = function(selEle)
{
    var ret = 0;
    var i;

    for (i = 0; i < selEle.options.length; ++i)
    {
        if (selEle.options[i].selected)
        {
            ++ret;
        }
    }

    return ret;
};

/**
 * Sets the image on the buttons based on the disabled state of the buttons
 */
ListPicker.prototype.updateImages = function()
{
    this.rightToLeftButton.getElementsByTagName("img")[0].src = this.rightToLeftButton.disabled ? ListPicker.leftArrowImageDisabled : ListPicker.leftArrowImage;
    this.leftToRightButton.getElementsByTagName("img")[0].src = this.leftToRightButton.disabled ? ListPicker.rightArrowImageDisabled : ListPicker.rightArrowImage;
    this.upButton.getElementsByTagName("img")[0].src = this.upButton.disabled ? ListPicker.upArrowImageDisabled : ListPicker.upArrowImage;
    this.downButton.getElementsByTagName("img")[0].src = this.downButton.disabled ? ListPicker.downArrowImageDisabled : ListPicker.downArrowImage;
    this.alphaButton.getElementsByTagName("img")[0].src = this.alphaButton.disabled ? ListPicker.alphabetizeImageDisabled : ListPicker.alphabetizeImage;
};

/**
 * Maintains the state of the user interface based on user actions
 */
ListPicker.prototype.mediator = function()
{
    var selected_count = this.countSelected(this.selectedOptions);

    this.rightToLeftButton.disabled = !this.hasSelections(this.selectedOptions);
    this.leftToRightButton.disabled = !this.hasSelections(this.availableOptions);
    this.upButton.disabled = !(selected_count == 1 && !this.selectedOptions.options[0].selected);
    this.downButton.disabled = !(selected_count == 1 && !this.selectedOptions.options[this.selectedOptions.options.length - 1].selected);
    this.alphaButton.disabled = (this.selectedOptions.options.length <= 1);

    this.updateImages();
};

/**
 * Disables the list picker
 * @param disable true to disable, false to enable.
 */
ListPicker.prototype.disableListPicker = function(disable)
{
    document.getElementById("fs_" + this.baseName).disabled = disable;
    document.getElementById("lbl_" + this.baseName).disabled = disable;

    this.availableOptions.disabled = disable;
    this.selectedOptions.disabled = disable;
    this.leftToRightButton.disabled = disable;
    this.rightToLeftButton.disabled = disable;
    this.upButton.disabled = disable;
    this.downButton.disabled = disable;
    this.alphaButton.disabled = disable;

    this.updateImages();
};

/**
 * Moves all of the selections from "selected" to "all"
 */
ListPicker.prototype.revertAll = function()
{
    this.selectAll(this.selectedOptions);
    this.moveOptions(this.selectedOptions, this.availableOptions);
};

/**
 *  Selects all options in the select box identified by its id
 * @param theSel the select box
 */
ListPicker.prototype.selectAll = function(theSel)
{
    var i;
    for (i = 0; i < theSel.options.length; ++i)
    {
        theSel.options[i].selected = true;
    }
};

/**
 * Selected an option in the select box by value
 * @param theSel the select box
 * @param value the value of the option to select
 */
ListPicker.prototype.selectOptionByValue = function(theSel, value)
{
    var i;
    for (i = 0; i < theSel.options.length; ++i)
    {
        if (theSel.options[i].value == value)
        {
            theSel.options[i].selected = true;
            break;
        }
    }
};

/**
 * method used to compare two list options
 * @param a the first option to compare
 * @param b the second option to compare
 */
ListPicker.optionCompareMethod = function(a, b)
{
    return a.text.toLowerCase() > b.text.toLowerCase();
};

/**
 * Alphabetizes the options in the selected list
 */
ListPicker.prototype.alphabetize = function()
{
    var options = [];
    var i;
    for (i = 0; i < this.selectedOptions.length; ++i)
    {
        options.push(this.selectedOptions.options[i]);
    }

    options.sort(ListPicker.optionCompareMethod);

    // Add back the options
    this.selectedOptions.options.length = 0;

    for (i = 0; i < options.length; ++i)
    {
        this.selectedOptions.options[i] = options[i];
    }

    if (this.onchange)
    {
        this.onchange();
    }

    this.mediator();
};

/**
 * Adds an option to the given select box.
 * @param theSel the select box to add the option to
 * @param theText the text of the option
 * @param theValue the value of the option
 */
ListPicker.prototype.addOption = function(theSel, theText, theValue)
{
    var i;
    var newOpt = document.createElement("option");
    newOpt.text = theText;
    newOpt.value = theValue;

    if (theSel == this.availableOptions)
    {
        // Find the spot in the list where this will go.
        // The lists are sorted alphabetically.

        var opt = null;
        for (i = 0; i < theSel.length && opt === null; ++i)
        {
            if (theText < theSel.options[i].text)
            {
                opt = theSel.options[i];
            }
        }

        if (window.navigator.userAgent.indexOf("MSIE") != -1)
        {
            if (i > 0)
            {
                i--;
            }
            theSel.add(newOpt, i);
        }
        else
        {
            theSel.add(newOpt, opt);
        }
    }
    else
    {
        if (window.navigator.userAgent.indexOf("MSIE") != -1)
        {
            theSel.add(newOpt);
        }
        else
        {
            theSel.add(newOpt, null);
        }
    }
};

/**
 * Deletes the option with the given index from the select box
 * @param theSel the select box element to delete the option from
 * @param theIndex the index of the option to delete
 */
ListPicker.prototype.deleteOption = function(theSel, theIndex)
{
    var selLength = theSel.length;
    if (selLength > 0)
    {
        theSel.options[theIndex] = null;
    }
};

/**
 * Initializes an event object by making sure that it exists and then setting
 * the target if the target does not exist
 * @param e the event object to initialize
 */
ListPicker.prototype.initEvent = function(e)
{
    e = e ? e : window.event;
    if (typeof(e.target) == "undefined")
    {
        e.target = e.srcElement;
    }

    return e;
};

/**
 * Moves the selected options from the all box to the selected box
 * @param event the event object.  Used to blur the button.
 */
ListPicker.prototype.selectOptions = function(event)
{
    event = this.initEvent(event);
    event.target.blur();

    return this.moveOptions(this.availableOptions, this.selectedOptions);
};

/**
 * Moves the selected options from the selected box to the all box
 * @param event the event object.  Used to blur the button.
 */
ListPicker.prototype.deselectOptions = function(event)
{
    event = this.initEvent(event);
    event.target.blur();

    return this.moveOptions(this.selectedOptions, this.availableOptions);
};

/**
 * Moves the selected options up in the selected list
 * @param event the event object.  Used to blur the button.
 */
ListPicker.prototype.moveUp = function(event)
{
    event = this.initEvent(event);
    event.target.blur();

    var select_options = this.selectedOptions.getElementsByTagName('option');
    for (var i = 1; i < select_options.length; i++)
    {
        var opt = select_options[i];
        if (opt.selected)
        {
            this.selectedOptions.removeChild(opt);
            this.selectedOptions.insertBefore(opt, select_options[i - 1]);
        }
    }

    if (this.onchange)
    {
        this.onchange();
    }

    this.mediator();
};

/**
 * Moves the selected options down in the selected list
 * @param event the event object.  Used to blur the button.
 */
ListPicker.prototype.moveDown = function(event)
{
    event = this.initEvent(event);
    event.target.blur();

    var select_options = this.selectedOptions.getElementsByTagName('option');
    for (var i = select_options.length - 2; i >= 0; i--)
    {
        var opt = select_options[i];
        if (opt.selected)
        {
            var nextOpt = select_options[i + 1];
            opt = this.selectedOptions.removeChild(opt);
            nextOpt = this.selectedOptions.replaceChild(opt, nextOpt);
            this.selectedOptions.insertBefore(nextOpt, opt);
        }
    }

    if (this.onchange)
    {
        this.onchange();
    }

    this.mediator();
};

/**
 * Move the selected options from one list to another
 * @param theSelFrom the select box to move the options from
 * @param theSelTo the select box to move the options to.
 */
ListPicker.prototype.moveOptions = function(theSelFrom, theSelTo)
{
    var selLength = theSelFrom.length;
    var selectedText = [];
    var selectedValues = [];
    var selectedCount = 0;

    var i;

    // Find the selected Options in reverse order
    // and delete them from the 'from' Select.
    for (i = selLength - 1; i >= 0; i--)
    {
        if (theSelFrom.options[i].selected)
        {
            selectedText[selectedCount] = theSelFrom.options[i].text;
            selectedValues[selectedCount] = theSelFrom.options[i].value;
            this.deleteOption(theSelFrom, i);
            selectedCount++;
        }
    }

    // Add the selected text/values in reverse order.
    // This will add the Options to the 'to' Select
    // in the same order as they were in the 'from' Select.
    for (i = selectedCount - 1; i >= 0; i--)
    {
        this.addOption(theSelTo, selectedText[i], selectedValues[i]);
    }

    if (this.onchange)
    {
        this.onchange();
    }

    this.mediator();

    return false;
};

/**
 * Loads a new list of data into the widget
 * @param values the list of data
 */
ListPicker.prototype.loadNewData = function(values)
{
    var i;

    this.revertAll();

    for (i = 0; i < values.length; ++i)
    {
        this.selectOptionByValue(this.availableOptions, values[i]);
        this.moveOptions(this.availableOptions, this.selectedOptions);
    }
};

/**
 * Gets the values that are selected for the list picker.
 */
ListPicker.prototype.getSelectedValues = function()
{
    var i;
    var ret = [];
    var options = this.selectedOptions.options;
    for (i = 0; i < options.length; ++i)
    {
        ret.push(options[i].value);
    }

    return ret;
};

/**
 * This method should be called prior to doing a submit on a form with this control
 * embedded in it.  It will select all items in the listPicker so that they will be 
 * sent in the POST when the form is submitted
 */
ListPicker.prototype.beforeSubmit = function()
{
    this.selectAll(this.selectedOptions);
};

