/**
 * Manage browsing history and breadcrum UI elements.
 * Allow to query and navigate in the history.
 * Supports browser Back/Forward buttons, but
 * can also be used to go navigate by code using go().
 *
 * Current implementation:
 * Uses "app.html#detail=123" URLs to put in the browser history.
 */
function BrowsingHistory() {
  this._list = [ new BrowsingHistoryEntryMainpage() ];

  var self = this;
  window.addEventListener("hashchange", function(ev) {
    try {
      self.onLocationChange(ev);
    } catch (e) { showError(e); }
  }, false);
}
BrowsingHistory.prototype = {
  _list : null, // {Array of BrowsingHistoryEntry}

  /**
   * @returns {BrowsingHistoryEntry}
   */
  get current() {
    return this._list[this._list.length - 1]; // last array entry
  },
  /**
   * @returns {BrowsingHistoryEntryMainpage}
   */
  get mainPage() {
    return this._list[0];
  },
  /**
   * @param obj {Detail}
   * @returns {BrowsingHistoryEntry}   already existing entry for obj
   *     or null
   */
  haveObj : function(obj) {
    this._list.filter(function(he) { return he.obj == obj; })[0];
  },

  /**
   * Called only by editor.js
   */
  goBack : function() {
    window.history.back();
  },

  /**
   * Append |obj| to history
   * @param obj {Detail}
   * @returns {BrowsingHistoryEntry}
   */
  add : function(obj) {
    var he = new BrowsingHistoryEntry(obj)
    this._list.push(he);
    window.location = this.makeURL(obj);
    return he;
  },

  /**
   * Like add(), just that the current history item
   * is replaced with this new |obj|.
   * @param obj {Detail}
   * @returns {BrowsingHistoryEntry}
   */
  replace : function(obj) {
    this.current._remove();
    var he = new BrowsingHistoryEntry(obj);
    this._list.push(he);
    window.location.replace(this.makeURL(obj));
    return he;
  },

  reload : function() {
    if (this.current.obj) {
      var objID = this.current.objID;
      this.current.obj = gStorage.getID(objID) || new BibleText(objID);
    }
    this.current.go();
  },

  makeURL : function(obj) {
    //var url = window.location.href.replace("#.*", "");
    var url = "";
    if (obj) {
      return url + "#detail=" + obj.id;
    } else {
      return url;
    }
  },

  onLocationChange : function(ev) {
    var objID;
    var ref = window.location.hash;
    //console.log("ref change to " + ref);
    var query = parseURLQueryString(window.location.search);
    if (ref && ref.substr(0, 8) == "#detail=") {
      objID = ref.substr(8);
    } else if (query.detail) {
      objID = query.detail;
    }

    // case: reload on mainpage
    if ( !objID) {
      this.mainPage.go();
      return;
    }

    // case: we changed window.location ourselves and
    // the event here unfortunately fires for it
    if (objID == this.current.objID) {
      return;
    }

    // case: going back
    // find in current internal history list
    for (var i = 0; i < this._list.length; i++) {
      var he = this._list[i];
      if (he.objID == objID) {
        he.go();
        return;
      }
    }

    // case: bookmark
    var obj = gStorage.getID(objID);
    if (obj) {
      showDetail(obj);
      return;
    }

    // case: bookmark for bible text
    try {
      var bt = new BibleText(objID);
      if (bt) {
        showDetail(bt);
        return;
      }
    } catch (e) {} // not a bible text

    gBrowsingHistory.mainPage.go();
    console.error("Obj ID " + objID + " not found");
  },
}

function BrowsingHistoryEntry(obj) {
  assert(obj && obj instanceof Detail);
  this.obj = obj;
  var self = this;

  // save scroll position of previous object
  var cur = gBrowsingHistory.current;
  if (cur && !cur.scrollPosY) {
    cur.scrollPosY =
        window.scrollY || window.pageYOffset;
  }

  this.$breadcrum = uiObjButton({
    $container: $("#breadcrums"),
    classes : "breadcrum",
    obj : self.obj,
    navFunc : function() {
      self.go();
    },
  });
  this.$breadcrum.prop("historyEntry", this);
}
BrowsingHistoryEntry.prototype = {
  get objID() {
    return this.obj.id;
  },

  go : function() {
    // User clicked on us and is thus going back in history
    // Remove this breadcrum and all later ones
    this._remove();
    // Go to stored object
    showDetail(this.obj);

    // scroll to old position
    if (this.scrollPosY) {
      window.scroll(0, this.scrollPosY);
    }
  },
  _remove : function() {
    // remove this list entry and all later ones
    var list = gBrowsingHistory._list;
    for (var i = 0; i < list.length; i++) {
      //console.log("checking i " + i);
      if (list[i] == this) {
        //console.log("remvoing " + this.obj.name + " i " + i);
        list.splice(i, list.length - i); // remove all from here on
        break;
      }
    }

    // Remove this breadcrum and all later ones
    this.$breadcrum.nextAll().remove();
    this.$breadcrum.remove();
  },
}

function BrowsingHistoryEntryMainpage() {
  this.obj = null;
}
BrowsingHistoryEntryMainpage.prototype = {
  get objID() {
    return "mainpage";
  },

  go : function() {
    this._remove();
    showMainPage();
  },
  _remove : function() {
    var list = gBrowsingHistory._list;
    list.splice(1, list.length - 1);
    $("#breadcrums").empty();
  },
}
extend(BrowsingHistoryEntryMainpage, BrowsingHistoryEntry);
