// Copyright 2017 The Chromium Authors
// 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 type * as Protocol from '../../generated/protocol.js';
import type * as Bindings from '../../models/bindings/bindings.js';
import type * as UI from '../../ui/legacy/legacy.js';

export class ProfileHeader extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
  readonly #profileType: ProfileType;
  title: string;
  uid: number;
  #fromFile = false;
  tempFile: Bindings.TempFile.TempFile|null = null;

  constructor(profileType: ProfileType, title: string) {
    super();
    this.#profileType = profileType;
    this.title = title;
    this.uid = profileType.incrementProfileUid();
  }

  setTitle(title: string): void {
    this.title = title;
    this.dispatchEventToListeners(Events.PROFILE_TITLE_CHANGED, this);
  }

  profileType(): ProfileType {
    return this.#profileType;
  }

  updateStatus(subtitle: string|null, wait?: boolean): void {
    this.dispatchEventToListeners(Events.UPDATE_STATUS, new StatusUpdate(subtitle, wait));
  }

  removeTempFile(): void {
    if (this.tempFile) {
      this.tempFile.remove();
    }
  }

  dispose(): void {
  }

  canSaveToFile(): boolean {
    return false;
  }

  saveToFile(): void {
    throw new Error('Not implemented.');
  }

  loadFromFile(_file: File): Promise<Error|DOMError|null> {
    throw new Error('Not implemented.');
  }

  fromFile(): boolean {
    return this.#fromFile;
  }

  setFromFile(): void {
    this.#fromFile = true;
  }

  setProfile(_profile: Protocol.Profiler.Profile): void {
  }
}

export class StatusUpdate {
  subtitle: string|null;
  wait: boolean|undefined;
  constructor(subtitle: string|null, wait: boolean|undefined) {
    this.subtitle = subtitle;
    this.wait = wait;
  }
}

export const enum Events {
  UPDATE_STATUS = 'UpdateStatus',
  PROFILE_TITLE_CHANGED = 'ProfileTitleChanged',
}

export interface EventTypes {
  [Events.UPDATE_STATUS]: StatusUpdate;
  [Events.PROFILE_TITLE_CHANGED]: ProfileHeader;
}

export class ProfileType extends Common.ObjectWrapper.ObjectWrapper<ProfileEventTypes> {
  readonly #id: string;
  readonly #name: string;
  profiles: ProfileHeader[] = [];
  #profileBeingRecorded: ProfileHeader|null = null;
  #nextProfileUid = 1;

  constructor(id: string, name: string) {
    super();
    this.#id = id;
    this.#name = name;

    if (!window.opener) {
      window.addEventListener('pagehide', this.clearTempStorage.bind(this), false);
    }
  }

  typeName(): string {
    return '';
  }

  nextProfileUid(): number {
    return this.#nextProfileUid;
  }

  incrementProfileUid(): number {
    return this.#nextProfileUid++;
  }

  hasTemporaryView(): boolean {
    return false;
  }

  fileExtension(): string|null {
    return null;
  }

  get buttonTooltip(): string {
    return '';
  }

  get id(): string {
    return this.#id;
  }

  get treeItemTitle(): string {
    return this.#name;
  }

  get name(): string {
    return this.#name;
  }

  buttonClicked(): boolean {
    return false;
  }

  get description(): string {
    return '';
  }

  isInstantProfile(): boolean {
    return false;
  }

  isEnabled(): boolean {
    return true;
  }

  getProfiles(): ProfileHeader[] {
    return this.profiles.filter(profile => this.#profileBeingRecorded !== profile);
  }

  customContent(): Element|null {
    return null;
  }

  setCustomContentEnabled(_enable: boolean): void {
  }

  loadFromFile(file: File): Promise<Error|DOMError|null> {
    let name: string = file.name;
    const fileExtension = this.fileExtension();
    if (fileExtension && name.endsWith(fileExtension)) {
      name = name.substr(0, name.length - fileExtension.length);
    }
    const profile = this.createProfileLoadedFromFile(name);
    profile.setFromFile();
    this.setProfileBeingRecorded(profile);
    this.addProfile(profile);
    return profile.loadFromFile(file);
  }

  createProfileLoadedFromFile(_title: string): ProfileHeader {
    throw new Error('Not implemented');
  }

  addProfile(profile: ProfileHeader): void {
    this.profiles.push(profile);
    this.dispatchEventToListeners(ProfileEvents.ADD_PROFILE_HEADER, profile);
  }

  removeProfile(profile: ProfileHeader): void {
    const index = this.profiles.indexOf(profile);
    if (index === -1) {
      return;
    }
    this.profiles.splice(index, 1);
    this.disposeProfile(profile);
  }

  clearTempStorage(): void {
    for (let i = 0; i < this.profiles.length; ++i) {
      this.profiles[i].removeTempFile();
    }
  }

  profileBeingRecorded(): ProfileHeader|null {
    return this.#profileBeingRecorded;
  }

  setProfileBeingRecorded(profile: ProfileHeader|null): void {
    this.#profileBeingRecorded = profile;
  }

  profileBeingRecordedRemoved(): void {
  }

  reset(): void {
    for (const profile of this.profiles.slice()) {
      this.disposeProfile(profile);
    }
    this.profiles = [];
    this.#nextProfileUid = 1;
  }

  disposeProfile(profile: ProfileHeader): void {
    this.dispatchEventToListeners(ProfileEvents.REMOVE_PROFILE_HEADER, profile);
    profile.dispose();
    if (this.#profileBeingRecorded === profile) {
      this.profileBeingRecordedRemoved();
      this.setProfileBeingRecorded(null);
    }
  }
}

export const enum ProfileEvents {
  ADD_PROFILE_HEADER = 'add-profile-header',
  PROFILE_COMPLETE = 'profile-complete',
  REMOVE_PROFILE_HEADER = 'remove-profile-header',
  VIEW_UPDATED = 'view-updated',
}

export interface ProfileEventTypes {
  [ProfileEvents.ADD_PROFILE_HEADER]: ProfileHeader;
  [ProfileEvents.PROFILE_COMPLETE]: ProfileHeader;
  [ProfileEvents.REMOVE_PROFILE_HEADER]: ProfileHeader;
  [ProfileEvents.VIEW_UPDATED]: void;
}

export interface DataDisplayDelegate {
  showProfile(profile: ProfileHeader|null): UI.Widget.Widget|null;
  showObject(snapshotObjectId: string, perspectiveName: string): void;
  linkifyObject(nodeIndex: number): Promise<Element|null>;
}
