/**
 * Depends on jQuery.
 */
function IBEMenuManager() {

  this.menuList = [];

  this.addMenu = function (tabMenu) {
    this.menuList.push(tabMenu);
  };

  this.findMenuWithId = function (id) {
    if (id) {
      for (var i = 0, l = this.menuList.length; i < l; i++) {
        if (this.menuList[i].id === id) {
          return this.menuList[i];
        }
      }
    }
    return null;
  };

  this.clickTabOnMenu = function (menuId, tabId) {
    var menu = this.findMenuWithId(menuId);
    if (menu) {
      menu.selectTabWithId(tabId);
    } else {
      ibeerror('IBEMenuManager trying to click tab of menu that does not exist, menu ID=' + menuId);
    }
  };

}

function IBETab(id, title, callback, url, cssClass) {
  // Constants
  var left = 'Left';
  var right = 'Right';
  var middle = 'Middle';
  var selected = 'Selected';

  // Constructor
  if (id === undefined || (id && typeof id !== 'string')) {
    ibeerror('Cannot create a tab without an ID.', id);
  }
  if (title && typeof title !== 'string') {
    ibeerror('Tab title must be a string', title);
  }
  if (typeof callback === 'function' || callback === undefined) {
    this.callback = callback;
  } else {
    ibeerror('Tab "' + title + '" has invalid callback.');
  }
  if (url && typeof url !== 'string') {
    ibeerror('Url must be of type string', url);
  }

  // Members
  this.title = title;
  this.selected = false;
  this.parentMenu = undefined;
  this.url = url;
  this.id = id;
  this.index = undefined;
  this.parentObject = undefined;
  this.callbackScope = window;

  // CSS stuff
  this.leftCss = cssClass ? cssClass + left : undefined;
  this.middleCss = cssClass ? cssClass + middle : undefined;
  this.rightCss = cssClass ? cssClass + right : undefined;
  this.leftCssSelected = cssClass ? cssClass + left + selected : this.leftCss;
  this.middleCssSelected = cssClass ? cssClass + middle + selected : this.middleCss;
  this.rightCssSelected = cssClass ? cssClass + right + selected : this.rightCss;

  // Td node objects
  this.tdLeft = undefined;
  this.tdMiddle = undefined;
  this.tdRight = undefined;

  this.getTitle = function() {
    return this.title;
  };

  this.setTitle = function(title) {
    this.title = title;
  };

  this.getId = function() {
    return this.id;
  };

  this.getIndex = function() {
    return this.index;
  };

  this.getUrl = function() {
    return this.url;
  };

  this.isSelected = function() {
    return this.selected;
  };

  this.setSelected = function(selected) {
    this.selected = selected;
  };

  /**
   * Internal callback function. Is executed by parent class to select the tab.
   * @param doCallback If true, the callback will be executed. Defaults to true if not specified.
   * @param forceRender Forces the updates of the tabs CSS classes.
   */
  this.selectTab = function (doCallback, forceRender) {
    if (doCallback === undefined) doCallback = true;
    var isOk = undefined;
    if (!this.isSelected()) {
      if (this.callback && doCallback) {
        isOk = this.callback.call(this.callbackScope, this.getIndex(), this, this.parentObject);
      }
      if (this.getUrl() && doCallback) {
        window.location = this.getUrl();
        isOk = true;
      }
      if (isOk === undefined) isOk = true;
      if (isOk) {
        if (this.parentMenu.renderTabOnClick || forceRender) {
          if (this.leftCssSelected) this.tdLeft.className = this.leftCssSelected;
          if (this.middleCssSelected) this.tdMiddle.className = this.middleCssSelected;
          if (this.rightCssSelected) this.tdRight.className = this.rightCssSelected;
        }
        this.setSelected(true);
      }

    } else {
      isOk = false;
    }
    return isOk;

  };

  this.unselectTab = function (forceRender) {
    if (this.parentMenu.renderTabOnClick || forceRender) {
      if (this.leftCss) this.tdLeft.className = this.leftCss;
      if (this.middleCss) this.tdMiddle.className = this.middleCss;
      if (this.rightCss) this.tdRight.className = this.rightCss;
    }
    this.setSelected(false);
  };

  this.setCallbackScope = function(obj) {
    this.callbackScope = obj;
  };

}

