function toggleClass(e, c1, c2) {
  Element.removeClassName(e, c1);
  Element.addClassName(e, c2);
}

var lastSelectedLeaf = false;

Tree = Class.create();
Tree.prototype = {
  initialize: function(root_, styleOptions_, preOpen_, observers_) {
    this.root = $(root_) || false;
    this.tag = 'li'; // currently only 'li' supported
    this.styleOptions = styleOptions_;
    this.observers = observers_;
    this.treeItems = new Array();

    if (this.root) {
      var children = this.root.childNodes;
      if (children) {
        for (var i = 0; i < children.length; i++) {
          if (children[i].tagName && children[i].tagName.toLowerCase() == this.tag.toLowerCase())
            this.treeItems.push(new Tree.Item(children[i], this.root.tagName, styleOptions_, preOpen_, observers_));
        }
      }
    }
  },

  destroy: function() {
    for (var i = 0; i < this.treeItems.length; i++)
      this.treeItems[i].destroy();
  },

  open: function(e) {
    var el = $(e);
    if (!el)
      return false;

    var items = this.treeItems;
    for (var i = 0; i < items.length; i++) {
        var ti = items[i];
        if (ti.item.id == el.id) {
            ti.open();
            return ti;
        } else if (ti.subTree) {
            var openedElement = ti.subTree.open(el);
            if (openedElement) {
                ti.open();
                return openedElement;
            }
        }
    }

    return false;
  },

  insertChild: function(parent, item) {
    var el = $(parent);
    var items = this.treeItems;
    for (var i = 0; i < items.length; i++) {
      var ti = items[i];
      if (ti.item.id == el.id) {
        ti.createSubTree()
        if (ti.block.childNodes.length == 0)
          ti.block.appendChild(item);
        else
          ti.block.insertBefore(item, ti.block.childNodes[0]);
        ti.addChild(item);
        ti.open();
        return true;
      } else if (ti.subTree) {
        ti.subTree.insertChild(parent, item);
      }
    }
  },

  expandAll: function() {
    for (var i = 0; i < this.treeItems.length; i++) {
      var item = this.treeItems[i];
      if (item.subTree)
        item.subTree.expandAll();

      item.open();
    }
  },

  collapseAll: function() {
    for (var i = 0; i < this.treeItems.length; i++) {
      var item = this.treeItems[i];
      item.close();
      if (item.subTree)
        item.subTree.collapseAll();
    }
  }
}


