app.views.Content = class Content extends app.View {
  static el = "._content";
  static loadingClass = "_content-loading";

  static events = { click: "onClick" };

  static shortcuts = {
    altUp: "scrollStepUp",
    altDown: "scrollStepDown",
    pageUp: "scrollPageUp",
    pageDown: "scrollPageDown",
    pageTop: "scrollToTop",
    pageBottom: "scrollToBottom",
    altF: "onAltF",
  };

  static routes = {
    before: "beforeRoute",
    after: "afterRoute",
  };

  init() {
    this.scrollEl = app.isMobile()
      ? document.scrollingElement || document.body
      : this.el;
    this.scrollMap = {};
    this.scrollStack = [];

    this.rootPage = new app.views.RootPage();
    this.staticPage = new app.views.StaticPage();
    this.settingsPage = new app.views.SettingsPage();
    this.offlinePage = new app.views.OfflinePage();
    this.typePage = new app.views.TypePage();
    this.entryPage = new app.views.EntryPage();

    this.entryPage
      .on("loading", () => this.onEntryLoading())
      .on("loaded", () => this.onEntryLoaded());

    app
      .on("ready", () => this.onReady())
      .on("bootError", () => this.onBootError());
  }

  show(view) {
    this.hideLoading();
    if (view !== this.view) {
      if (this.view != null) {
        this.view.deactivate();
      }
      this.html((this.view = view));
      this.view.activate();
    }
  }

  showLoading() {
    this.addClass(this.constructor.loadingClass);
  }

  isLoading() {
    return this.el.classList.contains(this.constructor.loadingClass);
  }

  hideLoading() {
    this.removeClass(this.constructor.loadingClass);
  }

  scrollTo(value) {
    this.scrollEl.scrollTop = value || 0;
  }

  smoothScrollTo(value) {
    if (app.settings.get("fastScroll")) {
      this.scrollTo(value);
    } else {
      $.smoothScroll(this.scrollEl, value || 0);
    }
  }

  scrollBy(n) {
    this.smoothScrollTo(this.scrollEl.scrollTop + n);
  }

  scrollToTop() {
    this.smoothScrollTo(0);
  }

  scrollToBottom() {
    this.smoothScrollTo(this.scrollEl.scrollHeight);
  }

  scrollStepUp() {
    this.scrollBy(-80);
  }

  scrollStepDown() {
    this.scrollBy(80);
  }

  scrollPageUp() {
    this.scrollBy(40 - this.scrollEl.clientHeight);
  }

  scrollPageDown() {
    this.scrollBy(this.scrollEl.clientHeight - 40);
  }

  scrollToTarget() {
    let el;
    if (
      this.routeCtx.hash &&
      (el = this.findTargetByHash(this.routeCtx.hash))
    ) {
      $.scrollToWithImageLock(el, this.scrollEl, "top", {
        margin: this.scrollEl === this.el ? 0 : $.offset(this.el).top,
      });
      $.openDetailsAncestors(el);
      $.highlight(el, { className: "_highlight" });
    } else {
      this.scrollTo(this.scrollMap[this.routeCtx.state.id]);
    }
  }

  onReady() {
    this.hideLoading();
  }

  onBootError() {
    this.hideLoading();
    this.html(this.tmpl("bootError"));
  }

  onEntryLoading() {
    this.showLoading();
    if (this.scrollToTargetTimeout) {
      clearTimeout(this.scrollToTargetTimeout);
      this.scrollToTargetTimeout = null;
    }
  }

  onEntryLoaded() {
    this.hideLoading();
    if (this.scrollToTargetTimeout) {
      clearTimeout(this.scrollToTargetTimeout);
      this.scrollToTargetTimeout = null;
    }
    this.scrollToTarget();
  }

  beforeRoute(context) {
    this.cacheScrollPosition();
    this.routeCtx = context;
    this.scrollToTargetTimeout = this.delay(this.scrollToTarget);
  }

  cacheScrollPosition() {
    if (!this.routeCtx || this.routeCtx.hash) {
      return;
    }
    if (this.routeCtx.path === "/") {
      return;
    }

    if (this.scrollMap[this.routeCtx.state.id] == null) {
      this.scrollStack.push(this.routeCtx.state.id);
      while (this.scrollStack.length > app.config.history_cache_size) {
        delete this.scrollMap[this.scrollStack.shift()];
      }
    }

    this.scrollMap[this.routeCtx.state.id] = this.scrollEl.scrollTop;
  }

  afterRoute(route, context) {
    if (route !== "entry" && route !== "type") {
      resetFavicon();
    }

    switch (route) {
      case "root":
        this.show(this.rootPage);
        break;
      case "entry":
        this.show(this.entryPage);
        break;
      case "type":
        this.show(this.typePage);
        break;
      case "settings":
        this.show(this.settingsPage);
        break;
      case "offline":
        this.show(this.offlinePage);
        break;
      default:
        this.show(this.staticPage);
    }

    this.view.onRoute(context);
    app.document.setTitle(
      typeof this.view.getTitle === "function"
        ? this.view.getTitle()
        : undefined,
    );
  }

  onClick(event) {
    const link = $.closestLink($.eventTarget(event), this.el);
    if (link && this.isExternalUrl(link.getAttribute("href"))) {
      $.stopEvent(event);
      $.popup(link);
    }
  }

  onAltF(event) {
    if (
      !document.activeElement ||
      !$.hasChild(this.el, document.activeElement)
    ) {
      this.find("a:not(:empty)")?.focus();
      return $.stopEvent(event);
    }
  }

  findTargetByHash(hash) {
    let el = (() => {
      try {
        return $.id(decodeURIComponent(hash));
      } catch (error) {}
    })();
    if (!el) {
      el = (() => {
        try {
          return $.id(hash);
        } catch (error1) {}
      })();
    }
    return el;
  }

  isExternalUrl(url) {
    return url?.startsWith("http:") || url?.startsWith("https:");
  }
};
