/**
 * The accordion widget allows you to manage items that can collapse and expand.
 * An accordion is composed of set of items, each with a button and body
 * The button can be any element that is used to trigger the opening and closing of the item
 * The body is what will actually be collapsed and expanded (using style height)
 * Other options are:
 *   allowMultipleOpen (false) - allow multiple items to be open at the same time
 *   selfClosable (true) - allow an item to close itself
 *   animate (true) - animate the opening/closing
 *
 * default CSS structure:
 * 
 * .accordion
 *    .accItem
 *      .btn
 *      .bd
 *    .accItem
 *      .btn
 *      .bd
 */ 
(function() { // run with local scope
    var Dom = YAHOO.util.Dom;
    var Lang = YAHOO.lang;
    var Event = YAHOO.util.Event;
    
    YAHOO.widget.Accordion = function (options) {
    
        this._items = []; // { button, body, isOpen}

        this._options = {
            allowMultipleOpen:false, 
            openedClass:"open", 
            openingClass:"open", 
            closedClass:"closed", 
            closingClass:"open",
            bodyClass: "bd",
            buttonClass: "btn",
            itemClass: "accItem",
            selfClosable: true,
            openBodyHeight: -1,
            animate: true
        };
    
        if (options) {
            this._options = Lang.merge(this._options,options);
        }
    };
    
    /**
     * 
     */
    YAHOO.widget.Accordion.prototype = {
    
        /**
         * add all the items found within this element searching by className
         * if there is no itemClass specified, then it is assumed that buttons and
         * bodies will alternate
         */
        addAll: function(elem) {
            if (this._options.itemClass) {
                var items = Dom.getElementsByClassName(this._options.itemClass, "div", elem);
                for (var i=0;i<items.length;i++) {
                   this.add(items[i]);
                }
            }
            else {
                var btns = Dom.getElementsByClassName(this._options.buttonClass, null, elem);
                var bodies = Dom.getElementsByClassName(this._options.bodyClass, null, elem);
                 for (var i=0;i<btns.length&&i<bodies.length;i++) {
                   this.add(btns[i],bodies[i],btns[i]);
                }
            }

        },
        
        /**
         * get the button for an item based on className
         */
        getButton: function(item) {
            var items = Dom.getElementsByClassName(this._options.buttonClass, null, item);
            return items[0];
        },
        /**
         * get the body for an item based on className
         */
        getBody: function(item) {
            var items = Dom.getElementsByClassName(this._options.bodyClass, null, item);
            return items[0];
        },
        /**
         * add an item element
         */
        add: function (el, body, button) {
            
            el = Dom.get(el);
            var item = {
                element: el,
                button: button ? button : this.getButton(el),
                body: body ? body : this.getBody(el),
                isOpen: Dom.hasClass(el,this._options.openedClass)
            }
            
            if (this._options.animate) {
                // set a calculated height
                var h = (item.isOpen) ?this.getContentHeight(item.body) : 0;
                Dom.setStyle(item.body,'height',h+'px');
            }
            var idx = this._items.length;
            this._items.push(item);
            var self = this;

            if (item.button.tagName == 'input' && (item.button.type == 'radio' || item.button.type == 'checkbox')) {
                Event.on(item.button,"change", function (evt) {
                    self.toggleInput(idx);
                });
                // sync input to state
                item.button.checked = item.isOpen;
            }
            else {
                Event.on(item.button, "click", function (evt) {
                    self.toggle(idx);
                });
            }

        },
        
        remove:function (idx) {
            this._items[idx] = null;
        },
        
        /**
         * toggle an item open/close depending on the input's checked state
         */
        toggleInput: function(evt,idx) {
            var elem = Event.getTarget(evt);
            if (elem.checked) {
                this.collapse(idx);
            } else {
                this.expand(idx);
            }
        },
        
        /**
         * toggle an item open/close depending on its current state
         */
        toggle:function (idx) {
            var item = this._items[idx];
            if (item) {
                if (!item.isOpen) {
                    this.expand(idx);
                }
                else if (this._options.selfClosable) {
                    this.collapse(idx);
                }
            }
        }, 
        
        /**
         * remove the item class
         */
        removeItemClass:function (idx, className) {
            if (className) {
                Dom.removeClass(this._items[idx].element, className);
            }
        }, 
        
        /**
         * add the item class
         */
        addItemClass:function (idx, className) {
            if (className) {
                Dom.addClass(this._items[idx].element, className);
            }
        }, 
        
        /**
         * calculate the body height unless one is specified
         */
        getContentHeight: function (body) {
            if (this._options.openBodyHeight > 0) {
                return this._options.openBodyHeight;
            }

            var contentHeight = 0;
            var bodyElem = Dom.get(body);
            for (var i = 0; i < bodyElem.childNodes.length; i++) {
                if (bodyElem.childNodes[i].nodeType == 1) {
                    var region = Dom.getRegion(bodyElem.childNodes[i]);
                    contentHeight += (region.bottom - region.top);
                }
            }
            return contentHeight;
        }, 
        
        /**
         * finish opening an item
         */
        expandComplete:function (idx) {
            this.removeItemClass(idx, this._options.openingClass);
            this.addItemClass(idx, this._options.openedClass);
        }, 
        /**
         * begin opening an item
         */
        expand:function (idx, callback) {
            var item = this._items[idx];
            if (item.isOpen) {
                return;
            }
    
            this.removeItemClass(idx, this._options.closedClass);
            this.removeItemClass(idx, this._options.closingClass);
            
            if (this._options.animate) {
                this.animateStateChange(
                    idx,
                    callback,
                    true,
                    this.expandComplete,
                    0,
                    this.getContentHeight(item.body),
                    this._options.openingClass
                );
    
            }
            else {
    
                this.addItemClass(idx, this._options.openedClass);
                Dom.setStyle(item.body,'display','');
                item.isOpen = true;
                if (callback) callback();
            }
    
            if (!this._options.allowMultipleOpen) {
                for (var i = 0; i < this._items.length; i++) {
                    if (i != idx && this._items[i].isOpen) {
                        this.collapse(i);
                    }
                }
            }
        }, 
        
        /**
         * Create an animating transition from one state to another
         */
        animateStateChange: function(idx,callback,endState,onComplete,startHeight, endHeight,itemClass) {
            var item = this._items[idx];
            this.addItemClass(idx, itemClass);

            var anim = new YAHOO.util.Anim(item.body, {height: {from: startHeight, to: endHeight}}, 0.5);
            var self = this;
            anim.onComplete.subscribe(function () {
                onComplete.call(self,idx);
            });
            if (callback) {
                anim.onComplete.subscribe(callback);
            }
            anim.animate();
            item.isOpen = endState;
        },
        /**
         * finish closing an item
         */
        collapseComplete:function (idx) {
            this.removeItemClass(idx, this._options.closingClass);
            this.addItemClass(idx, this._options.closedClass);
        }, 
        /**
         * begin opening an item
         */
        collapse:function (idx, callback) {
            var item = this._items[idx];
            if (!item.isOpen) {
                return;
            }

            
            this.removeItemClass(idx, this._options.openedClass);
            this.removeItemClass(idx, this._options.openingClass);
            
            if (this._options.animate) {
                this.animateStateChange(
                    idx,
                    callback,
                    false,
                    this.collapseComplete,
                    parseInt(Dom.getStyle(item.body,'height')),
                    0,
                    this._options.closingClass
                );
            }
            else {;
                if (this._options.closedClass) this.addItemClass(idx, this._options.closedClass);
                Dom.setStyle(item.body,'display','none');
                item.isOpen = false;
                if (callback) callback();
            }
        }, 
        /**
         * deactivate by unregistering the even listeners that trigger state change
         */
        deactivate:function () {
            for (var i = 0; i < this._items.length; i++) {
                var item = this._items[i];
                Event.removeListener(item.button, "click");
                Event.removeListener(item.button, "change");
            }
        }
    
    };

})();
