namespace com.keyman.osk {
  /**
   * Represents the current location of the current cursor / touchpoint during
   * an ongoing OSK input event.  This class standardizes to .pageX (document)
   * coordinates, rather than .clientX (viewport) coordinates.
   */
  export class InputEventCoordinate {
    public readonly x: number;
    public readonly y: number;

    private readonly source: MouseEvent | TouchEvent;

    public constructor(x: number, y: number, source?: MouseEvent | TouchEvent) {
      this.x = x;
      this.y = y;

      if(source) {
        this.source = source;
      }
    }

    // Converts a MouseEvent or TouchEvent into the base coordinates needed 
    // by the mouse-dragging operations.
    public static fromEvent(e: MouseEvent | TouchEvent) {
      let coordSource: MouseEvent | Touch;

      // Desktop Safari versions as recent as 14.1 do not support TouchEvents.
      // So, just in case, a two-fold conditional check to avoid issues with a direct
      // 'instanceof' against the type.
      if(window['TouchEvent'] && e instanceof TouchEvent) {
        coordSource = e.changedTouches[0];
      } else if(e['changedTouches']) {
        coordSource = e['changedTouches'][0] as Touch;
      } else {
        coordSource = e as MouseEvent;
      }

      // For MouseEvents, .pageX is slightly less supported in older browsers when
      // compared to .clientX.  They're about equally supported for TouchEvents.
      if (coordSource.pageX) {
        return new InputEventCoordinate(coordSource.pageX, coordSource.pageY, e);
      } else if (coordSource.clientX) {
        const x = coordSource.clientX + document.body.scrollLeft;
        const y = coordSource.clientY + document.body.scrollTop;

        return new InputEventCoordinate(x, y, e);
      } else {
        return new InputEventCoordinate(null, null, e);
      }
    }

    public get activeInputCount(): number {
      // May not be an ACTUAL touch event during unit tests.
      if(window['TouchEvent'] && this.source['touches'] !== undefined && this.source['touches'] !== null) {
        return this.source['touches'].length;
      } else {
        const event = this.source as MouseEvent;
        return event.buttons > 0 ? 1 : 0;
      }
    }

    public get target() {
      return this.source?.target;
    }

    public get isFromTouch(): boolean {
      return !this.isFromMouse;
    }

    public get isFromMouse(): boolean {
      return this.source instanceof MouseEvent;
    }
  }
}