/*
 * Copyright (c) 2010, 2025 BSI Business Systems Integration AG
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 */
import {
  Action, arrays, Desktop, EventHandler, HtmlComponent, icons, InitModelOf, KeyStrokeContext, OutlineViewButton, PropertyChangeEvent, scout, ViewButton, ViewMenuOpenKeyStroke, ViewMenuPopup, ViewMenuTabEventMap, ViewMenuTabModel, Widget
} from '../../index';

/**
 * Shows a list of view buttons with displayStyle=MENU
 * and shows the title of the active view button, if the view button is one
 * of the view buttons contained in the menu.
 */
export class ViewMenuTab extends Widget implements ViewMenuTabModel {
  declare model: ViewMenuTabModel;
  declare eventMap: ViewMenuTabEventMap;
  declare self: ViewMenuTab;

  selected: boolean;
  selectedButtonVisible: boolean;
  defaultIconId: string;
  viewButtons: ViewButton[];
  selectedButton: ViewButton;
  dropdown: Action;
  popup: ViewMenuPopup;
  desktopKeyStrokeContext: KeyStrokeContext;
  protected _desktopInBackgroundHandler: EventHandler<PropertyChangeEvent<boolean, Desktop>>;

  constructor() {
    super();

    this.viewButtons = [];
    this.selected = false;
    this.selectedButton = null;
    this.selectedButtonVisible = true;
    this.defaultIconId = icons.FOLDER;
    this._desktopInBackgroundHandler = this._onDesktopInBackgroundChange.bind(this);
    this._addWidgetProperties(['selectedButton']);
    this._addPreserveOnPropertyChangeProperties(['selectedButton']);
  }

  protected override _init(model: InitModelOf<this>) {
    super._init(model);
    this.dropdown = scout.create(Action, {
      parent: this,
      iconId: icons.ANGLE_DOWN,
      text: this.session.text('ui.Views'),
      textVisible: false,
      tabbable: false,
      cssClass: 'view-menu',
      toggleAction: true
    });
    this.dropdown.on('action', this.togglePopup.bind(this));
    this._setViewButtons(this.viewButtons);
    this.session.desktop.on('propertyChange:inBackground', this._desktopInBackgroundHandler);
  }

  protected override _destroy() {
    this.session.desktop.off('propertyChange:inBackground', this._desktopInBackgroundHandler);
    super._destroy();
  }

  protected override _initKeyStrokeContext() {
    super._initKeyStrokeContext();

    // Bound to desktop
    this.desktopKeyStrokeContext = new KeyStrokeContext();
    this.desktopKeyStrokeContext.invokeAcceptInputOnActiveValueField = true;
    this.desktopKeyStrokeContext.$bindTarget = this.session.desktop.$container;
    this.desktopKeyStrokeContext.$scopeTarget = this.session.desktop.$container;
    this.desktopKeyStrokeContext.registerKeyStroke(new ViewMenuOpenKeyStroke(this));
  }

  protected override _render() {
    this.$container = this.$parent.appendDiv('view-tab view-menu-tab');
    this.htmlComp = HtmlComponent.install(this.$container, this.session);
    this.dropdown.render(this.$container);
    this.session.keyStrokeManager.installKeyStrokeContext(this.desktopKeyStrokeContext);
    this.$container.appendDiv('edge right');
  }

  protected override _remove() {
    this.session.keyStrokeManager.uninstallKeyStrokeContext(this.desktopKeyStrokeContext);
    super._remove();
    if (this.selectedButton) {
      this.selectedButton.remove();
    }
  }

  protected override _renderProperties() {
    super._renderProperties();
    this._renderSelectedButtonVisible();
    this._renderSelected();
    this._renderInBackground();
  }

  setViewButtons(viewButtons: ViewButton[]) {
    this.setProperty('viewButtons', viewButtons);
  }

  protected _setViewButtons(viewButtons: ViewButton[]) {
    this._setProperty('viewButtons', viewButtons);
    this.setVisible(this.viewButtons.length > 0);
    let selectedButton = this._findSelectedViewButton();
    if (selectedButton) {
      this.setSelectedButton(selectedButton);
    } else {
      this.setSelectedButton(arrays.find(this.viewButtons, v => v.selectedAsMenu) || this.viewButtons[0]);
    }
    this.setSelected(!!selectedButton);
  }

