function SuperSearchControl()
{
  var _onSearch = null;
  var _element = $(document.createElement("div"));
  var _typeBox = $(document.createElement("div"));
  var _goBox = $(document.createElement("div"));
  var _type = $(document.createElement("input"));
  var _go = $(document.createElement("button"));

  _typeBox.addClass("typeBox");
  _goBox.addClass("goBox");
  _element.addClass("superSearch");


  _type.bind("click", function (e) { this.focus(); this.select(); });
  _type.bind("keydown", function (e) { if (e.keyCode == 13) { _go.trigger("click"); return false; } });

  _go.bind("click", function (e) { if (_onSearch) { _onSearch(e, _type.val()); } } );

  _typeBox.append(_type);
  _goBox.append(_go);
  _element.append(_goBox, _typeBox);

  this.getNode = function() { return _element[0]; }
  this.searchText = function(value) { if (_type.attr("value") != value) { _type.attr("value", value); } }
  this.onSearch = function(callback) { _onSearch = callback; }
}

function SearchCompanion()
{
  var _element = $(document.createElement("div"));
  _element.addClass("searchCompanion");

  this.getNode = function() { return _element; }
}

function SearchResult(verseRef, contents, term, occurrences, transArray)
{
  var _self = this;
  this._onResultBookmark = null;
  this._onResultNavigate = null;

  var _element = $(document.createElement("div"));
  var _title = $(document.createElement("h5"));
  var _para = $(document.createElement("p"));
  var _meta = $(document.createElement("div"));
  var _metaOccurs = $(document.createElement("span"));
  var _metaTrans = $(document.createElement("span"));
  var _metaBookmark = $(document.createElement("a"));

  _element.addClass("result");
  _meta.addClass("meta");
  _metaOccurs.addClass("occurrences");
  _metaTrans.addClass("concordance");

  _title.bind("click", function () { if (_self._onResultNavigate) { _self._onResultNavigate(VerseReference.fromVerseId(verseRef)); } else { alert("Sorry, this feature is not yet available"); } } );
  _title.text(VerseReference.fromVerseId(verseRef).toString());
  var terms = term.split(" ");
  for (var i=0; i<terms.length; i++)
  {
    contents = contents.replace(new RegExp("\\b(" + terms[i] + ")\\b", "gi"), function (match, term) { return "<span class=\"hit\">" + term + "</span>"} );
  }
  _para.html(contents);
  _metaBookmark.bind("click", function () { if (_self._onResultBookmark) { _self._onResultBookmark(VerseReference.fromVerseId(verseRef)); } else { alert("Sorry, this feature is not yet available"); } } );
  _metaBookmark.text("Bookmark");

  if (transArray != null && transArray.length == occurrences)
  {
    for (var i=0; i<occurrences; i++)
    {

      if (transArray[i] == null || transArray[i] == "")
      {
        var concordance = $(document.createElement("a"));
        concordance.text(transArray[i].toUpperCase());
        _metaTrans.append(concordance);
      }
      else
      {
        _metaTrans.append(concordance);
      }
    }
  }
  else
  {
    _metaTrans.text("Concordance references unavailable");
  }

  if (occurrences == 1) { _metaOccurs.text(occurrences.toString() + " occurrence"); }
  else { _metaOccurs.text(occurrences.toString() + " occurrences"); }

  _meta.append(_metaOccurs, " - ", _metaTrans, " - ", _metaBookmark)
  _element.append(_title, _para, _meta);

  this.getNode          = function() { return _element; }
  this.onResultBookmark = function (callback) { this._onResultBookmark = callback; }
  this.onResultNavigate = function (callback) { this._onResultNavigate = callback; }
}

