///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2025, 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-2025 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 { Assembly, Client, File, Model } from "@inweb/client";
import { ICommandService } from "../commands/ICommands";
import { IOptions } from "../options/IOptions";
import { IDragger } from "../draggers/IDraggers";
import { IComponent } from "../components/IComponents";
import { IViewpoint } from "./IViewpoint";

/**
 * 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 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[];

  /**
   * 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;

  /**
   * Updates the viewer.
   *
   * Fires:
   *
   * - {@link UpdateEvent | update}
   *
   * @param force - If `true` updates the viewer immidietly. Otherwise updates on next animation frame.
   *   Default is `false`.
   */
  update(force?: boolean): void;

  /**
   * Loads a file from Open Cloud Server into the viewer.
   *
   * The file geometry data on the server must be converted into a format siutable for the viewer.
   *
   * This method requires a `Client` instance to be specified to load model reference files from the Open
   * Cloud Server.
   *
   * 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 OpenEvent | open}
   * - {@link GeometryStartEvent | geometrystart}
   * - {@link GeometryProgressEvent | geometryprogress}
   * - {@link DatabaseChunkEvent | databasechunk}
   * - {@link GeometryChunkEvent | geometrychunk}
   * - {@link GeometryEndEvent | geometryend}
   * - {@link GeometryErrorEvent | geometryerror}
   *
   * @param file - File, assembly or specific model to load. If a `File` instance with multiple models is
   *   specified, the default model will be loaded. If there is no default model, first availiable model
   *   will be loaded.
   */
  open(file: Model | File | Assembly): Promise<this>;

  /**
   * Cancels asynchronous file loading started by {@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 overlay view.
   */
  clearOverlay(): void;

  /**
   * Removes all cutting planes.
   */
  clearSlices(): void;

  /**
   * Returns a list of original handles for the selected objects.
   */
  getSelected(): string[];

  /**
   * Selects the objects by original handles.
   *
   * Fires:
   *
   * - {@link SelectEvent | select}
   *
   * @param handles - The list of original handles.
   */
  setSelected(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.
   */
  getComponent(name: string): IComponent | null;

  /**
   * Sets the viewer state to the specified viewpoint.
   *
   * To get a list of available viewpoints from the server for a specific file, use the
   * `File.getViewpoints()`.
   *
   * @param viewpoint - Viewpoint data.
   */
  drawViewpoint(viewpoint: IViewpoint): void;

  /**
   * Saves the viewer state at the viewpoint.
   *
   * To save a viewpoint to the server for a specific file, use the `File.saveViewpoint()`.
   */
  createViewpoint(): IViewpoint;
}
