// 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.

import * as Common from '../../core/common/common.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Platform from '../../core/platform/platform.js';
import type * as Protocol from '../../generated/protocol.js';
import * as UI from '../../ui/legacy/legacy.js';

import audioContextSelectorStyles from './audioContextSelector.css.js';

const UIStrings = {
  /**
   *@description Text that shows there is no recording
   */
  noRecordings: '(no recordings)',
  /**
   *@description Label prefix for an audio context selection
   *@example {realtime (1e03ec)} PH1
   */
  audioContextS: 'Audio context: {PH1}',
};
const str_ = i18n.i18n.registerUIStrings('panels/web_audio/AudioContextSelector.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);

export class AudioContextSelector extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
    UI.SoftDropDown.Delegate<Protocol.WebAudio.BaseAudioContext> {
  private readonly placeholderText: Platform.UIString.LocalizedString;
  private readonly items: UI.ListModel.ListModel<Protocol.WebAudio.BaseAudioContext>;
  private readonly dropDown: UI.SoftDropDown.SoftDropDown<Protocol.WebAudio.BaseAudioContext>;
  private readonly toolbarItemInternal: UI.Toolbar.ToolbarItem;
  private selectedContextInternal: Protocol.WebAudio.BaseAudioContext|null;
  constructor() {
    super();

    this.placeholderText = i18nString(UIStrings.noRecordings);

    this.items = new UI.ListModel.ListModel();

    this.dropDown = new UI.SoftDropDown.SoftDropDown(this.items, this, 'audio-context');
    this.dropDown.setPlaceholderText(this.placeholderText);

    this.toolbarItemInternal = new UI.Toolbar.ToolbarItem(this.dropDown.element);
    this.toolbarItemInternal.setEnabled(false);
    this.toolbarItemInternal.setTitle(i18nString(UIStrings.audioContextS, {PH1: this.placeholderText}));
    this.items.addEventListener(UI.ListModel.Events.ITEMS_REPLACED, this.onListItemReplaced, this);
    this.toolbarItemInternal.element.classList.add('toolbar-has-dropdown');

    this.selectedContextInternal = null;
  }

  private onListItemReplaced(): void {
    const hasItems = Boolean(this.items.length);
    this.toolbarItemInternal.setEnabled(hasItems);
    if (!hasItems) {
      this.toolbarItemInternal.setTitle(i18nString(UIStrings.audioContextS, {PH1: this.placeholderText}));
    }
  }

  contextCreated({data: context}: Common.EventTarget.EventTargetEvent<Protocol.WebAudio.BaseAudioContext>): void {
    this.items.insert(this.items.length, context);

    // Select if this is the first item.
    if (this.items.length === 1) {
      this.dropDown.selectItem(context);
    }
  }

  contextDestroyed({data: contextId}: Common.EventTarget.EventTargetEvent<string>): void {
    const contextIndex =
        this.items.findIndex((context: Protocol.WebAudio.BaseAudioContext) => context.contextId === contextId);
    if (contextIndex > -1) {
      this.items.remove(contextIndex);
    }
  }

  contextChanged({data: changedContext}: Common.EventTarget.EventTargetEvent<Protocol.WebAudio.BaseAudioContext>):
      void {
    const contextIndex = this.items.findIndex(
        (context: Protocol.WebAudio.BaseAudioContext) => context.contextId === changedContext.contextId);
    if (contextIndex > -1) {
      this.items.replace(contextIndex, changedContext);

      // If the changed context is currently selected by user. Re-select it
      // because the actual element is replaced with a new one.
      if (this.selectedContextInternal && this.selectedContextInternal.contextId === changedContext.contextId) {
        this.dropDown.selectItem(changedContext);
      }
    }
  }

  createElementForItem(item: Protocol.WebAudio.BaseAudioContext): Element {
    const element = document.createElement('div');
    const shadowRoot = UI.UIUtils.createShadowRootWithCoreStyles(element, {cssFile: audioContextSelectorStyles});
    const title = shadowRoot.createChild('div', 'title');
    UI.UIUtils.createTextChild(title, Platform.StringUtilities.trimEndWithMaxLength(this.titleFor(item), 100));
    return element;
  }

  selectedContext(): Protocol.WebAudio.BaseAudioContext|null {
    if (!this.selectedContextInternal) {
      return null;
    }

    return this.selectedContextInternal;
  }

  highlightedItemChanged(
      from: Protocol.WebAudio.BaseAudioContext|null, to: Protocol.WebAudio.BaseAudioContext|null,
      fromElement: Element|null, toElement: Element|null): void {
    if (fromElement) {
      fromElement.classList.remove('highlighted');
    }
    if (toElement) {
      toElement.classList.add('highlighted');
    }
  }

  isItemSelectable(_item: Protocol.WebAudio.BaseAudioContext): boolean {
    return true;
  }

  itemSelected(item: Protocol.WebAudio.BaseAudioContext|null): void {
    if (!item) {
      return;
    }

    // It's possible that no context is selected yet.
    if (!this.selectedContextInternal || this.selectedContextInternal.contextId !== item.contextId) {
      this.selectedContextInternal = item;
      this.toolbarItemInternal.setTitle(i18nString(UIStrings.audioContextS, {PH1: this.titleFor(item)}));
    }

    this.dispatchEventToListeners(Events.CONTEXT_SELECTED, item);
  }

  reset(): void {
    this.items.replaceAll([]);
  }

  titleFor(context: Protocol.WebAudio.BaseAudioContext): string {
    return `${context.contextType} (${context.contextId.substr(-6)})`;
  }

  toolbarItem(): UI.Toolbar.ToolbarItem {
    return this.toolbarItemInternal;
  }
}

export const enum Events {
  CONTEXT_SELECTED = 'ContextSelected',
}

export interface EventTypes {
  [Events.CONTEXT_SELECTED]: Protocol.WebAudio.BaseAudioContext|null;
}
