/**
 * Render the Sidebar container, and after rendering insert Sidebar tabs.
 */
class Sidebar extends Application {

  /**
   * Singleton application instances for each sidebar tab
   * @type {Record<string, SidebarTab>}
   */
  tabs = {};

  /**
   * Track whether the sidebar container is currently collapsed
   * @type {boolean}
   */
  _collapsed = false;

  /* -------------------------------------------- */

  /** @inheritdoc */
  static get defaultOptions() {
    return foundry.utils.mergeObject(super.defaultOptions, {
      id: "sidebar",
      template: "templates/sidebar/sidebar.html",
      popOut: false,
      width: 300,
      tabs: [{navSelector: ".tabs", contentSelector: "#sidebar", initial: "chat"}]
    });
  }

  /* -------------------------------------------- */

  /**
   * Return the name of the active Sidebar tab
   * @type {string}
   */
  get activeTab() {
    return this._tabs[0].active;
  }


  /* -------------------------------------------- */

  /**
   * Singleton application instances for each popout tab
   * @type {Record<string, SidebarTab>}
   */
  get popouts() {
    const popouts = {};
    for ( let [name, app] of Object.entries(this.tabs) ) {
      if ( app._popout ) popouts[name] = app._popout;
    }
    return popouts;
  }

  /* -------------------------------------------- */
  /*  Rendering
  /* -------------------------------------------- */

  /** @override */
  getData(options={}) {
    const isGM = game.user.isGM;

    // Configure tabs
    const tabs = {
      chat: {
        tooltip: ChatMessage.metadata.labelPlural,
        icon: CONFIG.ChatMessage.sidebarIcon,
        notification: "<i id=\"chat-notification\" class=\"notification-pip fas fa-exclamation-circle\"></i>"
      },
      combat: {
        tooltip: Combat.metadata.labelPlural,
        icon: CONFIG.Combat.sidebarIcon
      },
      scenes: {
        tooltip: Scene.metadata.labelPlural,
        icon: CONFIG.Scene.sidebarIcon
      },
      actors: {
        tooltip: Actor.metadata.labelPlural,
        icon: CONFIG.Actor.sidebarIcon
      },
      items: {
        tooltip: Item.metadata.labelPlural,
        icon: CONFIG.Item.sidebarIcon
      },
      journal: {
        tooltip: "SIDEBAR.TabJournal",
        icon: CONFIG.JournalEntry.sidebarIcon
      },
      tables: {
        tooltip: RollTable.metadata.labelPlural,
        icon: CONFIG.RollTable.sidebarIcon
      },
      cards: {
        tooltip: Cards.metadata.labelPlural,
        icon: CONFIG.Cards.sidebarIcon
      },
      playlists: {
        tooltip: Playlist.metadata.labelPlural,
        icon: CONFIG.Playlist.sidebarIcon
      },
      compendium: {
        tooltip: "SIDEBAR.TabCompendium",
        icon: "fas fa-atlas"
      },
      settings: {
        tooltip: "SIDEBAR.TabSettings",
        icon: "fas fa-cogs"
      }
    };
    if ( !isGM ) delete tabs.scenes;

    // Display core or system update notification?
    if ( isGM && (game.data.coreUpdate.hasUpdate || game.data.systemUpdate.hasUpdate) ) {
      tabs.settings.notification = `<i class="notification-pip fas fa-exclamation-circle"></i>`;
    }
    return {tabs};
  }

  /* -------------------------------------------- */

  /** @inheritdoc */
  async _render(force, options) {

    // Render the Sidebar container only once
    if ( !this.rendered ) await super._render(force, options);

    // Render sidebar Applications
    const renders = [];
    for ( let [name, app] of Object.entries(this.tabs) ) {
      renders.push(app._render(true).catch(err => {
        Hooks.onError("Sidebar#_render", err, {
          msg: `Failed to render Sidebar tab ${name}`,
          log: "error",
          name
        });
      }));
    }

    Promise.all(renders).then(() => this.activateTab(this.activeTab));
  }

  /* -------------------------------------------- */
  /*  Methods
  /* -------------------------------------------- */