function IBETabMenu(args) {
  args = $.extend({
                    id : '',
                    placeHolder : '',
                    defaultTabIndex : 0,
                    runCallbackAtRender : false,
                    useHref : false               // Defines if we use href when using URL. Otherwise, use only window.location = ...
                  }, args);

  this.id = args.id;
  this.tabList = [];
  this.selectedIndex = undefined;
  this.defaultTabIndex = args.defaultTabIndex;

  this.startWithSeparator = false;

  this.separatorCss = 'tabMenuEmpty';
  this.tableCss = 'tabMenuTable';
  this.rowCss = 'tabMenuRow';

  // This can be set to false, then, if a tab is clicked, the CSS wont change. This is good when using URL instead of event.
  this.renderTabOnClick = true;

  // Tab default CSS (only used if tab has no CSS)
  this.leftCss = 'leftTab';
  this.rightCss = 'rightTab';
  this.middleCss = 'middleTab';
  this.leftCssSelected = 'leftTab selected';
  this.rightCssSelected = 'rightTab selected';
  this.middleCssSelected = 'middleTab selected';

  this.getSelectedIndex = function() {
    return this.selectedIndex;
  };

  this.setSelectedIndex = function(selectedIndex) {
    this.selectedIndex = selectedIndex;
  };

  this.setRenderOnClick = function(enabled) {
    this.renderTabOnClick = enabled;
  };

  this.addTab = function (tab) {
    this.tabList.push(tab);
    tab.parentMenu = this;
  };

  this.getTabAtIndex = function(index) {
    return this.tabList[index];
  };

  this.getTabWithId = function (id) {
    if (id) {
      for (var i = 0, l = this.tabList.length; i < l; i++) {
        if (this.tabList[i].id === id) {
          return this.tabList[i];
        }
      }
    }
    return null;
  };

  this.getTabIndexWithId = function (id) {
    if (id) {
      for (var i = 0, l = this.tabList.length; i < l; i++) {
        if (this.tabList[i].id === id) {
          return i;
        }
      }
    }
    return null;
  };

  this.getTabIndexWithTitle = function (title) {
    if (title) {
      for (var i = 0,l = this.tabList.length; i < l; i++) {
        if (this.tabList[i].getTitle() === title) {
          return i;
        }
      }
    }
    return null;
  };

  this.getFirstTab = function () {
    return this.tabList[0];
  };

  this.getLastTab = function () {
    return this.tabList[this.tabList.length - 1];
  };

  this.clearSelected = function () {
    for (var i = 0, l = this.tabList.length; i < l; i++) {
      this.tabList[i].setSelected(false);
      setElementsClass('tabMenuButton' + i + args.id, 'tabMenuButton');
    }
  };

  this.setDefault = function (id) {
    if (id) {
      for (var i = 0, l = this.tabList.length; i < l; i++) {
        var tab = this.tabList[i];
        if (tab.id === id) {
          this.setDefaultTabIndex(i);
          return;
        }
      }
    }
  };

  /**
   * Sets default tab to the tab with title=title. If success, returns true. If false, uses defaultIndex if it is set,
   * and then returns false.
   * @param title
   */
  this.setDefaultTabWithTitle = function (title, defaultIndex) {
    var index = this.getTabIndexWithTitle(title);
    if (!nullOrUndefined(index) && index >= 0) {
      this.setDefaultTabIndex(index);
      return true;
    } else {
      if (!nullOrUndefined(defaultIndex) && defaultIndex >= 0) this.setDefaultTabIndex(defaultIndex);
      return false;
    }
  };

  this.getDefaultTabIndex = function() {
    return this.defaultTabIndex;
  };

  this.setDefaultTabIndex = function (i) {
    this.defaultTabIndex = i;
  };

  this.getDefaultId = function() {
    return this.tabList[this.getDefaultTabIndex()].id;
  };

  this.render = function () {
    var that = this, placeHolder = getObj(args.placeHolder), emptytd;
    if (placeHolder == null) {
      ibeerror('Trying to render tab view to non existing element, id=', args.placeHolder);
      return;
    }

    // Table
    var table = document.createElement('table');
    table.id = args.id + 'Table';
    table.className = this.tableCss;
    table.cellSpacing = '0';
    table.cellPadding = '0';
    table.border = '0';

    // Tr
    var tr = table.insertRow(0);
    tr.className = this.rowCss;

    if (this.startWithSeparator) {
      emptytd = tr.insertCell(-1);
      emptytd.className = this.separatorCss;
    }

    for (var i = 0, l = this.tabList.length; i < l; i++) {

      if (i > 0) {
        // Add empty
        emptytd = tr.insertCell(-1);
        emptytd.className = this.separatorCss;
      }

      var tab = this.tabList[i];
      var tdL = tab.leftCss || this.leftCss ? tr.insertCell(-1) : undefined;
      var tdM = tr.insertCell(-1);
      var tdR = tab.rightCss || this.rightCss ? tr.insertCell(-1) : undefined;

      if (tdL) {
        if (!tab.leftCss) tab.leftCss = this.leftCss;
        if (!tab.leftCssSelected) tab.leftCssSelected = this.leftCssSelected;
        tdL.id = args.id + '_Button_' + i + '_Left';
        $(tdL).bind('click', {that:that, tabIndex:i}, that.selectTabEvent);
      }

      tdM.id = args.id + '_Button_' + i + '_Middle';
      if (!tab.middleCss) tab.middleCss = this.middleCss;
      if (!tab.middleCssSelected) tab.middleCssSelected = this.middleCssSelected;
      $(tdM).bind('click', {that:that, tabIndex:i}, that.selectTabEvent);
      if (tab.getUrl() && args.useHref) {
        tdM.innerHTML = '<a href="' + tab.getUrl() + '">' + tab.getTitle() + '</a>';
      } else {
        tdM.innerHTML = tab.getTitle();
      }

      if (tdR) {
        if (!tab.rightCss) tab.rightCss = this.rightCss;
        if (!tab.rightCssSelected) tab.rightCssSelected = this.rightCssSelected;
        tdR.id = args.id + '_Button_' + i + '_Right';
        $(tdR).bind('click', {that:that, tabIndex:i}, that.selectTabEvent);
      }

      // Store td nodes.
      tab.tdLeft = tdL;
      tab.tdMiddle = tdM;
      tab.tdRight = tdR;

      // Appends 'first' and 'last' css class to the tabs.
      if (i == 0) {
        tab.leftCss += ' first';
        tab.leftCssSelected += ' first';
        tab.middleCss += ' first';
        tab.middleCssSelected += ' first';
        tab.rightCss += ' first';
        tab.rightCssSelected += ' first';
      } else if (l > 1 && i === (l - 1)) {
        tab.leftCss += ' last';
        tab.leftCssSelected += ' last';
        tab.middleCss += ' last';
        tab.middleCssSelected += ' last';
        tab.rightCss += ' last';
        tab.rightCssSelected += ' last';
      }

      tab.index = i;

      tab.unselectTab(true); // Activate unselected CSS
    }

    while (placeHolder.firstChild) placeHolder.removeChild(placeHolder.firstChild);
    placeHolder.appendChild(table);
    disableSelection(table);
    this.selectTabWithIndex(this.getDefaultTabIndex(), args.runCallbackAtRender, undefined, true);
  };

  /**
   * @param ev is an jQuery event.
   */
  this.selectTabEvent = function (ev) {
    var data = ev.data, doCallback = true;
    if (data === undefined || (data && data.tabIndex === undefined)) {
      ibewarning("Trying trigger tab select event, without any data passed along with the event.", ev);
    } else {
      data.that.selectTabWithIndex(data.tabIndex, doCallback);
    }
  };

  this.getSelectedTabIndex = function() {
    return this.selectedTabIndex;
  };

  this.setSelectedTabIndex = function(index) {
    this.selectedTabIndex = index;
  };

  /**
   * This function is run when a user clicks on a tab. If callback returns false, selectOk will be false and tab switch
   * will be cancelled.
   * @param index The index of the tab that has been clicked.
   * @param doCallback Defines whether we should run the callback or not. When selected default tab at render, this is
   * false.
   * @param forceSelect If true, we ignore callback result and switch tab regardless.
   * @param forceRender Forces the update of tabs CSS classes, regardless of anything else.
   */
  this.selectTabWithIndex = function (index, doCallback, forceSelect, forceRender) {
    if (index === undefined) {
      ibewarning("Trying to select a tab with index, but index is undefined");
    } else {
      if (index != this.getSelectedTabIndex()) {
        var indexToUnselect = this.getSelectedTabIndex();
        if (this.tabList[index]) {

          var selectOk = this.tabList[index].selectTab(doCallback, forceRender);

          if (selectOk || forceSelect) {
            this.setSelectedTabIndex(index);
            if (indexToUnselect >= 0 && indexToUnselect != this.getSelectedTabIndex()) {
              this.tabList[indexToUnselect].unselectTab();
            }
          } else {
            ibelog('Tab selection was cancelled by callback.');
          }
        } else {
          ibewarning('Trying to select tab which doesn\'t exist, index=' + index);
        }
      } else {
        ibelog('Trying to select tab that is already selected.');
      }
    }
  };

  this.selectTabWithId = function (id, doCallback, forceRender) {
    var i = this.getTabIndexWithId(id);
    if (i != null) {
      this.selectTabWithIndex(i, doCallback, forceRender);
    }
  };

  this.selectTabWithTitle = function (title, doCallback, forceRender) {
    var i = this.getTabIndexWithTitle(title);
    if (i != null) {
      this.selectTabWithIndex(i, doCallback);
    }
  };

  this.tabCount = function() {
    return this.tabList.length;
  };

  this.hasTabs = function() {
    return this.tabCount() > 0;
  };

  this.hasMoreThanOneTab = function() {
    return this.tabCount() > 1;
  };
}

