/**
 * This is the central UI point that changes pages.
 * The app starts up here, too.
 */

var gStorage = null; // {Storage}
var gBrowsingHistory = null; // {BrowsingHistory}
var gDevice = null; // {Device}
//const Leaflet = L.noConflict(); -- L needed by Leaflet.Label plugin
var gSettings = null; // {localStorage} or {Object}

/**
 * Primary app startup.
 */
function onLoad() {
  try {
    $("#loading-page, #main-page, #main-sub-page, " +
      "#detail-page, #header").removeAttr("hidden");
    changePage("loading", null);

    // Set title early, before loading data files
    $("title").text(tr("mainpage.title", [ tr("appname") ]));
    showLangChangeUI();

    gSettings = getSettingsObject();
    gDevice = new Device();
    gBrowsingHistory = new BrowsingHistory()

    loadMainDBInLang(detectLanguage(), function() {
      gBrowsingHistory.onLocationChange();
    }, showError);
  } catch (e) { showError(e); }
}
$(document).ready(function() {
  // runAsync needed for XULRunner minbrowser.
  runAsync(onLoad, showError);
});


var gLastError = null;

function showError(e) {
  var msg = e.message ? e.message : e + "";
  if (gLastError == msg) {
    return; // don't show again;
  }
  gLastError = msg;

  var $errorBox = $("<div class='error-box box'/>").appendTo($("#header"));
  var $errorMsg = $("<span class='error-msg'/>").appendTo($errorBox)
      .text(msg);

  function moreInfo() {
    var text = msg;
    text += "\n\n";
    if (e.code) {
      text += "Error code: " + e.code + "\n";
    }
    if (e.uri) {
      text += "URL: <" + e.uri + ">\n";
    }
    if (e.stack) {
      text += tr("error.stack") + "\n" + e.stack + "\n\n";
    }
    alert(text);
  }
  function close() {
    gLastError = null;
    $errorBox.slideUp({
      complete : function() {
        $errorBox.remove();
      },
    });
  }

  var $errorMore = uiButton({
    $container : $errorBox,
    title : "More info",
    classes : "error-more",
    clickCallback : moreInfo,
    errorCallback : function() {},
  });
  var $errorClose = uiButton({
    $container : $errorBox,
    title : "x",
    classes : "error-close",
    clickCallback : close,
    errorCallback : function() {},
  });

  // close automatically
  setTimeout(close, 5 * 1000); // 5s
}

function errorNonCritical(e) {
  var msg = e;
  if (e.stack) {
    msg += "\n\n" + tr("error.stack") + "\n" + e.stack;
  }
  console.log(msg);
}


/**
 * Shows an object full-page, and adds it to browsing history.
 * This is the main function steering the application.
 *
 * This is passed around as |navFunc|.
 *
 * @param obj {Detail}   what to show full-page
 * @param objList {Array of Detail}   (Optional)
 *    If this object is one of a longer list, allow to browse the others quickly
 * @param objListTitle {String}   (Optional)
 *    User-visible title for objList
 * @param p can contain:
 * @param historyReplaceLast {Boolean}
 *    The new object shall not be added to the end of the
 *    browsing history, but replace the current obj
 */
function showDetail(obj, objList, objListTitle, p) {
  try {
    assert(obj instanceof Detail, "obj: need Detail");

    var alreadyInHistory = gBrowsingHistory.haveObj(obj);
    if (alreadyInHistory) {
      alreadyInHistory.go();
      return;
    }
    if (p && p.historyReplaceLast) {
      gBrowsingHistory.replace(obj);
    } else {
      gBrowsingHistory.add(obj);
    }
    var oldScrollPos = window.scrollY;

    // display obj
    $("title").text(tr("detail.title", [ tr("appname"), obj.name ]));
    $("#detail").empty();

    window.scroll(0, 0);
    // workaround for Mozilla bug 951746
    //alert("TODO old scroll pos is " + oldScrollPos + "px");
    if (gDevice.browserVendor == "mozilla" && gDevice.deviceType == "mobile" &&
        oldScrollPos > gDevice.height) {
      setTimeout(function() {
        window.getComputedStyle($("#detail").get(0));
        window.scroll(0, 0);
        //TODO HACK This alert() itself is the workaround
        alert("TODO old scroll pos was " + oldScrollPos + "px");
      }, 0);
    }

    uiDetail({
      container: $("#detail"),
      obj : obj,
      navFunc: showDetail,
      errorCallback: showError,
      storage : gStorage,
    });

    changePage("detail", obj);
    console.log("showed detail " + obj.name);

    showHeaderList(obj, objList, objListTitle);
  } catch (e) { showError(e); }
}

