/**
 * This code is based on Leigrber's Accrodion script but is quite completely rewritten. 
 * For the original script see http://www.leigeber.com/2008/10/animated-javascript-accordion/ 
 */


if (!window.tpatpc) {
  window.tpatpc = {}
}


with(tpatpc) {
  
  /**
   * options={
   * 	fadeEnabled:true, //default: false
   *	openClass:"classname", //default: null
   *	millisInterval:25, //default: 10
   *	millisTime: 200, //default: 100
   *	events: ["mouseover","click"] //default: ["click"]
   * }
   *    
   * public methods
   *  open(index,overload)
   *  close(index,overload)
   *  collapse() //<- discouraged as it fires random events (it doesn't close the opened element as one could expect)
   *  isCollapsed()
   *  clicked(index,overload)   
   *     
   * public callbacks
   *  onClick(index,overload) //this callback SHOULD BE guaranteed, you can call any other method from here (be careful about loops)
   *  onClickOpen(index,overload) //this callback is guaranteed, you can call any other method from here (be careful about loops)
   *  onClickClose(index,overload) //this callback is guaranteed, you can call any other method from here (be careful about loops)        
   *  onOpen(index) //this callback SHOULD BE guaranteed, you can call any other method from here
   *  onClose(index) //this callback SHOULD BE guaranteed, you can call any other method from here
   *
   * element statuses:
   * -1 closed
   *  0 closing
   *  1 opening
   *  2 open
   *
   * TODO
   *  remove or fix the collapse method
   */
  
  tpatpc.Accordion = function(buttons, contents, options) {
    options = options ? options : {};
    this.arr = [];
    
    this.fadeEnabled = options.fadeEnabled ? true : false;
    this.openClass = options.openClass;
    
    this.millisInterval = options.millisInterval ? options.millisInterval : 10;
    this.millisTime = options.millisTime ? options.millisTime : 100;
    this.steps = Math.ceil(this.millisTime / this.millisInterval);
    this.diffReport = 0; 
    
    this.clickPhase = 0;
    
    var events = options.events ? options.events : ["click"]; 
    
    if (!buttons || !contents || buttons.length != contents.length) {
      throw ("wrong elements");
    }
    
    this.length = contents.length;
    
    for (var i=0; i < this.length; i++) {
      var button = buttons[i];
      this.arr[i] = {};
      this.arr[i].button = button;
      
      if (button) {
      	//it is possible to create accordions without buttons
      	for (var e = 0; e < events.length; e++) {
			attachEvent(button,this.getOnClickClosure(i),events[e]);
      	}
      }
            
      var content = contents[i];
      if (!content) {
      	//it is not possible to create an accordion element without content
      	throw ("null content");
      }
      
      this.arr[i].content = content;
      this.initAnimation(i);
      
      content.style.overflow = "hidden";
      if (options.opened != i) {
        content.style.height=0; 
        content.style.display='none';
        this.arr[i].status = -1;
        
      } else {
      	this.arr[i].status = 2;
      	if(this.openClass && button) {
      		button.className = this.openClass;
      	}
      }
  
    }
    
  };

  tpatpc.Accordion.prototype = {
  	initAnimation: function(index) {
  		this.arr[index].height = this.arr[index].content.offsetHeight;
  		this.arr[index].step = this.arr[index].height / this.steps;
  	},
  	
  	open: function(index,overload) {
  		if (!this.arr[index]) {
      		return;
      	}
      	
      	if (this.arr[index].status <= 0) {
      		this.clicked(index,overload);
      	}
  	},
  	
  	close: function(index,overload) {
  		if (!this.arr[index]) {
      		return;
      	}
      	
      	if (this.arr[index].status >= 1) {
      		this.clicked(index,overload);
      	}
  	},
  	
  	collapse: function() {
		//TODO which events?
      for(var i=0; i < this.length; i++){
        if (this.arr[i].status > 0) {
          this.closeElement(i);
        }
      }
  	},
  	
  	isCollapsed: function() {
  		for(var i=0; i < this.length; i++){
        if (this.arr[i].status > 0) {
          return false;
        }
      }
      return true;
  	},
  	
    clicked: function(index,overload) {
      if (!this.arr[index]) {
      	return;
      }
      
      //keep a phase on the clicks so that it's possible to call a click
      //method in callbacks
      var localClickPhase = ++this.clickPhase;
      
      
      //todo wrap callbacks
      if (this.onClick) {
        this.onClick(index,overload);
        if (localClickPhase != this.clickPhase) {
          return;
        }
      }
        	
      if (this.arr[index].status > 0) {
      	this.closeElement(index);
        if (this.onClickClose) {
          this.onClickClose(index,overload);
        }
        if (localClickPhase != this.clickPhase) {
          return;
        }
        		
      } else {
      	this.openElement(index);
      	if (this.onClickOpen) {
      		this.onClickOpen(index,overload);
      	}
      	if (localClickPhase != this.clickPhase) {
          return;
        }
      }  
      
      for(var i=0; i < this.length; i++){
        if (i!=index && this.arr[i].status > 0) {
         	this.closeElement(i);
        }
  	  }
    },
    
    getOnClickClosure: function(index) {
      var that = this;
      return function() {
        that.clicked(index);
      };  
    },
    
	closeElement: function(i) {
		if (!this.arr[i]) {
			return;
		}
		
		if (this.arr[i].status == 2) {
			this.initAnimation(i);
		}
		this.arr[i].status = 0;
	
		if (this.openClass && this.arr[i].button && this.openClass == this.arr[i].button.className) { 
	    	this.arr[i].button.className='';
	    }
	    
	    if(!this.interval) {
	    	this.launchInterval();
	    }
	},    
	
	openElement: function(i) {
		if (!this.arr[i]) {
			return;
		}
		this.arr[i].status = 1;
		this.arr[i].content.style.display=''; 
  		
  		 if (this.openClass && this.arr[i].button) {
            this.arr[i].button.className=this.openClass;
         }
         
         if(!this.interval) {
	    	this.launchInterval();
	     }
	},
    
    launchInterval: function() {
    	var that = this;
    	this.interval = setInterval(function() {
    		that.execute();
    	},this.millisInterval);
    },
    
    execute: function() {
		var stepsToDo = 1;
		
		var now = new Date().getTime();
		if (this.last) {
			//diffReport is the number of lost steps because of delay in the interval call 
			this.diffReport += ((now - this.last)-this.millisInterval)/this.millisInterval;
			if (this.diffReport < 0) {
				this.diffReport = 0;
			} else {
				var rounded = Math.round(this.diffReport);
				stepsToDo += rounded;
				this.diffReport -= rounded;
			}
		}
		
		localClickPhase = this.clickPhase;
		var more = false;
		for (var index = 0; index < this.arr.length; index++) {
			if (this.arr[index].status == 0 || this.arr[index].status == 1) {
				var f = this.arr[index].status == 0 ? -1 : 1; 
				
				var content = this.arr[index].content; 
				var height = content.offsetHeight + (this.arr[index].step*stepsToDo*f);
				content.style.height = (height <= 0) ? "0px" : height + "px";				
				
				if (this.fadeEnabled) {
		  	  		content.style.opacity = height/this.arr[index].height; 
		      		content.style.filter='alpha(opacity='+height*100/this.arr[index].height+')';
			    }
				
				if(f > 0 && height >= this.arr[index].height) {
					//completely opened
			        //clear the height so that if the contents grows up   
			        //the content element can contain it
			        content.style.height = "";
			        this.arr[index].status = 2;
			        if (this.onOpen) {
			        	this.onOpen(index);
			        }
			        
				} else if(f < 0 && height <= 0) {
					//completely closed, hide the element
			        content.style.display='none';
			        this.arr[index].status = -1;
 
			      	if (this.onClose) {
			    		this.onClose(index);
			      	}
			      		
				} else {
					more = true;
				}
				
				if (localClickPhase = this.clickPhase) {
		      		more = true;
		      	}
			}
		}
		
		if (more) {
			this.last = now;
		} else {
			clearInterval(this.interval);
			this.interval = null;
			this.diffReport = 0;
			this.last = null;
		}
    }
    
    
  };
  
  tpatpc.attachEvent = function(element,handler,event) {
		if(typeof element.addEventListener != "undefined") {
			//W3C
			element.addEventListener(event, handler, false);
			return true;
			
		} else if(typeof element.attachEvent != "undefined") {
			return element.attachEvent("on"+event, handler);
			
		} else {
		  throw("can't attach events!");
		
		}
  	
  }

}