  setSelectedButton(viewButton: ViewButton) {
    if (this.selectedButton && this.selectedButton.cloneOf === viewButton) {
      return;
    }
    if (viewButton) {
      this.setProperty('selectedButton', viewButton);
    }
  }

  protected _setSelectedButton(viewButton: ViewButton) {
    this.viewButtons.forEach(vb => vb.setSelectedAsMenu(vb === viewButton));

    // The selectedViewButton is a fake ViewButton but reflects the state of the actually selected one.
    // The fake button is created only once and must not be destroyed when the selected view button changes.
    // This is important to not break the CSS transition (e.g. when desktop is in background and another view selected using ViewMenuPopup).
    let clone = this.selectedButton;
    if (!clone) {
      clone = scout.create(OutlineViewButton, {
        parent: this,
        displayStyle: 'TAB',
        tabbable: false
      });
    }
    if (clone.cloneOf) {
      clone.cloneOf.unmirror(clone);
    }

    // Link our fake button with the original and apply all the relevant properties (which are stored in cloneProperties, e.g. outline, cssClass, enabled, etc.)
    clone.cloneOf = viewButton;
    Array.from(viewButton.cloneProperties)
      .filter(property => property !== 'tabbable')
      .forEach(property => clone.callSetter(property, viewButton[property]));

    // Use default icon if outline does not define one.
    clone.setIconId(viewButton.iconId || this.defaultIconId);

    // Mirror the events and property changes
    viewButton.mirror({
      delegateEventsToOriginal: ['acceptInput', 'action'],
      delegateAllPropertiesToClone: true,
      delegateAllPropertiesToOriginal: true,
      excludePropertiesToOriginal: ['selected', 'tabbable']
    }, clone);

    this._setProperty('selectedButton', clone);
  }

  protected _renderSelectedButton() {
    this._renderSelectedButtonVisible();
  }

  setSelectedButtonVisible(selectedButtonVisible: boolean) {
    this.setProperty('selectedButtonVisible', selectedButtonVisible);
  }

  protected _renderSelectedButtonVisible() {
    this.$container.toggleClass('selected-button-invisible', !this.selectedButtonVisible);
    if (!this.selectedButton) {
      return;
    }
    if (this.selectedButtonVisible) {
      if (!this.selectedButton.rendered) {
        this.selectedButton.render();
        this.selectedButton.$container.prependTo(this.$container);
        this.invalidateLayoutTree();
      }
    } else {
      if (this.selectedButton.rendered) {
        this.selectedButton.remove();
        this.invalidateLayoutTree();
      }
    }
  }

  setSelected(selected: boolean) {
    this.setProperty('selected', selected);
  }

  protected _renderSelected() {
    this.$container.setSelected(this.selected);
  }

  protected _findSelectedViewButton(): ViewButton {
    return arrays.find(this.viewButtons, v => v.selected);
  }

  /**
   * Toggles the 'view menu popup', or brings the outline content to the front if in background.
   */
  togglePopup() {
    if (this.popup) {
      this._closePopup();
    } else {
      this._openPopup();
    }
  }

  protected _openPopup() {
    if (this.popup) {
      // already open
      return;
    }
    this.popup = scout.create(ViewMenuPopup, {
      parent: this,
      viewMenus: this.viewButtons,
      defaultIconId: this.defaultIconId,
      $anchor: this.$parent // use view button box as parent for better alignment
    });
    this.popup.open();
    this.popup.one('destroy', event => {
      this.dropdown.setSelected(false);
      this.popup = null;
    });
  }

  protected _closePopup() {
    if (this.popup) {
      this.popup.close();
    }
  }

  protected _renderInBackground() {
    if (this.session.desktop.displayStyle === Desktop.DisplayStyle.COMPACT) {
      return;
    }
    if (!this.rendering) {
      if (this.session.desktop.inBackground) {
        this.$container.addClassForAnimation('animate-bring-to-back');
      } else {
        this.$container.addClassForAnimation('animate-bring-to-front');
      }
    }
    this.$container.toggleClass('in-background', this.session.desktop.inBackground);
  }

  onViewButtonSelected() {
    let viewButton = this._findSelectedViewButton();
    if (viewButton) {
      this.setSelectedButton(viewButton);
    }
    this.setSelected(!!viewButton);
    this._closePopup();
  }

  protected _onDesktopInBackgroundChange(event: PropertyChangeEvent<boolean, Desktop>) {
    if (this.rendered) {
      this._renderInBackground();
    }
  }
}
