/**
 * An abstract pattern followed by the different tabs of the sidebar
 * @abstract
 * @interface
 */
class SidebarTab extends Application {
  constructor(...args) {
    super(...args);

    /**
     * A reference to the pop-out variant of this SidebarTab, if one exists
     * @type {SidebarTab}
     * @protected
     */
    this._popout = null;

    /**
     * Denote whether this is the original version of the sidebar tab, or a pop-out variant
     * @type {SidebarTab}
     */
    this._original = null;

    // Adjust options
    if ( this.options.popOut ) this.options.classes.push("sidebar-popout");
    this.options.classes.push(`${this.tabName}-sidebar`);

    // Register the tab as the sidebar singleton
    if ( !this.popOut && ui.sidebar ) ui.sidebar.tabs[this.tabName] = this;
  }

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

  /** @override */
  static get defaultOptions() {
    return foundry.utils.mergeObject(super.defaultOptions, {
      id: null,
      popOut: false,
      width: 300,
      height: "auto",
      classes: ["tab", "sidebar-tab"],
      baseApplication: "SidebarTab"
    });
  }

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

  /** @override */
  get id() {
    return `${this.options.id}${this._original ? "-popout" : ""}`;
  }

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

  /**
   * The base name of this sidebar tab
   * @type {string}
   */
  get tabName() {
    return this.constructor.defaultOptions.id ?? this.id;
  }

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

  /** @override */
  async getData(options={}) {
    return {
      cssId: this.id,
      cssClass: this.options.classes.join(" "),
      tabName: this.tabName,
      user: game.user
    };
  }

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

  /** @override */
  async _render(force=false, options={}) {
    await super._render(force, options);
    if ( this._popout ) await this._popout._render(force, options);
  }

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

  /** @override */
  async _renderInner(data) {
    let html = await super._renderInner(data);
    if ( ui.sidebar?.activeTab === this.id ) html.addClass("active");
    if ( this.popOut ) html.removeClass("tab");
    return html;
  }

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

  /**
   * Activate this SidebarTab, switching focus to it
   */
  activate() {
    ui.sidebar.activateTab(this.tabName);
  }

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

  /** @override */
  async close(options) {
    if ( this.popOut ) {
      const base = this._original;
      if ( base ) base._popout = null;
      return super.close(options);
    }
  }

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

  /**
   * Create a second instance of this SidebarTab class which represents a singleton popped-out container
   * @returns {SidebarTab}   The popped out sidebar tab instance
   */
  createPopout() {
    if ( this._popout ) return this._popout;

    // Retain options from the main tab
    const options = {...this.options, popOut: true};
    delete options.id;
    delete options.classes;

    // Create a popout application
    const pop = new this.constructor(options);
    this._popout = pop;
    pop._original = this;
    return pop;
  }

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

  /**
   * Render the SidebarTab as a pop-out container
   */
  renderPopout() {
    const pop = this.createPopout();
    pop.render(true);
  }

  /* -------------------------------------------- */
  /*  Event Handlers                              */
  /* -------------------------------------------- */

  /**
   * Handle lazy loading for sidebar images to only load them once they become observed
   * @param {HTMLElement[]} entries               The entries which are now observed
   * @param {IntersectionObserver} observer       The intersection observer instance
   */
  _onLazyLoadImage(entries, observer) {
    for ( let e of entries ) {
      if ( !e.isIntersecting ) continue;
      const li = e.target;

      // Background Image
      if ( li.dataset.backgroundImage ) {
        li.style["background-image"] = `url("${li.dataset.backgroundImage}")`;
        delete li.dataset.backgroundImage;
      }

      // Avatar image
      const img = li.querySelector("img");
      if ( img && img.dataset.src ) {
        img.src = img.dataset.src;
        delete img.dataset.src;
      }

      // No longer observe the target
      observer.unobserve(e.target);
    }
  }
}