Tree.Item = Class.create();
Tree.Item.prototype = {
	initialize: function(item_, branchTag_, styleOptions_, preOpen_, observers_) {
    this.item = $(item_) || false;
    this.branchTag = branchTag_;
    this.preOpen = preOpen_ || []
    this.block = false;
    this.subTree = false;
    this.opened = false;

    this.styleOptions = Object.extend({
      openClass: 'open',
      closeClass: 'close',

      leafClass: 'blank',
      leafSelectedClass: '',
      leafHighlightClass: '',
      leafHoverHighlightClass: ''
    }, styleOptions_ || {});

    this.observers = observers_ || [];

    if (this.item) {
      var btn = document.createElement('span');

  		var startingPoint = 0;
  		var chs = new Array();

  		for(var i = 0;i < this.item.childNodes.length; i++)
  		{
          if(this.item.childNodes[i].tagName && this.item.childNodes[i].tagName.toLowerCase() == 'span' && this.item.childNodes[i].className != 'ac' )
            startingPoint = i + 1;
          else if(this.item.childNodes[i].tagName && this.item.childNodes[i].tagName.toLowerCase() == this.branchTag.toLowerCase())
            this.block = this.item.childNodes[i];
          else
            chs.push(this.item.childNodes[i]);
  		}

      var wrapper = document.createElement('span');
      wrapper.className = 'wrapper';

      //var ti = this;
      //this.wrapper.onmouseover = function(event) { Element.removeClassName(ti.wrapper, ti.styleOptions.leafHighlightClass) };
      //this.wrapper.onmouseout = function(event) {Element.removeClassName(ti.wrapper, ti.styleOptions.leafHighlightClass) };

      this.wrapper = this.item.insertBefore(wrapper, this.item.childNodes[startingPoint]);
      this.btn = this.item.insertBefore(btn, this.item.childNodes[startingPoint]);
      for(var i = 0; i < chs.length; i++) {
	    var child = chs[i];
	    this.wrapper.appendChild(child);
	  }

      this.eventMouseClick = false;
      if (!this.block)
      {
        this.btn.className = this.styleOptions.leafClass;
      }
      else
      {
        this.preOpen.find( function(i){ return (i == this.item.id)}.bind(this) ) ? this.open() : this.close();

        this.eventMouseClick = this.toggle.bindAsEventListener(this);
        Event.observe(this.btn, 'click', this.eventMouseClick);
        this.subTree = new Tree(this.block, this.styleOptions, this.preOpen, this.observers);
      }

      this.lastClickedItem = false;
      if (this.styleOptions.leafHoverHighlightClass != '')
      {
        this.eventMouseOver = this.hover.bindAsEventListener(this);
        this.eventMouseOut = this.hoverOut.bindAsEventListener(this);

        Event.observe(this.wrapper, 'mouseover', this.eventMouseOver);
        Event.observe(this.wrapper, 'mouseout', this.eventMouseOut);
      }

      if (this.styleOptions.leafSelectedHighlightClass != '')
      {
        this.leafMouseClicked = this.selectLeaf.bindAsEventListener(this);
        Event.observe(this.wrapper, 'click', this.leafMouseClicked);
      }
/*
      var highlightWrapper = document.createElement('span');
      highlightWrapper.className = 'wrapper';

      this.highlightWrapper = this.item.appendChild(highlightWrapper);

      debugger;
      for(var i = 0;i < this.item.childNodes.length; i++)
      {
        var child = this.item.childNodes[i];
        if (this.highlightWrapper != child)
          this.highlightWrapper.appendChild(child);
      }

      var ti = this;
      this.highlightWrapper.onmouseover = function(event) { Element.removeClassName(ti.highlightWrapper, ti.styleOptions.leafHighlightClass) };
      this.highlightWrapper.onmouseout = function(event) { Element.removeClassName(ti.highlightWrapper, ti.styleOptions.leafHighlightClass) };
*/
    }
  },

  destroy: function() {
    if (this.eventMouseClick)
      Event.stopObserving(this.btn, 'click', this.eventMouseClick);
    if (this.subTree)
      this.subTree.destroy();
  },

  addChild: function(item) {
    if (!this.subTree) {
      this.subTree.treeItems.unshift(new Tree.Item(item, this.branchTag, this.styleOptions, this.preOpen, this.observers));
    }
  },

  createSubTree: function() {
    if (!this.block) {
      var node = this.item;
      ul = document.createElement("ul");
      node.appendChild(ul);
      this.block = ul;
      Element.removeClassName(node, this.styleOptions.leafClass);
    }
    
    if (!this.subTree) {
      this.preOpen.find( function(i){ return (i == this.item.id)}.bind(this) ) ? this.open() : this.close();
      this.eventMouseClick = this.toggle.bindAsEventListener(this);
      Event.observe(this.btn, 'click', this.eventMouseClick);
      this.subTree = new Tree(this.block, this.styleOptions, this.preOpen, this.observers);
    }
  },


  close: function() {
    if (this.block) {
      Element.hide(this.block);
      this.opened = false;
      toggleClass(this.btn, this.styleOptions.openClass, this.styleOptions.closeClass);
      this.notifyObservers('onClose', this);
    }
  },

  open: function() {
    if (this.block) {
      Element.show(this.block);
      this.opened = true;
      toggleClass(this.btn, this.styleOptions.closeClass, this.styleOptions.openClass);
      this.notifyObservers('onOpen', this);
    }
  },
/*
  close: function() {
    if (this.block) {
      Element.show(this.block);
      this.opened = false;
      toggleClass(this.btn, this.styleOptions.openClass, this.styleOptions.closeClass);
      this.notifyObservers('onClose', this);
    }
  },

  open: function() {
    if (this.block) {
      Element.hide(this.block);
      this.opened = true;
      toggleClass(this.btn, this.styleOptions.closeClass, this.styleOptions.openClass);
      this.notifyObservers('onOpen', this);
    }
  },
*/

  toggle: function() {
    if(this.block) {
      if (this.block.style.display == 'none')
        this.open();
      else
        this.close();

      return this.opened;
    }
  },

  hover: function()
  {
    Element.addClassName(this.wrapper, this.styleOptions.leafHoverHighlightClass);
  },

  hoverOut: function()
  {
    if (!this.lastClickedItem || this.lastClickedItem != this.wrapper)
      Element.removeClassName(this.wrapper, this.styleOptions.leafHoverHighlightClass);
  },

  selectLeaf: function()
  {
    /*
    this.wrapper.className = 'wrapper ' + this.styleOptions.leafSelectedHighlightClass;


    if (this.lastClickedItem)
    {
        this.lastClickedItem.className = 'wrapper';
    }

    this.lastClickedItem = this.wrapper;
    */
  },

  observers: [],

  addObserver: function(observer) {
    this.observers.push(observer);
  },

  removeObserver: function(observer) {
    this.observers = this.observers.reject( function(o) { return o==observer });
  },

  notifyObservers: function(eventName, observable) {
    this.observers.each( function(o) { if(o[eventName]) o[eventName](eventName, observable); });
  }
}

Tree.CookieObserver = Class.create();
Tree.CookieObserver.prototype = {
  initialize: function(cookieName_, options_) {
    this.cookieName = cookieName_;
    this.options = Object.extend({
      delimiter: '#',
      taxonomy: false
    }, options_ || {});
  },

  onOpen: function(eventName, ti) {
    if (ti && ti.item) {
      var a = this.getCookieArray();
      if (!a.find(function(id){return (id == ti.item.id)})) a.push(ti.item.id);
      this.setCookieArray(a);
    }
  },

  onClose: function(eventName, ti) {
    if (ti && ti.item)
      this.setCookieArray(this.getCookieArray().reject(function(id){return id == ti.item.id;}));
  },

  getCookieArray: function() {
    var c = xGetCookie(this.getCookieName());
    return c ? c.split(this.options.delimiter) : [];
  },

  setCookieArray: function(a) { xSetCookie(this.getCookieName(), a.join(this.options.delimiter)); },

  getCookieName: function() { return this.options.taxonomy ? this.cookieName + '-' + this.options.taxonomy : this.cookieName; }
}
