/**
 * this library just exports the public function
 * $.tableOfContents
 */
(function($) {
  $.tableOfContents = {
    defaults: {
      tpl: "<div class='toc'><p>Contents</p><ol class='top'>%@</ol></div>",
      selector: ":header",
      styles: {
        ".toc": {
          "background-color": "#efefef",
          "border": "1px solid gray",
          "font-size": "0.85em",
          "padding": "5px",
          "text-align": "left",
          "width": "350px",
          "margin": "10px 0"
        },
        ".toc p": {
          "text-align": "center",
          "font-weight": "bold",
          "margin": 0
        },
        ".toc > ol": {
          "padding": "0 1ex"
        },
        ".toc .top li": {
          "list-style-type": "none",
          "line-height": "1.2em",
          "margin": "1ex 0"
        },
        ".toc-item-level-1": {
          "width": "100%",
          "border-bottom": "1px solid gray",
          "font-size": "23px",
          "font-weight": "normal"
        },
        ".toc-item-level-2": {
          "font-size": "16px",
          "font-weight": "bold"
        },
        ".toc-item-level-3": {
          "font-size": "16px",
          "font-weight": "bold"
        },
        ".toc-item-level-4": {
          "font-size": "15px",
          "font-weight": "normal"
        },
        ".toc-item-level-5": {
          "font-size": "15px",
          "font-weight": "normal"
        }
      }
    }
  };

  var TOCItem = (function() {
    var getTOCItemId = function(el) {
      return $(el).html()
      .toLowerCase()
      .replace(/[^a-z0-9]/g,"-")  // replace all non alpha-numerics w/ "-"
      .replace(/^(-)*|(-)*$/g,'') // trim leading/trailing "-"
      .replace(/[-]{2,}/g,'-')    // squish consecutive "-"
    };

    var _constructor = function(el) {
      this.tagName = el.tagName;
      this.children = [];
      this.parent = null;
      this.el = el;
      this.id = getTOCItemId(el);

      $(el).prepend("<a name='%@'></a>".fmt(this.id));
    };

    _constructor.prototype = {
      tagName: null,
      children: null,
      parent: null,
      el: null,
      tpl: "<li class='toc-item %@'><a href='#%@'>%@) %@</a>%@</li>",

      compareTo: function(tocItem) {
        if (tocItem.tagName == this.tagName) return 0;
        if (tocItem.tagName > this.tagName) return 1;
        return -1
      },

      canAddItem: function(tocItem) {
        var ret = this.compareTo(tocItem) > 0;
        return ret;
      },

      lastChild: function() {
        return this.children.slice(-1)[0];
      },

      hasChildren: function() {
        return this.children.length > 0;
      },

      addItem: function(tocItem) {
        if (!this.canAddItem(tocItem)) return false;
        
        if (this.lastChild() && this.lastChild().canAddItem(tocItem)) {
          return this.lastChild().addItem(tocItem);
        }

        this.children.push(tocItem)
        tocItem.parent = this;

        return true;        
      },

      html: function() {
        var childHtml = '';

        if (!arguments.callee._count) arguments.callee._count = 0;
        var count = arguments[0] || ++arguments.callee._count;

        if (this.hasChildren()) {
          childHtml = "<ol>%@</ol>".fmt(this.children.map(function(i) {
            if (!arguments.callee._count) arguments.callee._count = 0;

            var cntInner = "%@.%@".fmt(count,++arguments.callee._count);
            return i.html(cntInner);
          }).join(''));
        }

        // total hack to figure out what menu-level this is
        $(this.el)
          .addClass("toc-item-level-%@".fmt(count.toString().split(".").length))
          .addClass("toc-item");

        return this.tpl.fmt(this.tagName.toLowerCase(),
                            this.id,
                            count,
                            $(this.el).text(),
                            childHtml);
      }
    };

    return _constructor;
  }());

  var tocBuild = function(el,settings) {
    var hEls = $(el).find(settings.selector),
      list = [],
      current;

    $(hEls).each(function(i) {
      var item = new TOCItem($(this)[0])
      if (current && current.addItem(item)) return;

      current = item;
      list.push(item);
    });

    if (list.length == 0) return false;
    return settings.tpl.fmt(list.map(function(i) { return i.html(); }).join(""));
  };

  $.fn.extend({
    tableOfContents: function(settings) {
      settings = $.extend({}, $.tableOfContents.defaults, settings);

      var html = tocBuild(this, settings);

      if (html) {
        $(this).prepend(html);

        var styles = settings.styles;
        for (var sel in styles) if (styles.hasOwnProperty(sel) && styles[sel]) {
          $(this).find(sel).css(styles[sel]);
        }

        $(this).find(".toc-item a")
          .css("text-decoration","none")
          .hover(
            function(evt) { $(this).css("text-decoration","underline"); },
            function(evt) { $(this).css("text-decoration","none"); }
          );
      }
    }
  });
}(jQuery));
