///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2026, Open Design Alliance (the "Alliance").
// All rights reserved.
//
// This software and its documentation and related materials are owned by
// the Alliance. The software may only be incorporated into application
// programs owned by members of the Alliance, subject to a signed
// Membership Agreement and Supplemental Software License Agreement with the
// Alliance. The structure and organization of this software are the valuable
// trade secrets of the Alliance and its suppliers. The software is also
// protected by copyright law and international treaty provisions. Application
// programs incorporating this software must include the following statement
// with their copyright notices:
//
//   This application incorporates Open Design Alliance software pursuant to a
//   license agreement with Open Design Alliance.
//   Open Design Alliance Copyright (C) 2002-2026 by Open Design Alliance.
//   All rights reserved.
//
// By use of this software, its documentation or related materials, you
// acknowledge and accept the above terms.
///////////////////////////////////////////////////////////////////////////////

import { IEventEmitter } from "@inweb/eventemitter2";
import { Client } from "@inweb/client";
import { ICommandService } from "../commands/ICommands";
import { IOptions } from "../options/IOptions";
import { IDragger } from "../draggers/IDraggers";
import { IComponent } from "../components/IComponents";
import { FileSource, ILoader } from "../loaders/ILoader";
import { IModel } from "../models/IModel";
import { IViewpoint } from "./IViewpoint";
import { IInfo } from "./IInfo";

/**
 * Viewer core interface.
 */
export interface IViewer extends IEventEmitter, ICommandService {
  /**
   * The `Client` instance that is used to load model reference files from the Open Cloud Server.
   */
  client: Client | undefined;

  /**
   * Viewer options.
   */
  options: IOptions;

  /**
   * {@link https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement | HTMLCanvasElement} for the
   * viewer used to operate on. Defined only while the viewer is initialized.
   */
  canvas: HTMLCanvasElement | undefined;

  /**
   * List of canvas events, such as mouse events or
   * {@link https://developer.mozilla.org/docs/Web/API/Pointer_events#event_types_and_global_event_handlers | pointer events}
   * or {@link https://developer.mozilla.org/docs/Web/API/TouchEvent#touch_event_types | touch events}
   * that the viewer should listen and redirect to the draggers and components.
   *
   * By default, the following events are redirected:
   *
   * - click
   * - contextmenu
   * - dblclick
   * - mousedown
   * - mouseleave
   * - mousemove
   * - mouseup
   * - pointercancel
   * - pointerdown
   * - pointerleave
   * - pointermove
   * - pointerup
   * - touchcancel
   * - touchend
   * - touchmove
   * - touchstart
   * - wheel
   */
  canvasEvents: string[];

  /**
   * List of active loaders used to load models into the viewer.
   */
  loaders: ILoader[];

  /**
   * List of models loaded into the viewer.
   */
  models: IModel[];

  /**
   * List of names of available draggers.
   *
   * The following draggers are available by default:
   *
   * - Pan
   * - Orbit
   * - Zoom
   * - MeasureLine
   * - CuttingPlaneXAxis
   * - CuttingPlaneYAxis
   * - CuttingPlaneZAxis
   * - Walk
   *
   * For a quick reference on how to implement your own dragger, see {@link IDragger}.
   */
  draggers: string[];

  /**
   * List of names of available components.
   */
  components: string[];

  /**
   * Statistical information about the GPU memory and the rendering process.
   */
  info: IInfo;

  /**
   * Initializes the viewer it with the specified canvas. Call {@link dispose | dispose()} to release
   * allocated resources.
   *
   * Fires:
   *
   * - {@link InitializeEvent | initialize}
   * - {@link InitializeProgressEvent | initializeprogress}
   *
   * @param canvas -
   *   {@link https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement | HTMLCanvasElement} for the
   *   viewer used to operate on.
   * @param onProgress - A callback function that handles events measuring progress of viewer
   *   initialization.
   */
  initialize(canvas: HTMLCanvasElement, onProgress?: (event: ProgressEvent) => void): Promise<this>;

  /**
   * Unloads an open file, clears the canvas and markups, and releases resources allocated by this viewer
   * instance. Call this method before release the `Viewer` instance.
   */
  dispose(): this;

  /**
   * Returns `true` if viewer has been initialized.
   */
  isInitialized(): boolean;

  /**
   * Resizes the output canvas to (width, height) with device pixel ratio taken into account.
   *
   * Fires:
   *
   * - {@link ResizeEvent | resize}
   *
   * @param width - The width of the canvas.
   * @param height - The height of the canvas.
   * @param updateStyle - Setting `updateStyle` to false prevents any style changes to the output canvas.
   */
  setSize(width: number, height: number, updateStyle?: boolean): void;