function Paginator()
{
  var _self = this;
  this._onPageSelected = null;

  this._element = $(document.createElement("div"));
  this._prev = $(document.createElement("a"));
  this._list = $(document.createElement("ol"));
  this._next = $(document.createElement("a"));

  this._element.addClass("paginator");
  this._prev.addClass("prev");
  this._next.addClass("next");

  this._prev.html("&laquo;");
  this._next.html("&raquo;");

  this.displayPages = function(totalPages, currentPage, pageLimit)
  {
    this._element.empty();
    this._list.empty();

    if (totalPages > 1)
    {
      var startPage = 1;
      var endPage = totalPages;
      var logicalLimit = (pageLimit > totalPages) ? totalPages : pageLimit;
      var halfPages = Math.floor(logicalLimit / 2);

      if (currentPage <= halfPages)
      {
        endPage = logicalLimit;
      }
      else if (endPage - halfPages <= currentPage)
      {
        startPage = totalPages - logicalLimit  + 1;
        if (startPage < 1) {startPage = 1; }
        endPage = totalPages;
      }
      else
      {
        startPage = currentPage - halfPages;
        endPage = currentPage + halfPages;
      }

      if (currentPage == 1)
      {
        this._prev.addClass("disabled");
        this._prev.attr("title", "There are no prior results to display");
        this._prev.unbind();
      }
      else
      {
        this._prev.removeClass("disabled");
        this._prev.data("page", currentPage - 1);
        this._prev.attr("title", "Go to page " + (currentPage - 1));
        this._prev.bind("click", function () { var page = $(this).data("page"); if (_self._onPageSelected) { _self._onPageSelected(page); } } );
      }

      if (currentPage == endPage)
      {
        this._next.addClass("disabled");
        this._next.attr("title", "There are no further results to display");
        this._next.unbind();
      }
      else
      {
        this._next.removeClass("disabled");
        this._next.data("page", currentPage + 1);
        this._next.attr("title", "Go to page " + (currentPage + 1));
        this._next.bind("click", function () { var page = $(this).data("page"); if (_self._onPageSelected) { _self._onPageSelected(page); } } );
      }

      for (var i=startPage; i<=endPage; i++)
      {
        var pageContainer = $(document.createElement("li"));
        var page = $(document.createElement("a"));
        if (i == currentPage)
        {
          pageContainer.addClass("current");
        }
        else
        {
          page.attr("title", "Results page " + i.toString());
          page.bind("click", function() { var page = $(this).data("page"); if (_self._onPageSelected) { _self._onPageSelected(page); } } );
        }
        page.data("page", i);
        page.text(i.toString());
        pageContainer.append(page);
        this._list.append(pageContainer);
      }
      this._element.append(this._prev, this._list, this._next);
    }
    else
    {
      //this._element.html("<em>No further results</em>");
    }

  }

  this.addClass = function(className) { return this._element.addClass(className); }
  this.getNode = function() { return this._element; }
  this.onPageSelected = function(callback) { this._onPageSelected = callback; }
}

function BibleFilter()
{
  var _self = this;
  var _onFilter = null;

  this._element = $(document.createElement("div"));
  this._bookFilter = $(document.createElement("div"));
  this._tstFilter = $(document.createElement("div"));
  this._noFilter = $(document.createElement("div"));
  this._tooltip = $(document.createElement("div"));

  this._element.addClass("filter");
  this._bookFilter.addClass("books");
  this._tstFilter.addClass("testaments");
  this._noFilter.addClass("all");
  this._tooltip.addClass("tooltip");

  var oldNode = $(document.createElement("div")).addClass("testament old");
  var newNode = $(document.createElement("div")).addClass("testament new");

  this._tstFilter.append(oldNode, newNode);
  this._element.append(this._bookFilter, this._tstFilter, this._noFilter);

  this.displayFilter = function(distribution)
  {
    var oldCount = 0;
    var newCount = 0;
    var totalHits = 0;
    var maxPerBook = 0;
    var oldEnabled = false;
    var newEnabled = false;

    this._bookFilter.empty();
    this._bookFilter.append(this._tooltip);

    for (var i=0; i<distribution.length; i++)
    {
      var hits = distribution[i][2];

      if (hits > maxPerBook)
      {
        maxPerBook = hits;
      }

      totalHits += hits;
    }

    var bibleBooks = Bible.Books();

    for (var i=0; i<bibleBooks.length; i++)
    {
      var bookHits = 0;
      for (var j=0; j<distribution.length; j++)
      {
        if (bibleBooks[i].bookId() == distribution[j][0])
        {
          if (bibleBooks[i].bookId() < 40)
          {
            oldCount += distribution[j][2];
            oldEnabled = true;
          }
          else
          {
            newCount += distribution[j][2];
            newEnabled = true;
          }
          bookHits += distribution[j][2];
        }
      }
      var height = Math.ceil((20 / maxPerBook) * bookHits);

      var bookNode = $(document.createElement("div"))
      bookNode.addClass("book");

      if (bookHits > 0)
      {
        var hitsNode = $(document.createElement("div")).addClass("hits filterable");
        hitsNode.css("height", height.toString() + "px");
        hitsNode.css("margin-top", (20 - height).toString() + "px");
        var hitsText = (bookHits == 1) ? "hit" : "hits";
        bookNode.addClass("filterable");
        bookNode.data("book", bibleBooks[i].bookId());
        bookNode.data("title", bookHits.toString() + " " + hitsText + " in " + bibleBooks[i].text() + " (" + Math.round((100 / totalHits) * bookHits).toString()  + "%)");
        bookNode.bind("mousemove", function () { _self._tooltip.css("display", "block"); _self._tooltip.text($(this).data("title")); } );
        bookNode.bind("click", function() { if (_self.onFilter) { _self._onFilter("book", $(this).data("book")) }});
        bookNode.append(hitsNode);
      }
      else
      {
        bookNode.bind("mouseenter", function () { _self._tooltip.css("display", "none"); } );
      }

      this._bookFilter.append(bookNode);
    }

    if (oldEnabled)
    {
      oldNode.addClass("filterable");
      oldNode.bind("click", function() { if (_self.onFilter) { _self._onFilter("testament", "old") }});
      oldNode.bind("mousemove", function () { _self._tooltip.css("display", "block"); _self._tooltip.text("Filter by Old Testament (" + oldCount + " hits)"); } );
    }
    else
    {
      oldNode.removeClass("filterable");
    }

    if (newEnabled)
    {
      newNode.addClass("filterable");
      newNode.bind("click", function() { if (_self.onFilter) { _self._onFilter("testament", "new") }});
      newNode.bind("mousemove", function () { _self._tooltip.css("display", "block"); _self._tooltip.text("Filter by New Testament (" + newCount + " hits)"); } );
    }
    else
    {
      newNode.removeClass("filterable");
    }

    this._noFilter.unbind("mousemove");
    if (totalHits > 0)
    {
      this._noFilter.addClass("filterable");
      this._noFilter.bind("click", function() { if (_self.onFilter) { _self._onFilter("none", "all") }});
      this._noFilter.bind("mousemove", function () { _self._tooltip.css("display", "block"); _self._tooltip.text("No filter, show all " + totalHits.toString() + " results"); } );
    }
    else
    {
      this._noFilter.removeClass("filterable");
    }

    this._element.unbind();
    this._element.bind("mousemove", function (e) { try { _self._tooltip.css("left", e.pageX + "px"); _self._tooltip.css("top", e.pageY + "px"); } catch (err) { alert(err); } } );
    this._element.bind("mouseout", function () { _self._tooltip.css("display", "none"); } );
  }

  this.getNode  = function() { return this._element; }
  this.onFilter = function(callback) { this._onFilter = callback; }
}

