import { WebsocketConnection } from '../WebsocketConnection';

export interface vtkSubscription {
  unsubscribe(): void;
}

/**
 * Bind optional dependency from WSLink to our current class.
 * This is mandatory when using that class
 *
 * ```
 * import SmartConnect from 'wslink/src/SmartConnect';
 * import vtkWSLinkClient from '@kitware/vtk.js/IO/Core/WSLinkClient';
 *
 * vtkWSLinkClient.setSmartConnectClass(SmartConnect);
 * ```
 *
 * @param smartConnectClass
 */
export function setSmartConnectClass(smartConnectClass: object): void;

export interface vtkWSLinkClient {
  /**
   * Virtually increase work load to maybe keep isBusy() on
   * while executing a synchronous task.
   */
  beginBusy(): void;

  /**
   * Virtually decreasing work load to maybe free isBusy()
   * after executing a synchronous task. Other async calls
   * could still keep the state as busy.
   */
  endBusy(): void;

  /**
   * Return the current state of busy.
   * Do we still have pending calls?
   */
  isBusy(): boolean;

  /**
   * Return true if the client is currently connected to a server
   */
  isConnected(): boolean;

  /**
   * Initiate the connection with the server
   * @param {Object} config
   * @param {Function} [configDecorator] (default: null)
   */
  connect(
    config: object,
    configDecorator?: (config: object) => object,
  ): Promise<vtkWSLinkClient>;

  /**
   * Disconnect from server
   * @param {Number} timeout amount of second to wait before the server exit as well. If we want to avoid the server from quitting, `-1` should be provided. (default=60)
   */
  disconnect(timeout: number): void;

  /**
   * Register dynamically a protocol after being connected
   *
   * @param {String} name
   * @param {Function} protocol
   */
  registerProtocol(name: string, protocol: (session: object) => object): void;

  /**
   * Remove a given protocol from the available list
   *
   * @param {String} name
   */
  unregisterProtocol(name: string): void;

  // --- via macro --

  /**
   * Assign protocols to the client. Those will only be used at connect time and therefore needs to be set before being connected otherwise `registerProtocol` should be used instead.
   * @returns {Boolean} true if the set method modified the object
   */
  setProtocols(protocols: Record<string, any>): boolean;

  /**
   * Get protocols that were either provided in `newInstance` or via its set
   */
  getProtocols(): Record<string, any>;

  /**
   * Update the list of methods that should be ignore from the busy state monitoring
   * @returns {Boolean} true if the set method modified the object
   */
  setNotBusyList(methodList: [string]): boolean;

  /**
   * @returns {object} the current set of methods to ignore from busy state
   */
  getNotBusyList(): object;

  /**
   * Set a config decorator to possibly alternate the config object that get received from the launcher.
   * @param decorator function for config object
   */
  setConfigDecorator(decorator: (config: object) => object): boolean;

  /**
   * @returns {Function} configDecorator function if any was provided
   */
  getConfigDecorator(): (config: object) => object;

  /**
   *
   */
  getConnection(): WebsocketConnection | null;

  /**
   *
   */
  getConfig(): object;

  /**
   *
   */
  getRemote(): Record<string, any>;

  /**
   * Register a callback for the busyChange event
   * @param callback
   *
   * @returns {vtkSubscription} subscription object that can be used to unsubscribe from the event.
   */
  onBusyChange(callback: (count: number) => void): vtkSubscription;

  /**
   * Emits a busyChange event
   */
  invokeBusyChange(count: number): void;

  /**
   * Emits a busyChange event
   */
  fireBusyChange(count: number): void;

  /**
   * Register a callback for the connectionReady event
   * @param callback
   *
   * @returns {vtkSubscription} subscription object that can be used to unsubscribe from the event.
   */
  onConnectionReady(callback: (client: vtkWSLinkClient) => void): vtkSubscription;

  /**
   * Emits a connectionReady event
   */
  invokeConnectionReady(client: vtkWSLinkClient): void

  /**
   * Emits a connectionReady event
   */
  fireConnectionReady(client: vtkWSLinkClient): void

  /**
   * Register a callback for the connectionError event
   * @param callback
   *
   * @returns {vtkSubscription} subscription object that can be used to unsubscribe from the event.
   */
  onConnectionError(callback: (...args: any[]) => void): vtkSubscription;

  /**
   * Emits a connectionError event
   */
  invokeConnectionError(...args: any[]): void

  /**
   * Emits a connectionError event
   */
  fireConnectionError(...args: any[]): void

  /**
   * Register a callback for the connectionClose event
   * @param callback
   *
   * @returns {vtkSubscription} subscription object that can be used to unsubscribe from the event.
   */
  onConnectionClose(callback: (...args: any[]) => void): vtkSubscription;

  /**
   * Emits a connectionClose event
   */
  invokeConnectionClose(...args: any[]): void

  /**
   * Emits a connectionClose event
   */
  fireConnectionClose(...args: any[]): void
}

/**
 * Method use to decorate a given object (publicAPI+model) with vtkWSLinkClient characteristics.
 *
 * @param publicAPI object on which methods will be bounds (public)
 * @param model object on which data structure will be bounds (protected)
 * @param {object} [initialValues] (default: {})
 */
export function extend(
  publicAPI: object,
  model: object,
  initialValues?: object,
): void;

// ----------------------------------------------------------------------------

/**
 * Method use to create a new instance of vtkWSLinkClient
 * @param {object} [initialValues] for pre-setting some of its content
 */
export function newInstance(initialValues?: object): vtkWSLinkClient;

/**
 * vtkWSLinkClient is a WSLink client for talking to a server over WebSocket
 */
export declare const vtkWSLinkClient: {
  newInstance: typeof newInstance;
  extend: typeof extend;
  // static
  setSmartConnectClass: typeof setSmartConnectClass;
};

export default vtkWSLinkClient;
