/**
 * Select
 * 
 * Manages one select with all options inside this select
 * 
 * @require     MooTools
 * 
 * @author      Steffen Maechtel <s.maechtel@netzbewegung.com>
 * @version     4.00
 */

var Nb_Form_Select = new Class({
    initialize: function(element, options)
        {
        // check if we have failure
        if (element.getStyle('display') == 'none')
        {
            return;
        }
        
        options = options || {};
        
        // option scroll
        this.maxItemCount            = options.maxItemCount || -1;
        
        // option additional divs for styling
        this.selectCurrentInner      = options.selectCurrentInner      || 0;
        this.selectListContinerInner = options.selectListContinerInner || 0;
        this.selectListScrollInner   = options.selectListScrollInner   || 0;
        this.selectOptionInner       = options.selectOptionInner       || 0;
        this.selectOptionGroupInner  = options.selectOptionGroupInner  || 0;
        
        // option offset list width
        this.intOffsetListWidth = options.intOffsetListWidth || 0;

        // body element
        this.body = $$('body')[0];

        this.onBodyClick = this.hide.bind(this);

        // init
        this.status  = false;
        this.original = {};
        this.original.select = element;
        this.original.option = this.original.select.getElements('option');

        // get selected
        this.selected = this.getSelected();

        // hide orginal select
        this.hideOriginal();
        
        // create custom select
        this.custom = this.getCustom();
        
        // add toggle
        this.custom.select.addEvent('click', this.toggle.create({'bind': this, 'event': true}));
        
        // add hover
        this.custom.option.each(this.optionEvent.bind(this));
    },
    toggle: function(event) 
    {
        event.stop();

        // toggle layer (hide/show)
        if (this.status)
        {
            this.hide();
        } else {
            this.show();
        }
    },
    
    optionEvent: function(option) 
    {
        option.addEvent('mouseover', this.mouseover.create({'bind': this, 'arguments': option}));
        option.addEvent('mouseout',  this.mouseout.create({'bind': this, 'arguments': option}));
        option.addEvent('click',     this.select.create({'bind': this, 'event': true, 'arguments': option}));
    },
    
    mouseover: function(option) 
    {
        option.addClass('select-option-hover');
    },
    
    mouseout: function(option) 
    {
        option.removeClass('select-option-hover');
    },
    
    key: function(event) 
    {
        // layer is not displayed
        if (!this.status)
        {
            return true;
        }
        
        // 13: return, 27: esc, 38: arrow up, 40: arrow down
        if (event.code != 13 && 
            event.code != 27 && 
            event.code != 38 && 
            event.code != 40)
        {
            return true;
        }
        
        event.stop();

        // esc
        if (event.code == 27)
        {
            this.hide();
            return true;
        }
        
        // get current active
        this.intFocus = -1;
    
        for (var i = 0; i < this.custom.option.length; i++)
        {
            
            if (this.custom.option[i].hasClass('selected'))
        {
                this.intFocus = i;
                this.custom.option[i].removeClass('selected');
                break;
            }
            
            if (this.custom.option[i].hasClass('hover'))
        {
                this.intFocus = i;
                this.custom.option[i].removeClass('hover');
                break;
            }
        }
        
        // return
        if (event.code == 13)
        {
            this.select(event, this.custom.option[this.intFocus]);
            return true;
        }
                
        // down
        if (event.code == 40)
        {
            if (this.intFocus + 1 == this.custom.option.length)
        {
                this.intFocus = -1;
            }
            this.intFocus++;
            this.custom.option[this.intFocus].addClass('hover');
            return true;
        } 
        
        // up
        if (event.code == 38)
        {
            if (this.intFocus === 0)
        {
                this.intFocus = this.custom.option.length;
            }
            this.intFocus--;
            this.custom.option[this.intFocus].addClass('hover');
            return true;
        }
    },
    
    select: function(event, option)
    {
        event.stop();
        
        var selected = 0;
        
        // get current index
        for (var i = 0; i < this.custom.option.length; i++) 
        {
            if (option === this.custom.option[i]) 
            {
                selected = i;
            }
        }
        
        if (this.selected != selected) 
        {
            // unset old entry
            this.custom.option[this.selected].removeClass('select-option-selected');
            
            // change current
            this.selected = selected;
            
            // set new entry
            this.original.option[this.selected].set('selected', 'selected');
            this.custom.option[this.selected].addClass('select-option-selected');
            this.custom.currentValue.set('html', this.custom.option[this.selected].get('html'));
        }
        
        // fire orginal select event
        this.original.select.fireEvent('change');
        
        this.hide();
    },
    show: function() 
    {
        this.status = true;
        
        // add body event

        this.body.addEvent('click', this.onBodyClick);
        
        // set open css class
        this.custom.select.addClass('select-open');
        
        // show layer

        var width = this.custom.select.getDimensions().x + this.intOffsetListWidth;
        
        if (width < 45)
{
            width = 45;
        }

        this.custom.listContainer.setStyle('width', width);
        this.custom.listContainer.setStyle('display', 'block');

        if(this.maxItemCount != -1 && this.original.option.length > this.maxItemCount)
        {
            this.custom.listScroll.setStyle("overflow-x", "hidden");
            this.custom.listScroll.setStyle("overflow-y", "scroll");
            
            var intHeight = this.custom.listScroll.getElements(".select-option")[0].getSize().y * this.maxItemCount;
            this.custom.listScroll.setStyle("height", intHeight + "px");
        }
        
    },
    
    hide: function()
        {
        this.status = false;
        
        // remove body event
        
        this.body.removeEvent('click', this.onBodyClick);
        
        // remove open css class
        this.custom.select.removeClass('select-open');
        
        // hide layer
        this.custom.listContainer.setStyle('display', 'none');
    },
    
    getSelected: function()
    {

        var selected = 0;
        
        for (var i = 0; i < this.original.option.length; i++)
        {
            if (this.original.option[i].get('selected'))
            {
                selected = i;
                break;
            }
        }
        
        return selected;
    },

    getCustom: function()
    {
        /*
         *  Example:
         *  <div class="usercontrol-select">
         *      <div class="select-current">
         *          [n-div class="select-current-inner-n"]
         *              <div class="select-current-value">[VALUE-TEXT]</div>
         *          [/n-div]
         *      </div>
         *  
         *      <div class="select-list-container">
         *          [n-div class="select-list-container-inner-n"]
         *              <div class="select-list-scroll"> // height: 500px; overflow: hidden ...
         *                  [n-div class="select-list-scroll-inner-n"]
         *                      <ul class="select-list select-list-level-1">
         *                          <li class="select-option select-option-level-1">
         *                              [n-span class="select-option-inner-n select-option-inner-level-1-n"]
         *                                  <span class="select-option-value">[VALUE-TEXT]</span>
         *                              [n-span]
         *                          </li>
         *                          <li class="select-option select-option-level-1">
         *                              [n-span class="select-option-inner-n select-option-inner-level-1-n"]
         *                                  <span class="select-option-value select-option-value-level-1">[VALUE-TEXT]</span>
         *                              [n-span]
         *                          </li>
         *                          ...
         *                          <li class="select-option-group">
         *                              [n-span class="select-option-group-inner-n"]
         *                                  <ul class="select-list select-list-level-2">
         *                                      <li class="select-option-inner select-option-inner-level-2">
         *                                          [n-span class="select-option-inner-n select-option-inner-level-2-n"]
         *                                              <span class="select-option-value  select-option-value-level-2">[VALUE-TEXT]</span>
         *                                          [n-span]
         *                                      </li>
         *                                      ...
         *                                  </ul>
         *                              [n-span]
         *                          </li>
         *                          ...
         *                      </ul>
         *                  [n-div]
         *              </div>
         *          [n-div]
         *      </div>
         */
        
        // select
        var elementSelect = new Element('div', {'class': this.original.select.get('class')});
        
        // select-current
        var elementSelectCurrent = new Element('div', {'class': 'select-current'});
        elementSelect.grab(elementSelectCurrent);
        
        // select-current-inner-n
        var temp = elementSelectCurrent;
        for (var i = 0; i < this.selectCurrentInner; i++)
        {
            var inner = new Element('div', {'class': 'select-current-inner-' + (i + 1)});
            temp.grab(inner);
            temp = inner;
        }
        
        // select-current-value
        var elementSelectCurrentValue = new Element('div', {
            'class': 'select-current-value', 
            html: this.original.option[this.selected].get('html')
        });
        temp.grab(elementSelectCurrentValue);
        
        // select-list-container
        var elementSelectListContainer = new Element('div', {'class': 'select-list-container'});
        elementSelect.grab(elementSelectListContainer);
        
        // select-list-container-inner-n
        var temp = elementSelectListContainer;
        for (var i = 0; i < this.selectListContainerInner; i++)
        {
            var inner = new Element('div', {'class': 'select-list-container-inner-' + (i + 1)});
            temp.grab(inner);
            temp = inner;
        }
        
        // select-list-scroll
        var elementSelectListScroll = new Element('div', {'class': 'select-list-scroll'});
        temp.grab(elementSelectListScroll);
        
        // select-list-scroll-inner-n
        var temp = elementSelectListScroll;
        for (var i = 0; i < this.selectListScrollInner; i++)
        {
            var inner = new Element('div', {'class': 'select-list-scroll-inner-' + (i + 1)});
            temp.grab(inner);
            temp = inner;
        }
        
        // select-list select-list-level-1
        var elementSelectList = new Element('ul', {'class': 'select-list select-list-level-1'});
        temp.grab(elementSelectList);
        
        // select-option
        var option      = [];
        var optionValue = [];
        
        
        var intCurrent  = 0;
        var fncAddOption = function(objParent, intLevel, addClass)
        {
            if (intCurrent == this.selected)
            {
                var strClass = 'select-option select-option-level-' + intLevel + ' select-option-selected' + addClass;
            } 
            else 
            {
                var strClass = 'select-option select-option-level-' + intLevel + addClass;
            }
            
            var elementOption = new Element('li', {'class': strClass}); 
            option[option.length] = elementOption;
            
            objParent.grab(elementOption);
            
            // select-option-inner-n select-option-inner-level-1-n
            var temp = elementOption;
            for (var j = 0; j < this.selectOptionInner; j++)
            {
                var inner = new Element('span', {'class': 'select-option-inner-' + (j + 1) + ' select-option-inner-level-' + intLevel + '-' + (j + 1)});
                temp.grab(inner);
                temp = inner;
            }
            
            // select-option-value select-option-value-level-1
            var elementSelectOptionValue = new Element('span', {'class': 'select-option-value select-option-value-level-' + intLevel, 'html': this.original.option[intCurrent].get('html')});
            optionValue[optionValue.length] = elementSelectOptionValue;
            temp.grab(elementSelectOptionValue);
            
            intCurrent++;
            
            return temp; // @todo maybe we dont need this ...
        }
        
        
        var children = this.original.select.getChildren();
        
        for (var i = 0; i < children.length; i++)
        {
            var element = children[i];
            
            if (element.tagName.toLowerCase() == 'option')
            {
                if (i == 0)
                {
                    addClass = ' select-option-first select-option-first-level-1';
                } 
                else if (i + 1 == children.length)
                {
                    addClass = ' select-option-last select-option-last-level-1';
                } 
                else {
                    addClass = '';
                } 
                var temp = fncAddOption.create({bind:this, arguments:[elementSelectList, 1, addClass]})();
            } 
            else if (element.tagName.toLowerCase() == 'optgroup')
                {
                var elementOptionGroup = new Element('li', {'class': 'select-option-group'});
                
                elementOptionGroup = new Element('li', {'class': 'select-option-group'});
                elementOptionGroup.grab(new Element('span', {'class': 'select-option-group-label', html: element.get('label')}));
                
                var temp = elementOptionGroup;
                for (var j = 0; j < this.selectOptionGroupInner; j++)
                {
                    var inner = new Element('dl', {'class': 'select-option-group-inner-' + (j + 1)});
                    var innerInner = new Element('dd');
                    inner.grab(innerInner);
                    temp.grab(inner);
                    temp = innerInner;
                }
                
                var elementSelectList2 = new Element('ul', {'class': 'select-list select-list-level-2'});
                temp.grab(elementSelectList2);
                
                elementSelectList.grab(elementOptionGroup);
                
                var children2 = element.getChildren();
                
                for (var j = 0; j < children2.length; j++)
                {
                    if (j == 0)
                    {
                        addClass = ' select-option-first select-option-first-level-2';
                    } 
                    else if (j + 1 == children2.length)
                    {
                        addClass = ' select-option-last select-option-last-level-2';
                    } else {
                        addClass = '';
                    } 
                    var temp = fncAddOption.create({bind:this, arguments:[elementSelectList2, 2, addClass]})();
                }
            }
        }

        // new data object with important elements
        var custom = {};
        
        custom.select        = elementSelect;
        custom.currentValue  = elementSelectCurrentValue;
        custom.listContainer = elementSelectListContainer;
        custom.listScroll    = elementSelectListScroll;
        
        custom.option        = option;
        custom.optionValue   = optionValue;
        
        this.original.select.getParent().grab(elementSelect);
        
        return custom;
    },
    hideOriginal: function()
    {
        this.original.select.setStyle('display', 'none');
    }
    
});