function SearchControl(id)
{
  var _self = this;
  this._term = null;
  this._resultsPerPage = 10;
  this._maxPages = 9;
  this._filterType = null;
  this._filterValue = null;
  this._onSearch = null;
  this._onResultBookmark = null;
  this._onResultNavigate = null;
  this._onResultRequest = null;

  this._element = $(document.createElement("div"));
  this._searchInfoBar = $(document.createElement("div"));
  this._searchResults = $(document.createElement("div"));
  this._superSearch = new SuperSearchControl();
  this._searchFilter = new BibleFilter();
  this._upperPaginator = new Paginator();
  this._lowerPaginator = new Paginator();

  this._element.attr("id", id);
  this._element.addClass("pane");
  this._searchInfoBar.addClass("infoBar");
  this._searchResults.attr("id", "searchResults");
  this._superSearch.onSearch(function (e, term) { if (_self._onSearch) { _self._term = term; _self._onSearch(term, 0, 10); } });
  this._element.append(this._superSearch.getNode(), this._searchResults);

  this._upperPaginator.addClass("upper");
  this._lowerPaginator.addClass("lower");

  this.displayResults = function(results)
  {
    this._searchResults.empty();
    this._searchInfoBar.html("<div>Results <strong>" + (results.offset + 1).toString() + "-" + (results.offset + results.limit).toString() + "</strong> of <strong>" + results.occurs.toString() + "</strong> matches from <strong>" + results.verses.toString() + "</strong> verses (" + results.time.toString() + " seconds)</div>");
    var companion = new SearchCompanion();

    this._searchFilter.displayFilter(results.distribution);

    var currentPage = (results.offset > 0) ? Math.floor(results.offset / this._resultsPerPage) + 1: 1;
    var totalPages = (results.matches > 0) ? Math.ceil(results.matches / this._resultsPerPage): 1;
    this._upperPaginator.displayPages(totalPages, currentPage, this._maxPages);
    this._lowerPaginator.displayPages(totalPages, currentPage, this._maxPages);

    this._searchResults.append(this._searchInfoBar, companion.getNode(), this._searchFilter.getNode(), this._upperPaginator.getNode())

    for (var i=0; i<results.data.length; i++)
    {
      try
      {
        var result = new SearchResult(results.data[i][0], results.data[i][1], results.term, results.data[i][2], null);

        result.onResultBookmark(function (ref) { if (_self._onResultBookmark) { _self._onResultBookmark(ref); } });
        result.onResultNavigate(function (ref) { if (_self._onResultNavigate) { _self._onResultNavigate(ref); } });

        this._searchResults.append(result.getNode());
      }
      catch (err)
      {
        alert(err);
      }
    }
    this._searchResults.append(this._lowerPaginator.getNode())
  }

  this.refreshResults = function(filterType, filterValue)
  {
    _self._filterType = filterType;
    _self._filterValue = filterValue;

    if (_self._onSearch)
    {
      _self._onSearch(_self._term, 0, _self._resultsPerPage, filterType, filterValue);
    }
  }

  this.refreshPagedResults = function(page)
  {
    var offset = (page - 1) * _self._resultsPerPage;

    if (_self._onSearch)
    {
      _self._onSearch(_self._term, offset, _self._resultsPerPage, _self._filterType, _self._filterValue);
    }
  }

  this._searchFilter.onFilter(this.refreshResults);
  this._upperPaginator.onPageSelected(this.refreshPagedResults);
  this._lowerPaginator.onPageSelected(this.refreshPagedResults);

  this.getNode          = function() { return this._element[0]; }
  this.getContentNode   = function() { return this._searchResults[0]; }
  this.searchText       = function(value) { _self._term = value; return this._superSearch.searchText(value); }
  this.onSearch         = function (callback) { this._onSearch = callback; }
  this.onResultBookmark = function (callback) { this._onResultBookmark = callback; }
  this.onResultNavigate = function (callback) { this._onResultNavigate = callback; }
}