  /**
   * Expand the Sidebar container from a collapsed state.
   * Take no action if the sidebar is already expanded.
   */
  expand() {
    if ( !this._collapsed ) return;
    const sidebar = this.element;
    const tab = sidebar.find(".sidebar-tab.active");
    const tabs = sidebar.find("#sidebar-tabs");
    const icon = tabs.find("a.collapse i");

    // Animate the sidebar expansion
    tab.hide();
    sidebar.animate({width: this.options.width, height: this.position.height}, 150, () => {
      sidebar.css({width: "", height: ""}); // Revert to default styling
      sidebar.removeClass("collapsed");
      tabs[0].dataset.tooltipDirection = TooltipManager.TOOLTIP_DIRECTIONS.DOWN;
      tab.fadeIn(250, () => {
        tab.css({
          display: "",
          height: ""
        });
      });
      icon.removeClass("fa-caret-left").addClass("fa-caret-right");
      this._collapsed = false;
      Hooks.callAll("collapseSidebar", this, this._collapsed);
    });
  }

  /* -------------------------------------------- */

  /**
   * Collapse the sidebar to a minimized state.
   * Take no action if the sidebar is already collapsed.
   */
  collapse() {
    if ( this._collapsed ) return;
    const sidebar = this.element;
    const tab = sidebar.find(".sidebar-tab.active");
    const tabs = sidebar.find("#sidebar-tabs");
    const icon = tabs.find("a.collapse i");

    // Animate the sidebar collapse
    tab.fadeOut(250, () => {
      sidebar.animate({width: 32, height: (32 + 4) * (Object.values(this.tabs).length + 1)}, 150, () => {
        sidebar.css("height", ""); // Revert to default styling
        sidebar.addClass("collapsed");
        tabs[0].dataset.tooltipDirection = TooltipManager.TOOLTIP_DIRECTIONS.LEFT;
        tab.css("display", "");
        icon.removeClass("fa-caret-right").addClass("fa-caret-left");
        this._collapsed = true;
        Hooks.callAll("collapseSidebar", this, this._collapsed);
      });
    });
  }

  /* -------------------------------------------- */
  /*  Event Listeners and Handlers
  /* -------------------------------------------- */

  /** @inheritdoc */
  activateListeners(html) {
    super.activateListeners(html);

    // Right click pop-out
    const nav = this._tabs[0]._nav;
    nav.addEventListener("contextmenu", this._onRightClickTab.bind(this));

    // Toggle Collapse
    const collapse = nav.querySelector(".collapse");
    collapse.addEventListener("click", this._onToggleCollapse.bind(this));

    // Left click a tab
    const tabs = nav.querySelectorAll(".item");
    tabs.forEach(tab => tab.addEventListener("click", this._onLeftClickTab.bind(this)));
  }

  /* -------------------------------------------- */

  /** @override */
  _onChangeTab(event, tabs, active) {
    const app = ui[active];
    Hooks.callAll("changeSidebarTab", app);
  }

  /* -------------------------------------------- */

  /**
   * Handle the special case of left-clicking a tab when the sidebar is collapsed.
   * @param {MouseEvent} event  The originating click event
   * @private
   */
  _onLeftClickTab(event) {
    const app = ui[event.currentTarget.dataset.tab];
    if ( app && this._collapsed ) app.renderPopout(app);
  }

  /* -------------------------------------------- */

  /**
   * Handle right-click events on tab controls to trigger pop-out containers for each tab
   * @param {Event} event     The originating contextmenu event
   * @private
   */
  _onRightClickTab(event) {
    const li = event.target.closest(".item");
    if ( !li ) return;
    event.preventDefault();
    const tabApp = ui[li.dataset.tab];
    tabApp.renderPopout(tabApp);
  }

  /* -------------------------------------------- */

  /**
   * Handle toggling of the Sidebar container's collapsed or expanded state
   * @param {Event} event
   * @private
   */
  _onToggleCollapse(event) {
    event.preventDefault();
    if ( this._collapsed ) this.expand();
    else this.collapse();
  }
}