  /**
   * Updates the viewer.
   *
   * Fires:
   *
   * - {@link UpdateEvent | update}
   * - {@link RenderEvent | render}
   *
   * @param force - If `true` updates the viewer immidietly. If a `number` is specified and more than the
   *   given amount of milliseconds has elapsed since the last rendering, the update is performed
   *   immediately as well. Otherwise the update will be scheduled for the next animation frame. Default
   *   is `false`.
   */
  update(force?: boolean | number): void;

  /**
   * Loads a file into the viewer.
   *
   * The viewer must be {@link initialize | initialized} before opening the file. Otherwise, `open()` does
   * nothing.
   *
   * This method requires a `Client` instance to be specified to load file from the Open Cloud Server.
   * The file geometry data on the Open Cloud Server must be converted into a format siutable for the
   * viewer, otherwise an exception will be thrown.
   *
   * For files from Open Cloud Server, the default model will be loaded. If there is no default model,
   * first availiable model will be loaded. If no models are found in the file, an exception will be
   * thrown.
   *
   * For URLs, the file extension is used to determine the file format. For a `ArrayBuffer` and `Data
   * URL`, a file format must be specified using `params.format` parameter. If no appropriate loader is
   * found for the specified format, an exception will be thrown.
   *
   * If there was an active dragger before opening the file, it will be deactivated. After opening the
   * file, you must manually activate the required dragger.
   *
   * Fires:
   *
   * - {@link CancelEvent | cancel}
   * - {@link ClearEvent | clear}
   * - {@link OpenEvent | open}
   * - {@link GeometryStartEvent | geometrystart}
   * - {@link GeometryProgressEvent | geometryprogress}
   * - {@link DatabaseChunkEvent | databasechunk}
   * - {@link GeometryChunkEvent | geometrychunk}
   * - {@link GeometryEndEvent | geometryend}
   * - {@link GeometryErrorEvent | geometryerror}
   *
   * @param file - File to load. Can be:
   *
   *   - `File`, `Assembly` or `Model` instance from the Open Cloud Server
   *   - `URL` string
   *   - {@link https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs | Data URL} string
   *   - {@link https://developer.mozilla.org/docs/Web/API/File | Web API File} object
   *   - {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer | ArrayBuffer}
   *       object
   *
   * @param params - Loading parameters.
   * @param params.format - File format. Required when loading a file as `ArrayBuffer` or `Data URL`.
   * @param params.mode - File opening mode. Can be one of:
   *
   *   - `file` - Single file mode. Unloads an open file and opens a new one. This is default mode.
   *   - `assembly` - Assembly mode. Appends a file to an already open file. This mode is not supported for
   *       all viewers.
   *
   * @param params.modelId - Unique model ID in the assembly (multi-model scene). Used as a model prefix
   *   when selecting objects (see {@link getSelected2}). Must not contain the ":" (colon). Required when
   *   loading a file as `ArrayBuffer` or `Data URL`.
   * @param params.requestHeader - The
   *   {@link https://developer.mozilla.org/docs/Glossary/Request_header | request header} used in HTTP
   *   request.
   * @param params.withCredentials - Whether the HTTP request uses credentials such as cookies,
   *   authorization headers or TLS client certificates. See
   *   {@link https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/withCredentials | XMLHttpRequest.withCredentials}
   *   for mode details.
   */
  open(
    file: FileSource,
    params: {
      format?: string;
      mode?: string;
      modelId?: string;
      requestHeader?: HeadersInit;
      withCredentials?: boolean;
    }
  ): Promise<this>;

  /**
   * Cancels asynchronous file loading started with {@link open | open()}.
   *
   * Fires:
   *
   * - {@link CancelEvent | calcel}
   */
  cancel(): this;

  /**
   * Unloads an open file, clears the canvas and markups, deactivates the active dragger.
   *
   * Fires:
   *
   * - {@link ClearEvent | clear}
   */
  clear(): this;

  /**
   * Returns `true` if current opened model is 3D model.
   */
  is3D(): boolean;

  /**
   * Creates an overlay view. Overlay view is used to draw cutting planes and markups.
   */
  syncOverlay(): void;

  /**
   * Clears the markup overlay view.
   *
   * Fires:
   *
   * - {@link ClearOverlayEvent | clearoverlay}
   */
  clearOverlay(): void;

  /**
   * Removes all cutting planes.
   *
   * Fires:
   *
   * - {@link ClearSlicesEvent | clearslices}
   */
  clearSlices(): void;

  /**
   * Returns a list of original handles for the selected objects. To avoid handle collisions in
   * assemblies (multi-model scenes), use {@link getSelected2} instead.
   *
   * @returns The list of original object handles.
   */
  getSelected(): string[];

