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

import '../../ui/legacy/legacy.js';

import * as i18n from '../../core/i18n/i18n.js';
import type * as SDK from '../../core/sdk/sdk.js';
import type * as UI from '../../ui/legacy/legacy.js';
import {html, nothing, render} from '../../ui/lit/lit.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';

import {AccessibilitySubPane} from './AccessibilitySubPane.js';

const UIStrings = {
  /**
   * @description Name of a tool which allows the developer to view the contents of the page in the
   * 'source order' (the order in which the HTML elements show up in the source code). In the
   * Accessibility panel.
   */
  sourceOrderViewer: 'Source Order Viewer',
  /**
   * @description Text in Source Order Viewer of the Accessibility panel shown when the selected node has no child elements
   */
  noSourceOrderInformation: 'No source order information available',
  /**
   * @description Text in Source Order Viewer of the Accessibility panel shown when the selected node has many child elements
   */
  thereMayBeADelayInDisplaying: 'There may be a delay in displaying source order for elements with many children',
  /**
   * @description Checkbox label in Source Order Viewer of the Accessibility panel. Source order
   * means the order in which the HTML elements show up in the source code.
   */
  showSourceOrder: 'Show source order',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/accessibility/SourceOrderView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
const MAX_CHILD_ELEMENTS_THRESHOLD = 300;

interface ViewInput {
  childCount: number;
  showSourceOrder: boolean|undefined;
  onShowSourceOrderChanged: (showSourceOrder: boolean) => void;
}

type View = (input: ViewInput, output: unknown, target: HTMLElement) => void;

const DEFAULT_VIEW: View = (input, _output, target) => {
  function onShowSourceOrderChanged(event: Event): void {
    const checkbox = event.currentTarget as UI.UIUtils.CheckboxLabel;
    input.onShowSourceOrderChanged(checkbox.checked);
    event.consume();
  }

  // clang-format off
  render(html`
    ${input.showSourceOrder === undefined
      ? html`
        <div class="gray-info-message info-message-overflow">
          ${i18nString(UIStrings.noSourceOrderInformation)}
        </div>
      `
      : html`
      ${input.childCount >= MAX_CHILD_ELEMENTS_THRESHOLD
        ? html`
          <div class="gray-info-message info-message-overflow"
                id="source-order-warning">
            ${i18nString(UIStrings.thereMayBeADelayInDisplaying)}
          </div>
        `
        : nothing
      }
      <devtools-checkbox class="source-order-checkbox"
                          jslog=${VisualLogging.toggle().track({click: true})}
                          ?checked=${input.showSourceOrder}
                          @change=${onShowSourceOrderChanged}>
        ${i18nString(UIStrings.showSourceOrder)}
      </devtools-checkbox>
      `
    }
  `, target);
  // clang-format on
};

export class SourceOrderPane extends AccessibilitySubPane {
  #childCount = 0;
  #showSourceOrder: boolean|undefined = undefined;
  readonly #view: View;

  constructor(view: View = DEFAULT_VIEW) {
    super({
      title: i18nString(UIStrings.sourceOrderViewer),
      viewId: 'source-order-viewer',
      jslog: `${VisualLogging.section('source-order-viewer')}`,
    });
    this.#view = view;
  }

  async setNodeAsync(node: SDK.DOMModel.DOMNode|null): Promise<void> {
    if (this.nodeInternal && this.#showSourceOrder) {
      this.nodeInternal.domModel().overlayModel().hideSourceOrderInOverlay();
    }
    super.setNode(node);
    this.#childCount = this.nodeInternal?.childNodeCount() ?? 0;
    if (!this.nodeInternal || !this.#childCount) {
      this.#showSourceOrder = undefined;
    } else {
      if (!this.nodeInternal.children()) {
        await this.nodeInternal.getSubtree(1, false);
      }
      const children = this.nodeInternal.children() as SDK.DOMModel.DOMNode[];
      if (!children.some(child => child.nodeType() === Node.ELEMENT_NODE)) {
        this.#showSourceOrder = undefined;
      } else if (this.#showSourceOrder === undefined) {
        this.#showSourceOrder = false;
      }
      if (this.#showSourceOrder) {
        this.nodeInternal.domModel().overlayModel().highlightSourceOrderInOverlay(this.nodeInternal);
      }
    }
    this.requestUpdate();
  }

  override async performUpdate(): Promise<void> {
    const onShowSourceOrderChanged = (showSourceOrder: boolean): void => {
      if (!this.nodeInternal) {
        this.#showSourceOrder = undefined;
        return;
      }
      if (showSourceOrder) {
        this.nodeInternal.domModel().overlayModel().highlightSourceOrderInOverlay(this.nodeInternal);
      } else {
        this.nodeInternal.domModel().overlayModel().hideSourceOrderInOverlay();
      }
      this.#showSourceOrder = showSourceOrder;
    };
    const input = {
      childCount: this.#childCount,
      showSourceOrder: this.#showSourceOrder,
      onShowSourceOrderChanged,
    };
    const output = undefined;
    this.#view(input, output, this.contentElement);
  }
}
