/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as dom from '../Dom';
import { DisposableStore, IDisposable, toDisposable } from '../../common/Lifecycle';

type PointerMoveCallback = (event: PointerEvent) => void;
type OnStopCallback = () => void;

export class GlobalPointerMoveMonitor implements IDisposable {

  private readonly _hooks = new DisposableStore();
  private _pointerMoveCallback: PointerMoveCallback | null = null;
  private _onStopCallback: OnStopCallback | null = null;

  public dispose(): void {
    this.stopMonitoring(false);
    this._hooks.dispose();
  }

  public stopMonitoring(invokeStopCallback: boolean): void {
    if (!this.isMonitoring()) {
      return;
    }

    this._hooks.clear();
    this._pointerMoveCallback = null;
    const onStopCallback = this._onStopCallback;
    this._onStopCallback = null;

    if (invokeStopCallback && onStopCallback) {
      onStopCallback();
    }
  }

  public isMonitoring(): boolean {
    return !!this._pointerMoveCallback;
  }

  public startMonitoring(
    initialElement: Element,
    pointerId: number,
    initialButtons: number,
    pointerMoveCallback: PointerMoveCallback,
    onStopCallback: OnStopCallback
  ): void {
    if (this.isMonitoring()) {
      this.stopMonitoring(false);
    }
    this._pointerMoveCallback = pointerMoveCallback;
    this._onStopCallback = onStopCallback;

    let eventSource: Element | Window = initialElement;

    try {
      initialElement.setPointerCapture(pointerId);
      this._hooks.add(toDisposable(() => {
        try {
          initialElement.releasePointerCapture(pointerId);
        } catch {
          // ignore
        }
      }));
    } catch {
      eventSource = dom.getWindow(initialElement);
    }

    this._hooks.add(dom.addDisposableListener(
      eventSource,
      dom.eventType.POINTER_MOVE,
      (e) => {
        if (e.buttons !== initialButtons) {
          this.stopMonitoring(true);
          return;
        }

        e.preventDefault();
        this._pointerMoveCallback!(e);
      }
    ));

    this._hooks.add(dom.addDisposableListener(
      eventSource,
      dom.eventType.POINTER_UP,
      (e: PointerEvent) => this.stopMonitoring(true)
    ));
  }
}