  /**
   * Returns a list of original handles for the selected objects in assemblies (multi-model scenes).
   *
   * @returns The list of original object handles with model prefix in format "model:handle".
   */
  getSelected2(): string[];

  /**
   * Selects the objects by original handles. To avoid handle collisions in assemblies (multi-model
   * scenes), use {@link setSelected2} instead.
   *
   * Fires:
   *
   * - {@link SelectEvent | select}
   * - {@link Select2Event | select2}
   *
   * @param handles - The list of original handles.
   */
  setSelected(handles?: string[]): void;

  /**
   * Selects the objects by original handles in assemblies (multi-model scenes).
   *
   * Fires:
   *
   * - {@link SelectEvent | select}
   * - {@link Select2Event | select2}
   *
   * @param handles - The list of original handles with model prefix in format "model:handle".
   */
  setSelected2(handles?: string[]): void;

  /**
   * Unselects all objects.
   *
   * Fires:
   *
   * - {@link SelectEvent | select}
   */
  clearSelected(): void;

  /**
   * Makes the selected objects invisible.
   *
   * Fires:
   *
   * - {@link HideEvent | hide}
   * - {@link SelectEvent | select}
   */
  hideSelected(): void;

  /**
   * Hides all objects except selected.
   *
   * Fires:
   *
   * - {@link IsolateEvent | isolate}
   */
  isolateSelected(): void;

  /**
   * Makes all objects visible.
   *
   * Fires:
   *
   * - {@link ShowAllEvent | showall}
   */
  showAll(): void;

  /**
   * Breaks the model into its component objects. To collect objects back use index `0`.
   *
   * Fires:
   *
   * - {@link ExplodeEvent | explode}
   *
   * @param index - Explode index. Range is 0 to 100.
   */
  explode(index: number): void;

  /**
   * Collect model objects back. Alias to {@link explode | explode(0)}.
   *
   * Fires:
   *
   * - {@link ExplodeEvent | explode}
   */
  collect(): void;

  /**
   * Returns the active dragger reference, or `null` if there is no active dragger.
   */
  activeDragger(): IDragger | null;

  /**
   * Changes the active dragger. The viewer must be initialized before activating the dragger, otherwise
   * an exception will be thrown.
   *
   * Fires:
   *
   * - {@link ChangeActiveDraggerEvent | changeactivedragger}
   *
   * @param name - Dragger name. Can be one of the {@link draggers} list or an ampty string to deactivate
   *   the current dragger.
   * @returns Returns the new active dragger reference or `null` if there is no dragger with the given
   *   name.
   */
  setActiveDragger(name: string): IDragger | null;

  /**
   * Resets the state of the active dragger.
   */
  resetActiveDragger(): void;

  /**
   * Returns the component reference, or `null` if there is no component with the specified name.
   *
   * @param name - Component name. Can be one of the {@link components} list.
   */
  getComponent(name: string): IComponent | null;

  /**
   * Sets the viewer state to the specified viewpoint.
   *
   * To get a list of available viewpoints from the Open Cloud Server for a specific file, use the
   * `File.getViewpoints()`.
   *
   * Fires:
   *
   * - {@link ViewpointEvent | drawviewpoint}
   *
   * @param viewpoint - Viewpoint data.
   */
  drawViewpoint(viewpoint: IViewpoint): void;

  /**
   * Saves the viewer state at the viewpoint.
   *
   * To save a viewpoint to the Open Cloud Server for a specific file, use the `File.saveViewpoint()`.
   *
   * Fires:
   *
   * - {@link ViewpointEvent | createviewpoint}
   */
  createViewpoint(): IViewpoint;

  /**
   * Creates a snapshot image of the current viewer state.
   *
   * If the viewer has an active markup, combines the viewer canvas with the markup overlay. Otherwise,
   * captures only the viewer canvas.
   *
   * @param type - The MIME type of the image format. Can be one of:
   *
   *   - `image/png` - PNG format with lossless compression (recommended for quality)
   *   - `image/jpeg` - JPEG format with lossy compression (smaller file size)
   *   - `image/webp` - WebP format (modern browsers only)
   *
   *   If not specified, uses {@link IOptions.snapshotMimeType | snapshotMimeType} option.
   * @param quality - A number between 0 and 1 indicating the image quality for lossy formats. If not
   *   specified, uses {@link IOptions.snapshotQuality | snapshotQuality} option.
   * @returns A {@link https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs | Data URL}
   *   string containing the snapshot image.
   */
  getSnapshot(type?: string, quality?: number): string;
}
