var IntoDocs = function ()
{
  var _sliderValue = 100;
  var _slider = null;
  var _tabs = $A();
  var _opened = '&#x25BC';
  var _closed = '&#x25B6';

  /**
   * Expands the table of contents so that it displays the currently selected
   * page
   */
  var _expandTree = function ()
  {
    var filename = window.location.pathname.split('/').last();
    var link = $('toc-content').down('a[href*="' + filename + '"]');

    // if the link was found
    if (link)
    {
      var parentListItem = link.up('div.info') ? link.up('div.info').up('li') : link.up('li');
      var parentList = link.up('ul');

      parentListItem.addClassName('selected');

      if (parentListItem.down('.toggle'))
      {
        parentListItem.down('.toggle').update(_opened);
        parentListItem.down('ul').show();
      }

      do
      {
        parentList.show();
        
        if (parentList.previous('.toggle'))
          parentList.previous('.toggle').update(_opened);

        parentList = parentList.up('ul');
      }
      while (parentList && parentList.readAttribute('id') != 'toc-content');
    }
  };

  /**
   * Makes the table of contents expandable by clicking the nodes
   */
  var _addTocClickObserver = function ()
  {
    // toggle nodes by clicking the icons
    $('toc-content').observe('click', function (evt)
    {
      var clicked = evt.findElement('span.toggle');

      if (clicked)
      {
        var hd = clicked.up('li');

        if (hd)
        {
          var lst = hd.down('ul');

          lst.toggle();
          clicked.update(lst.visible() ? _opened : _closed);
          _slider.redraw();
        }
      }
    });
  };

  /**
   * Adds observers that show and hide the toc-bar
   */
  var _addTocToggleObserver = function ()
  {
    var opts = {transition: 'easeOutExpo', duration: .5};
    var bar = $('bar');

    // show toc when the mouse is over the handle
    $('toc-handle').observe('click', function (evt)
    {
      var newLeft = parseInt(bar.offsetLeft) < 0 ? 0 : -$('toc').getWidth();
      bar.morph('left: ' + newLeft + 'px', opts);
    });

    $(document).observe('click', function (evt)
    {
      // if the toc is visible
      if (parseInt(bar.offsetLeft) >= 0)
      {
        if (!evt.findElement('#toc'))
          bar.morph('left: -' + $('toc').getWidth() + 'px', opts);
      }
    });
  };

  /**
   * Resizes the toc-bar to match the height of the parent element
   */
  var _resizeToc = function ()
  {
    var vpHeight = document.viewport.getHeight();

    $('toc').setStyle({height: vpHeight + 'px'});
    $('toc-scrollbar').setStyle({height: (vpHeight - 10) + 'px'});

    if (_slider)
      _slider.redraw();
  };

  /**
   * All browsers don't support :nth-child -pseudo-classes and doxygen outputs
   * poorly marked html, so let's add even- and odd-classes to all the table rows
   * in the document
   */
  var _zebrafyTables = function ()
  {
    $$('tr:nth-child(odd)').invoke('addClassName', 'odd');
    $$('tr:nth-child(even)').invoke('addClassName', 'even');
  };

  /**
   * Adds some custom buttons to textareas
   */
  var _buttonizeTextareas = function ()
  {
    $$('textarea.into-textarea').each(function (ta)
    {
      new IntoTextarea(ta, {plugins: [ 'Code' ]});
    });
  };

  /**
   *
   */
  var _addSpecializationObservers = function ()
  {
    /**
     * <div class="item-wrapper">
     *   <p class="title">foo</p>
     *   <div class="info" style="display: none"></div>
     * </div>
     */

    $('nav').observe('mouseover', function (evt)
    {
      var el = evt.findElement('div.item-wrapper');

      if (el && el != document)
      {
        var info = el.down('div.info');

        if (info && !info.visible())
        {
          // HACK HACK HACK!
          el.observe('mouseleave', function (evt2)
          {
            el.stopObserving('mouseleave');
            info.fade();
          });

          info.appear();
        }
      }
    });
  };

  /**
   *
   */
  var _initializeScrollbar = function ()
  {
    var parent = $('toc');
    var list = $('toc-content');
    
    _slider = new IntoSlider('toc-scrollbar',
    {
      orientation: 'vertical',
      value: {min: 0, max: 100, initial: _sliderValue, step: null},
      onSlide: function (values, slider)
      {
        var perc = (100 - parseFloat(values[0])) / 100;
        var height = list.getHeight() - parent.getHeight();
        var newTop = -perc * height;

        list.setStyle({top: newTop + 'px'});
        _sliderValue = values[0];
      },
      onChange: function (values, slider)
      {
        _sliderValue = values[0];
      }
    });

    var handler = function (evt)
    {
      _slider.setValue(_sliderValue + Event.wheel(evt) * 4);
      evt.stop();
    };

    $('toc').observe('mousewheel', handler).observe('DOMMouseScroll', handler);
  };

  /**
   * Scrolls the table of contents to currently selected page
   */
  var _scrollToSelected = function ()
  {
    var selected = $('toc-content').down('li.selected');

    if (selected)
    {
      var offset = selected.positionedOffset();
      var perc = offset[1] / $('toc-content').getHeight();
      var newVal = 100 - (100 * perc);

      _slider.setValue(newVal);
    }
  };

  /**
   * 
   */
  var _tabifyContent = function ()
  {
    var Tab = function (title, content)
    {
      this._title = title;
      this._content = content;
      this._el = null;
      this._id = content.readAttribute('id');

      this.getElement = function ()
      {
        if (!this._el)
        {
          this._el = new Element('li').
            update(this._title).
            observe('click', function (evt)
            {
              this.select();
            }.bind(this));
        }

        return this._el;
      };

      this.select = function ()
      {
        this._content.siblings().invoke('hide');
        this._content.show();
        this._el.siblings().invoke('removeClassName', 'selected');
        this._el.addClassName('selected');
      };

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

    var container   = $('content');
    var docTab      = $('docs');
    var noteTab     = $('notes');
    var title       = docTab.down('h2');
    var tabElements = $A([ docTab, noteTab ]);
    var tabRow      = new Element('ul', { id: 'tab-row' });
    var tabArea     = new Element('div', { id: 'tab-content' });

    container.insert(title);
    container.insert(tabRow).insert(tabArea);

    tabElements.each(function (el)
    {
      tabArea.insert(el);
      var titleEl = el.down('span.title');
      var tab = new Tab(titleEl.innerHTML, el);
      titleEl.remove();
      tabRow.insert(tab.getElement());
      _tabs.push(tab);
    });
  };

  /**
   * Selects a tab. If a corresponding tab can't be found, the first tab will
   * be selected.
   *
   * @param String hash The tab to be selected (the tabs indentifier)
   */
  var _selectTabByHash = function (hash)
  {
    var tabFound = false;

    _tabs.each(function (tab)
    {
      if (tab.getId() == hash)
      {
        tab.select();
        tabFound = true;
        throw $break;
      }
    });

    if (!tabFound)
      _tabs.first().select();
  };

  /**
   * Creates the navigation for a single page. The script searches for h3-tags,
   * grabs their contents and creates the navigation from them.
   */
  var _createPageToc = function ()
  {
    var docEl     = $('docs');
    var headings  = docEl.select('h3');
    var container = new Element('div', { id: 'page-toc' });
    var list      = new Element('ul');

    headings.each(function (el)
    {
      var listEl = new Element('li').
        update(el.innerHTML).
        observe('click', function (evt) { el.scrollTo(); });

      list.insert(listEl);
    });

    container.insert(new Element('p').update('Jump to')).insert(list);
    docEl.insert({ top: container });
  };

  return {

    init: function ()
    {
      try
      {
        var hash = window.location.hash.replace('#_', '');
        var isSearch = !!$('suggestions');

        _resizeToc();
        _addTocClickObserver();
        _addTocToggleObserver();
        _expandTree();
        _buttonizeTextareas();
        _zebrafyTables();
        _addSpecializationObservers();
        _initializeScrollbar();
        _scrollToSelected();

        if (!isSearch)
        {
          _tabifyContent();
          _createPageToc();
          _selectTabByHash(hash);
        }        

        Event.observe(window, 'resize', _resizeToc);
      }
      catch (e)
      {
        console.log(e);
      }
    },

    extendElements: function ()
    {
      Element.addMethods('div',
      {
        newspaperify: function (element)
        {
          if (!(element = $(element)))
            return;

          new NewspaperLayout(element, arguments[1] || {});

          return element;
        }
      });
    }
  }
}();
