import { Container, ContainerConfig } from '../Container';
import { VolumeSlider } from '../seekbar/VolumeSlider';
import { VolumeToggleButton } from './VolumeToggleButton';
import { UIInstanceManager } from '../../UIManager';
import { Timeout } from '../../utils/Timeout';
import { PlayerAPI } from 'bitmovin-player';

/**
 * Configuration interface for a {@link VolumeControlButton}.
 *
 * @category Configs
 */
export interface VolumeControlButtonConfig extends ContainerConfig {
  /**
   * The delay after which the volume slider will be hidden when there is no user interaction.
   * Care must be taken that the delay is long enough so users can reach the slider from the toggle button, e.g. by
   * mouse movement. If the delay is too short, the sliders disappears before the mouse pointer has reached it and
   * the user is not able to use it.
   * Default: 500ms
   */
  hideDelay?: number;
  /**
   * Specifies if the volume slider should be vertically or horizontally aligned.
   * Default: true
   */
  vertical?: boolean;
}

/**
 * A composite volume control that consists of and internally manages a volume control button that can be used
 * for muting, and a (depending on the CSS style, e.g. slide-out) volume control bar.
 *
 * @category Buttons
 */
export class VolumeControlButton extends Container<VolumeControlButtonConfig> {
  private volumeToggleButton: VolumeToggleButton;
  private volumeSlider: VolumeSlider;

  private volumeSliderHideTimeout: Timeout;

  constructor(config: VolumeControlButtonConfig = {}) {
    super(config);

    this.volumeToggleButton = new VolumeToggleButton();
    this.volumeSlider = new VolumeSlider({
      vertical: config.vertical != null ? config.vertical : true,
      hidden: true,
    });

    this.config = this.mergeConfig(
      config,
      {
        cssClass: 'ui-volumecontrolbutton',
        components: [this.volumeToggleButton, this.volumeSlider],
        hideDelay: 500,
      },
      <VolumeControlButtonConfig>this.config,
    );
  }

  configure(player: PlayerAPI, uimanager: UIInstanceManager): void {
    super.configure(player, uimanager);

    const volumeToggleButton = this.getVolumeToggleButton();
    const volumeSlider = this.getVolumeSlider();

    this.volumeSliderHideTimeout = new Timeout(this.getConfig().hideDelay, () => {
      volumeSlider.hide();
    });

    /*
     * Volume Slider visibility handling
     *
     * The volume slider shall be visible while the user hovers the mute toggle button, while the user hovers the
     * volume slider, and while the user slides the volume slider. If none of these situations are true, the slider
     * shall disappear.
     */
    let volumeSliderHovered = false;
    volumeToggleButton.getDomElement().on('mouseenter', () => {
      // Show volume slider when mouse enters the button area
      if (volumeSlider.isHidden()) {
        volumeSlider.show();
      }
      // Avoid hiding of the slider when button is hovered
      this.volumeSliderHideTimeout.clear();
    });
    volumeToggleButton.getDomElement().on('mouseleave', () => {
      // Hide slider delayed when button is left
      this.volumeSliderHideTimeout.reset();
    });
    volumeSlider.getDomElement().on('mouseenter', () => {
      // When the slider is entered, cancel the hide timeout activated by leaving the button
      this.volumeSliderHideTimeout.clear();
      volumeSliderHovered = true;
    });
    volumeSlider.getDomElement().on('mouseleave', () => {
      // When mouse leaves the slider, only hide it if there is no slide operation in progress
      if (volumeSlider.isSeeking()) {
        this.volumeSliderHideTimeout.clear();
      } else {
        this.volumeSliderHideTimeout.reset();
      }
      volumeSliderHovered = false;
    });
    volumeSlider.onSeeked.subscribe(() => {
      // When a slide operation is done and the slider not hovered (mouse outside slider), hide slider delayed
      if (!volumeSliderHovered) {
        this.volumeSliderHideTimeout.reset();
      }
    });
  }

  release(): void {
    super.release();
    this.volumeSliderHideTimeout.clear();
  }

  /**
   * Provides access to the internally managed volume toggle button.
   * @returns {VolumeToggleButton}
   */
  getVolumeToggleButton(): VolumeToggleButton {
    return this.volumeToggleButton;
  }

  /**
   * Provides access to the internally managed volume silder.
   * @returns {VolumeSlider}
   */
  getVolumeSlider(): VolumeSlider {
    return this.volumeSlider;
  }
}
