/**
 * Extend a DL element to be used as an accordion menu
 * Accordion Menu must be build with DL, DT and DD Elements
 * Use dt.menu in css to define styles in DT that have submenu
 * Use dt.open in css to define styles on opened DT
 * Use dt.over in css to define styles on active DT
 *
 * @param {String | Element} menu: DL element in document
 * @param {Array} opened: list of initial opened tabs
 */
TAccMenu = function(menu, opened) {
  this.menu    = TDOM.getElement(menu);
  this.opened  = opened && opened.length ? opened : [];
  this.count   = 0;
  this.speed   = 10;
  this.multiOpen = true;

  this._anims = [];
  this._recs  = [];

  // Initialize menu
  if (this.menu && this.menu.tagName.toUpperCase() == 'DL') {
    var dt = TDOM.firstChild(this.menu);
    while (dt && dt.nodeName.toUpperCase() != 'DT') {
      dt = TDOM.nextElement(dt);
    }
    while (dt) {
      var dd = TDOM.nextElement(dt);
      if (dd && dd.nodeName.toUpperCase() == 'DD') {
        this.count++;
        dt.id = dt.id == '' ? 'tab' + this.count : dt.id;
        this._anims[dt.id] = [];
        TDOM.addClass(dt, 'menu');
        TEvents.listen(dt, 'click', this.doClick, this);
        TEvents.listen(dt, 'mouseover', this.doOver, this);
        TEvents.listen(dt, 'mouseout', this.doOut, this);
        var opened = this.tabOpen(dt.id);
        opened ? TDOM.addClass(dt, 'open') : TDOM.removeClass(dt, 'open');
        while (dd && dd.nodeName.toUpperCase() == 'DD') {
          opened ? TDOM.addClass(dd, 'open') : TDOM.removeClass(dd, 'open');
          dd.style.display = opened ? 'block' : 'none';
          dd = TDOM.nextElement(dd);
        }
      }
      dt = dd;
    }
  }
}
TAccMenu.inherits(TEventDispatcher);

/**
 * Test if a submenu open
 * @param {String | Element} id: tab id or DT or DD element
 */
TAccMenu.prototype.tabOpen = function (id) {
  id = typeof id == "object" ? id.id : id;
  for (var i = 0; i < this.opened.length; i++) {
    if (this.opened[i] == id) {
      return true;
    }
  }
  return false;
}

/**
 * Get a menu tab by his id
 *
 * @param {Integer} id: tab id
 * @return {Element} dt element or null
 */
TAccMenu.prototype.getTab = function(id) {
  var dt = TDOM.firstChild(this.menu);
  while (dt) {
    if (dt.nodeName.toUpperCase() == 'DT' && dt.id == id) {
      return dt;
    }
    dt = TDOM.nextElement(dt);
  }
  return null;
}

/**
 * Add class over to DT on mouse over
 * Use dt.over in css styles to dt visual style when mouse over
 * @param {TDOMEvent} e: DOM event object
 */
TAccMenu.prototype.doOver = function(e) {
  TDOM.addClass(e.currentTarget, 'over');
  this.dispatch("mouseover", e.currentTarget);
}

/**
 * Remove the over class from dt when mouse out
 * @param {TDOMEvent} e: DOM event object
 */
TAccMenu.prototype.doOut = function(e) {
  TDOM.removeClass(e.currentTarget, 'over');
  this.dispatch("mouseout", e.currentTarget);
}

/**
 * Fired when the user click a tab
 * @param {TDOMEvent} e: DOM event object
 */
TAccMenu.prototype.doClick = function(e) {
  var dt = e.currentTarget;
  this.dispatch("click", dt);
  this.setOpen(dt, !this.tabOpen(dt));
}

/**
 * Change the state of a tab to open/close
 * Set dt and his dd´s in open state
 * Use dt.open class in css to define styles on opened DT
 *
 * @param {Element | Integer} dt: dt document element or tab id
 * @param {Boolean} dt: dt document element or tab id
 */
TAccMenu.prototype.setOpen = function(dt, open) {
  dt = typeof dt == "string" ? this.getTab(dt) : dt;
  if (dt && dt.nodeName && dt.nodeName.toUpperCase() == 'DT') {
    // If open tab
    if (open) {
      if (this.tabOpen(dt)) { return; }
      if (!this.multiOpen) {
        for (var i = 0; i < this.opened.length; i++) {
          this.setOpen(this.opened[i], false);
        }
      }
      TDOM.addClass(dt, 'open');
      // Add it to the opened list
      this.opened.push(dt.id);
    // If close tab
    } else {
      if (!this.tabOpen(dt)) { return; }
      TDOM.removeClass(dt, 'open');
      // Remove it from opened list
      for (var i = 0; i < this.opened.length; i++) {
        if (this.opened[i] == dt.id) {
          this.opened.splice(i, 1);
          break;
        }
      }
    }
    // Stop any pending animation
    this.stopAnim(dt.id);
    var dd = TDOM.nextElement(dt);
    while (dd && dd.nodeName.toUpperCase() == 'DD') {
      open ? TDOM.addClass(dd, 'open') : TDOM.removeClass(dd, 'open');
      if (this.speed != 0) {
        this._anims[dt.id].push(this.initAnim(dd, open));
      } else {
        dd.style.display = 'block';
      }
      dd = TDOM.nextElement(dd);
    }
  }
}

/**
 * Stop any pending animation on tab
 * @param {Integer} id: tab id
 */
TAccMenu.prototype.stopAnim = function (id) {
  var anims = this._anims[id];
  if (anims && anims.length) {
    while (anims.length > 0) {
      var anim = anims.shift();
      window.clearInterval(anim);
    }
  }
}

/**
 * Initialize an open or close animation
 * @param {Element} dd: dd element
 */
TAccMenu.prototype.initAnim = function (dd, open) {
  var rec = TDOM.getBounds(dd);
  var to = open ? rec.height : 0;
  var from = open ? 0 : rec.height;
  var speed = this.speed;
  dd.style.overflow = 'hidden';
  if (open) {
    dd.style.height = '0px';
    dd.style.display = 'block';
  }
  // Start animation
  var anim = window.setInterval(
      function () {
        from = open ? from + speed : from - speed;
        if (open && from > to) {
          dd.style.height = '';
          window.clearInterval(anim);
          return;
        } else
        if (!open && from < to) {
          dd.style.display = 'none';
          dd.style.height = '';
          window.clearInterval(anim);
          return;
        }
        dd.style.height = from + 'px';
      }, 50);
  return anim;
}
