// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/* eslint-disable rulesdir/no_underscored_properties */

import * as i18n from '../i18n/i18n.js';
import * as UI from '../ui/ui.js';

import {MainView, TriggerDispatcher} from './MainView.js';  // eslint-disable-line no-unused-vars
import {PlayerEvent} from './MediaModel.js';                // eslint-disable-line no-unused-vars
import {PlayerPropertyKeys} from './PlayerPropertiesView.js';

export const UIStrings = {
  /**
  *@description A right-click context menu entry which when clicked causes the menu entry for that player to be removed.
  */
  hidePlayer: 'Hide player',
  /**
  *@description A right-click context menu entry which should keep the element selected, while hiding all other entries.
  */
  hideAllOthers: 'Hide all others',
  /**
  *@description Context menu entry which downloads the json dump when clicked
  */
  savePlayerInfo: 'Save player info',
  /**
  *@description Side-panel entry title text for the players section.
  */
  players: 'Players',
};
const str_ = i18n.i18n.registerUIStrings('media/PlayerListView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export interface PlayerStatus {
  playerTitle: string;
  playerID: string;
  exists: boolean;
  playing: boolean;
  titleEdited: boolean;
}
export interface PlayerStatusMapElement {
  playerStatus: PlayerStatus;
  playerTitleElement: HTMLElement|null;
}

export class PlayerEntryTreeElement extends UI.TreeOutline.TreeElement {
  titleFromUrl: boolean;
  _playerStatus: PlayerStatus;
  _displayContainer: MainView;

  constructor(playerStatus: PlayerStatus, displayContainer: MainView, playerID: string) {
    super(playerStatus.playerTitle, false);
    this.titleFromUrl = true;
    this._playerStatus = playerStatus;
    this._displayContainer = displayContainer;
    this.setLeadingIcons([UI.Icon.Icon.create('smallicon-videoplayer-playing', 'media-player')]);
    this.listItemElement.classList.add('player-entry-tree-element');
    this.listItemElement.addEventListener('contextmenu', this._rightClickContextMenu.bind(this, playerID), false);
  }

  onselect(_selectedByUser?: boolean): boolean {
    this._displayContainer.renderMainPanel(this._playerStatus.playerID);
    return true;
  }

  _rightClickContextMenu(playerID: string, event: Event): boolean {
    const contextMenu = new UI.ContextMenu.ContextMenu(event);
    contextMenu.headerSection().appendItem(i18nString(UIStrings.hidePlayer), this._hidePlayer.bind(this, playerID));
    contextMenu.headerSection().appendItem(i18nString(UIStrings.hideAllOthers), this._hideOthers.bind(this, playerID));
    contextMenu.headerSection().appendItem(i18nString(UIStrings.savePlayerInfo), this._savePlayer.bind(this, playerID));
    contextMenu.show();
    return true;
  }

  _hidePlayer(playerID: string): void {
    this._displayContainer.markPlayerForDeletion(playerID);
  }

  _savePlayer(playerID: string): void {
    this._displayContainer.exportPlayerData(playerID);
  }

  _hideOthers(playerID: string): void {
    this._displayContainer.markOtherPlayersForDeletion(playerID);
  }
}

export class PlayerListView extends UI.Widget.VBox implements TriggerDispatcher {
  _playerStatuses: Map<string, PlayerEntryTreeElement>;
  _mainContainer: MainView;
  _sidebarTree: UI.TreeOutline.TreeOutlineInShadow;
  _playerList: UI.TreeOutline.TreeElement;

  constructor(mainContainer: MainView) {
    super(true);

    this._playerStatuses = new Map();

    // Container where new panels can be added based on clicks.
    this._mainContainer = mainContainer;

    // The parent tree for storing sections
    this._sidebarTree = new UI.TreeOutline.TreeOutlineInShadow();
    this.contentElement.appendChild(this._sidebarTree.element);
    this._sidebarTree.registerRequiredCSS('media/playerListView.css', {enableLegacyPatching: true});

    // Players active in this tab.
    this._playerList = this._addListSection(i18nString(UIStrings.players));
    this._playerList.listItemElement.classList.add('player-entry-header');
  }

  deletePlayer(playerID: string): void {
    this._playerList.removeChild(this._playerStatuses.get(playerID) as UI.TreeOutline.TreeElement);
    this._playerStatuses.delete(playerID);
  }

  _addListSection(title: string): UI.TreeOutline.TreeElement {
    const treeElement = new UI.TreeOutline.TreeElement(title, true);
    treeElement.listItemElement.classList.add('storage-group-list-item');
    treeElement.setCollapsible(false);
    treeElement.selectable = false;
    this._sidebarTree.appendChild(treeElement);
    return treeElement;
  }

  addMediaElementItem(playerID: string): void {
    const playerStatus = {playerTitle: playerID, playerID: playerID, exists: true, playing: false, titleEdited: false};
    const playerElement = new PlayerEntryTreeElement(playerStatus, this._mainContainer, playerID);
    this._playerStatuses.set(playerID, playerElement);
    this._playerList.appendChild(playerElement);
  }

  setMediaElementPlayerTitle(playerID: string, newTitle: string, isTitleExtractedFromUrl: boolean): void {
    if (this._playerStatuses.has(playerID)) {
      const sidebarEntry = this._playerStatuses.get(playerID);
      if (sidebarEntry && (!isTitleExtractedFromUrl || sidebarEntry.titleFromUrl)) {
        sidebarEntry.title = newTitle;
        sidebarEntry.titleFromUrl = isTitleExtractedFromUrl;
      }
    }
  }

  setMediaElementPlayerIcon(playerID: string, iconName: string): void {
    if (this._playerStatuses.has(playerID)) {
      const sidebarEntry = this._playerStatuses.get(playerID);
      if (!sidebarEntry) {
        throw new Error('sidebarEntry is expected to not be null');
      }
      sidebarEntry.setLeadingIcons([UI.Icon.Icon.create('smallicon-videoplayer-' + iconName, 'media-player')]);
    }
  }

  onProperty(playerID: string, property: Protocol.Media.PlayerProperty): void {
    // Sometimes the title will be an empty string, since this is provided
    // by the website. We don't want to swap title to an empty string.
    if (property.name === PlayerPropertyKeys.FrameTitle && property.value) {
      this.setMediaElementPlayerTitle(playerID, property.value as string, false);
    }

    // Url always has a value.
    if (property.name === PlayerPropertyKeys.FrameUrl) {
      const urlPathComponent = property.value.substring(property.value.lastIndexOf('/') + 1);
      this.setMediaElementPlayerTitle(playerID, urlPathComponent, true);
    }
  }

  onError(_playerID: string, _error: Protocol.Media.PlayerError): void {
    // TODO(tmathmeyer) show an error icon next to the player name
  }

  onMessage(_playerID: string, _message: Protocol.Media.PlayerMessage): void {
    // TODO(tmathmeyer) show a message count number next to the player name.
  }

  onEvent(playerID: string, event: PlayerEvent): void {
    if (event.value === 'PLAY') {
      this.setMediaElementPlayerIcon(playerID, 'playing');
    } else if (event.value === 'PAUSE') {
      this.setMediaElementPlayerIcon(playerID, 'paused');
    } else if (event.value === 'WEBMEDIAPLAYER_DESTROYED') {
      this.setMediaElementPlayerIcon(playerID, 'destroyed');
    }
  }
}