function changePage(pageName, obj) {
  assert(typeof(pageName) == "string");
  assert(!obj || obj instanceof Detail);

  $("#loading-page").hide();
  $("#main-page").hide();
  $("#detail-page").hide();
  $("#main-sub-page").hide();
  $("#header-box").show();
  $("#breadcrums").hide();
  if (pageName == "detail") {
    $("#detail-page").show();
    $("#app-title").hide();
    $("#breadcrums").show();
  } else if (pageName == "main") {
    $("#main-page").show();
    $("#app-title").show();
  } else if (pageName == "main-sub-page") {
    $("#main-sub-page").show();
    $("#app-title").show();
    $("#header-box").hide();
  } else if (pageName == "loading") {
    $("#loading-page").show();
    $("#header-box").hide();
    $("#app-title").hide();
  }

  var bodyE = $("body").get(0);
  bodyE.setAttribute("page", pageName);
  bodyE.detailObj = obj;

  var ev = document.createEvent("CustomEvent");
  ev.initCustomEvent("page-change", false, false, obj);
  bodyE.dispatchEvent(ev);
}


/**
 * Above the page, show these objects as a list, for fast browsing.
 *
 * @param obj {Detail} object that caused this list to be displayed
 * @param list {Array of Detail}  objs to display in the header list
 * @param title {String}   Title to show to end user for header list
 */
function showHeaderList(obj, list, title) {
  var containerE = $("#headerList");
  containerE.empty();
  if ( !list || list.length < 2) {
    return;
  }
  assert(list.length >= 0, "list: need Array");
  assert(list[0] instanceof Detail, "list: need Detail");
  var boxE = $("<div class='border'/>").appendTo(containerE);

  function navFunc(obj, list, listTitle) {
    showDetail(obj, list, listTitle, { historyReplaceLast : true });
  }
  function close() {
    containerE.empty();
  }

  uiButton({
    $container : boxE,
    title : "x",
    classes : "closeButton",
    errorCallback : showError,
    clickCallback : close,
  });

  var contentE = $("<div class='content'/>").appendTo(boxE);
  /*var title = $("<div class='headerTitle'>").appendTo(boxE);
  title.text(headerListTitle);*/
  /*contentE.click(function() {
    showDetail(obj);
  });*/

  uiHeaderList({
    container : contentE,
    list : list,
    title : title,
    navFunc : navFunc,
  });
}


function getSettingsObject() {
  try {
    if ( !window.localStorage) {
      throw "use alternative";
    }
    window.localStorage.firstRun = false; // test whether it works
    return window.localStorage;
  } catch (e) {
    return {};
  }
}


/**
 * Info about current device
 * For responsive UI
 */
function Device() {
  this.browserVendor = navigator.userAgent.indexOf("Gecko/") != -1 ? "mozilla" : null;
  this.deviceType = navigator.userAgent.indexOf("Mobile") != -1 ||
      navigator.userAgent.indexOf("Android") != -1 ? "mobile" : "desktop"; // Add "tablet"
  this.width = document.documentElement.clientWidth;
  this.height = document.documentElement.clientHeight;

  // Browser detection to enable columns on Mozilla desktop only, via CSS in widgets.css
  var $body = $("body");
  $body.attr("browser-vendor", this.browserVendor);
  $body.attr("device-type", this.deviceType);
}
Device.prototype = {
}


/* Language handling */

var gCurrentLang = "en";
var _availableLangs = [ "en", "de", "fr" ];
var gStorageInLang = {}; // {Map lang -> Storage}

function loadMainDBInLang(lang, successCallback, errorCallback) {
  assert(arrayContains(_availableLangs, lang), "Language " + lang + " not supported");

  gCurrentLang = lang;
  tr._sb = null;
  BibleText.loadTranslation();

  if (gStorageInLang[lang]) {
    gStorage = gStorageInLang[lang];
    successCallback();
    return;
  }

  loadMainDB(function(storage) {
    gStorage = storage;
    gStorageInLang[lang] = storage;
    successCallback();
  }, errorCallback);
}

function detectLanguage() {
  assert(_availableLangs[0] && _availableLangs[0].length == 2);

  // Explicit URL parameter
  var query = parseURLQueryString(window.location.search);
  if (query.lang && query.lang.length == 2) {
    return query.lang;
  }

  // User setting in app
  var prefLang = gSettings.language;
  if (prefLang && arrayContains(_availableLangs, prefLang)) {
    return prefLang;
  }

  // Browser / OS setting
  var browserLang = window.navigator.language.substr(0, 2);
  if (arrayContains(_availableLangs, browserLang)) {
    return browserLang;
  }

  // default English
  if (arrayContains(_availableLangs, gCurrentLang)) {
    return gCurrentLang;
  }

  // build with single language
  return _availableLangs[0];
}

function showLangChangeUI() {
  _availableLangs.forEach(function(lang) {
    var buttonE = $("<span class='language'/>");
    $("#lang-box").append(buttonE);
    buttonE.text(lang.toUpperCase());
    buttonE.click(function() {
      try {
        console.log("Change language to " + lang);
        var obj = $("body").get(0).detailObj;
        changePage("loading", null);
        loadMainDBInLang(lang, function() {
          gSettings.language = lang;
          var $search = $("#search");
          $("#main-page").empty().append($search);
          changePage(obj ? "detail" : "main", obj);
          gBrowsingHistory.reload();
        }, showError);
      } catch (e) { showError(e); }
    });
  });
}
