Accordion = Class.create();   
// Some 
Accordion.removeOverflow = function(element) {
  element = $(element);
  element.savedOverflow =  element.getStyle("overflow") || "auto"; 
  element.setStyle({overflow: "hidden"});
}                     
Accordion.resetOverflow = function(element) {
  element = $(element);   
  if (element.savedOverflow)
    element.setStyle({overflow: element.savedOverflow});
}
Accordion.removeCursor = function(element) {
  element = $(element);
  element.savedCursor =  element.getStyle("cursor") || "default"; 
  element.setStyle({cursor: "default"});
}                     
Accordion.resetCursor = function(element) {
  element = $(element);   
  if (element.savedCursor)
    element.setStyle({cursor: element.savedCursor});
} 

Element.addMethods(Accordion);

Accordion.prototype =  {
  initialize: function(container, options) {   
    this.panels    = $A(options);  
    this.container = container
    this.animating = false;
    this.headersHeight = 0;
    // Register button and resize bar   
    this.panels.each(function(panel, index) { 
      panel.elements = $A(panel.elements);
      panel.open = true;  
      panel.visible = true;
      panel.container = panel.elements.first();  
      panel.dh = new Array();
       
      panel.minHeight = panel.minHeight || 100;   
      this.headersHeight += $(panel.header).getHeight();   
      // Store dh from panel container
      panel.elements.each(function(e) {
        panel.dh.push($(panel.container).getHeight() - $(e).getHeight())
      })
      if (panel.button)
        Event.observe(panel.button, "click", this._togglePanel.bindAsEventListener(this, panel)); 
      if (index > 0) {
        Event.observe(panel.header, "mousedown", this._mouseDown.bindAsEventListener(this, index));
        Event.observe(panel.header, "mouseover", function() {$(panel.header).addClassName("hover")});
        Event.observe(panel.header, "mouseout", function() {$(panel.header).removeClassName("hover")});
      }
    }.bind(this));                     
    
    this.eventMouseUp   = this._mouseUp.bindAsEventListener(this);
    this.eventMouseMove = this._mouseMove.bindAsEventListener(this);
  },
  
  changeHeight: function(deltaHeight) {    
    var nb    = this._getNbOpenPanels();
    var delta = parseInt(deltaHeight / nb); 
    var rest  = deltaHeight - delta * nb;    
    this.panels.each(function(panel, index) { 
      if (panel.open)   
        panel.elements.each(function(element) { 
          var h = $(element).getHeight(); 
          if (index == nb - 1)  {                                 
            $(element).setStyle({height: h + delta + rest + "px"})
          }
          else
            $(element).setStyle({height: h + delta + "px"})
        })    
    })
  }, 
  
  hide: function(index) {
    var panel = this.panels[index];    
    if (panel.visible) {
      panel.visible = false;         
      if ($(panel.container))  {
        $(panel.container).hide(); 
        var otherPanel = this._getOtherPanel(panel);
        this._changeHeight(otherPanel, $(panel.container).getHeight())
      } else {
      }
    }
  },
  
  show: function(index) {
    var panel = this.panels[index];    
    if (!panel.visible) {
      panel.visible = true;
      $(panel.container).show(); 
      var otherPanel = this._getOtherPanel(panel);
      this._changeHeight(otherPanel, -$(panel.container).getHeight())
    }
    
  }, 
  
  reset: function() {
    var nb    = this._getNbOpenPanels();
    var eltHeight = parseInt($(this.container).getHeight() / nb); 
    var rest  = $(this.container).getHeight() - eltHeight * nb;     
    this.panels.each(function(panel, index) { 
      if (panel.open)   
        panel.elements.each(function(element, index) { 
          if (!$(element))
            return
          if (index > 0) 
            Event.observe(panel.header, "mousedown", this._mouseDown.bindAsEventListener(this, index));
          var h = $(element).getHeight();  
          var delta = eltHeight - h  - panel.dh[index]
          if (index == nb - 1)                                
            $(element).setStyle({height: h + delta + rest + "px"})
          else
            $(element).setStyle({height: h + delta + "px"})
        }.bind(this))    
    }.bind(this))
  },
  
  _togglePanel: function(event, panel) {   
    if (this.animating)
      return;
    
    // Close clicked element                
    if (panel.open) { 
      this.animating = true;
      panel.dh        = $(panel.header).getHeight() - $(panel.container).getHeight();
      $(panel.button).src = $(panel.button).src.gsub("open", "close");
      panel.elements.each(function(element, index) {                   
        var newHeight = $(element).getHeight() + panel.dh;
        $(element).currentHeight = newHeight;        
        $(element).morph({height: newHeight + "px"}, {duration: 0.5, 
                                                      beforeSetup: function() {$(element).removeOverflow()}, 
                                                      afterFinish: function() {this.animating = false; panel.open = false}.bind(this)});
      }.bind(this));
          
      // Remove cursor      
      $(panel.header).removeCursor();

      // Add size to the last one     
      var otherPanel = this._getOtherPanel(panel);
      if (otherPanel)
        otherPanel.elements.each(function(element) {
          var newHeight = $(element).getHeight() - panel.dh
          $(element).morph({height: newHeight + "px"})
        }.bind(this));          
    } 
    // Open clicked element
    else {                  
      var nb = this._getNbOpenPanels();
      var dh;
      // Everything is closed
      if (nb == 0) {
        dh = $(this.container).getHeight() - this.headersHeight;
        var otherPanel = null;
      }
      else {    
        // dh is > 0
        dh = -panel.dh;                                 
        var otherPanel = this._getOtherPanel(panel);    
        var otherHeight = $(otherPanel.container).getHeight();
        if (otherHeight - dh < otherPanel.minHeight)
          dh = otherHeight - otherPanel.minHeight; 
      }
      this.animating = true;    
      $(panel.button).src = $(panel.button).src.gsub("close", "open");
      panel.elements.each(function(element) { 
        var newHeight = $(element).currentHeight + dh;          
        $(element).morph({height: newHeight + "px"}, {duration: 0.5, afterFinish: function() {$(element).resetOverflow(); this.animating = false; panel.open = true}.bind(this)});
      }.bind(this));          
      
      // Restore cursor                          
      $(panel.header).resetCursor();
      
      // Remove size to the last one
      if (otherPanel)
        otherPanel.elements.each(function(element) { 
          var newHeight = $(element).getHeight() - dh
          $(element).morph({height: newHeight + "px"})
        }.bind(this));          
    }
  }, 
  
  _getOtherPanel: function(panel) {
    var panel = this.panels.find(function(p) { 
      return (p != panel && p.open && p.visible)
    })
    return panel;
  }, 
  
  _getNbOpenPanels: function() {
    return this.panels.inject(0, function(n, panel) { return n + (panel.open && panel.visible ? 1 : 0)})
  },
  
  _mouseDown: function(event, index) {   
    this.draggingPanel = this.panels[index];
    this.previousPanel = this.panels[index-1];

    // Cannot resize closed panel or if previous panel is closed
    if (!this.draggingPanel.open || !this.previousPanel.open)
      return;
    Util.disableSelection();
      
    this.dragging     = true;  
    this.draggingPanelInitHeight = $(this.draggingPanel.container).getHeight();
    this.previousPanelInitHeight = $(this.previousPanel.container).getHeight();

    Event.observe(document, "mouseup",   this.eventMouseUp, false);
    Event.observe(document, "mousemove", this.eventMouseMove, false);
    this.y = Event.pointerY(event);  
    this.minY = this.maxY = 0;
    Event.stop(event);
  },

  _mouseMove: function(event) {
    if (!this.dragging)
      return;                 
    // Check mouse bounds (when minHeight is reached)
    if ((this.minY && Event.pointerY(event) > this.minY) || (this.maxY && Event.pointerY(event) < this.maxY)) 
      return;                       
                              
    var dy = Event.pointerY(event) - this.y;
    
    if ($(this.previousPanel.container).getHeight() + dy < this.previousPanel.minHeight) {
      this.maxY = this.y;   
    } else
      this.maxY = 0;
    if ($(this.draggingPanel.container).getHeight() - dy < this.draggingPanel.minHeight) {
      this.minY = this.y;
    } else 
    this.minY = 0;
    
    this._changeHeight(this.previousPanel, dy);
    this._changeHeight(this.draggingPanel, -dy);

    this.y = Event.pointerY(event);      
    Event.stop(event);
  },

  _mouseUp: function(event) {
    if (!this.dragging)
      return;  

    this.dragging = false;    
    Event.stopObserving(document, "mouseup", this.eventMouseUp,false);
    Event.stopObserving(document, "mousemove", this.eventMouseMove, false);
    Util.enableSelection();
    Event.stop(event);
  },
  
  _changeHeight: function(panel, dh) {
    panel.elements.each(function(panel) {
      $(panel).setStyle({height: $(panel).getHeight() + dh + "px"});
    })
  }
}    