///////////////////////////////////////////////////////////////////////////////
// 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.
///////////////////////////////////////////////////////////////////////////////
/* eslint-disable no-unused-vars */

import { CANVAS_EVENTS } from "@inweb/viewer-core";
import { Viewer } from "../../Viewer";
import { Point2d } from "./Geometry";
import { OdaGeAction } from "./OdaGeAction";

const CLICK_DELTA = 5;

const INTERACTIVITY_FPS = 24;

export class OdBaseDragger extends OdaGeAction {
  protected subject: Viewer;
  protected needInputText: boolean;
  protected mouseDownPosition: Point2d;
  protected autoSelect: boolean;
  protected onmessage: (event: any) => boolean;
  protected canvasEvents: string[];
  protected isDragging: boolean;
  protected press: boolean;

  public name: string;

  constructor(subject: Viewer) {
    super(subject.visualizeJs);
    this.subject = subject;
    this.needInputText = false;
    this.mouseDownPosition = { x: 0, y: 0 };
    this.autoSelect = false;

    this.onmessage = (event) => this.subject.emitEvent(event);
    this.canvasEvents = CANVAS_EVENTS;
  }

  public initialize(): void {
    this.canvasEvents = this.canvasEvents.filter((x) => typeof this[x] === "function");
    this.canvasEvents.forEach((x) => (this[x] = this[x].bind(this)));
    this.canvasEvents.forEach((x) => this.subject.on(x, this[x]));

    this.getViewer().setEnableAutoSelect(!!this.autoSelect);
  }

  public dispose(): void {
    this.canvasEvents.forEach((x) => this.subject.off(x, this[x]));
  }

  protected relativeCoords(event: MouseEvent): Point2d {
    return { x: event.offsetX * window.devicePixelRatio, y: event.offsetY * window.devicePixelRatio };
  }

  protected pointerdown(ev: PointerEvent): void {
    if (!ev.isPrimary || OdBaseDragger.isGestureActive) {
      return;
    }

    (ev.target as HTMLElement).setPointerCapture(ev.pointerId);

    const relCoord = this.relativeCoords(ev);
    this.isDragging = true;
    this.mouseDownPosition = { x: relCoord.x, y: relCoord.y };
    this.start(relCoord.x, relCoord.y, ev.clientX, ev.clientY);
    this.subject.update();
  }

  protected pointerup(ev: PointerEvent): void {
    if (OdBaseDragger.needSkipPointerUp) {
      return;
    }

    if (!ev.isPrimary) {
      return;
    }

    (ev.target as HTMLElement).releasePointerCapture(ev.pointerId);

    const relCoord = this.relativeCoords(ev);
    this.end(relCoord.x, relCoord.y, ev.clientX, ev.clientY);
    this.isDragging = false;
    this.subject.update();
  }

  protected pointercancel(ev: PointerEvent): void {
    if (!ev.isPrimary) {
      return;
    }

    this.m_module.canvas.dispatchEvent(new PointerEvent("pointerup", ev));
  }

  protected pointermove(ev: PointerEvent): void {
    if (!ev.isPrimary || OdBaseDragger.isGestureActive) {
      return;
    }

    const relCoord = this.relativeCoords(ev);
    this.drag(relCoord.x, relCoord.y, ev.clientX, ev.clientY);
    if (this.isDragging) {
      this.subject.update();
    }
  }

  protected click(ev: MouseEvent): void {
    const viewer = this.getViewer();
    const relCoord = this.relativeCoords(ev);
    const x = relCoord.x;
    const y = relCoord.y;

    const isNotDragging =
      Math.abs(x - this.mouseDownPosition.x) < CLICK_DELTA && Math.abs(y - this.mouseDownPosition.y) < CLICK_DELTA;

    if (viewer && viewer.getEnableAutoSelect() && isNotDragging) {
      viewer.unselect();
      viewer.select(x, y, x, y);
      this.subject.update();

      const selectionSet = viewer.getSelected();
      const handles = this.subject.getSelected();
      this.onmessage({ type: "select", data: selectionSet, handles });
    }
  }

  protected dblclick(ev: MouseEvent): void {
    const viewer = this.getViewer();
    const relCoord = this.relativeCoords(ev);
    const x = relCoord.x;
    const y = relCoord.y;

    const device = viewer.getActiveDevice();
    const clickView = device.viewAt([x, y]);
    if (clickView && !clickView.active) {
      viewer.activeView = clickView;
      clickView.delete();
      this.subject.update();
    } else {
      if (viewer && viewer.getEnableAutoSelect()) {
        const pSelected = viewer.getSelected();

        if (!pSelected.isNull() && pSelected.numItems() !== 0) {
          const itr = pSelected.getIterator();
          const entity = itr.getEntity();
          viewer.zoomToEntity(entity);

          this.onmessage({ type: "zoomtoentity", data: entity });
          this.subject.update();

          this.deleteAll([itr, entity]);
        }
      }
    }

    device.delete();
  }

  protected start(x: number, y: number, absoluteX = 0, absoluteY = 0): void {}

  protected drag(x: number, y: number, absoluteX = 0, absoluteY = 0): void {}

  protected end(x: number, y: number, absoluteX = 0, absoluteY = 0): void {}

  protected beginInteractivity = () => {
    const viewer = this.getViewer();
    const view = viewer.activeView;
    if (view["beginInteractivity"]) {
      view.beginInteractivity(INTERACTIVITY_FPS);
      this.subject.update();
    }
    view.delete();
  };

  protected endInteractivity = () => {
    const viewer = this.getViewer();
    const view = viewer.activeView;
    if (view["endInteractivity"]) {
      view.endInteractivity();

      const device = this.getViewer().getActiveDevice();
      const canvas = this.m_module.canvas;
      device.invalidate([0, 0, canvas.width, canvas.height]);
      device.delete();
      this.subject.update();
    }
    view.delete();
  };

  protected getActiveMarkupEntity(entityName: string): any {
    return this.subject.addMarkupEntity(entityName);
  }

  private syncOverlayView(): any {
    return this.subject.syncOverlay();
  }

  protected deleteAll(objects): void {
    for (const obj of objects) {
      obj?.delete?.();
    }
  }

  public updatePreview(): void {}

  private static _isGestureActive = false;

  protected static set isGestureActive(value: boolean) {
    if (OdBaseDragger._isGestureActive === value) {
      return;
    }

    OdBaseDragger._isGestureActive = value;
    if (OdBaseDragger._isGestureActive) {
      OdBaseDragger.needSkipPointerUp = true;
    }
  }

  protected static get isGestureActive() {
    return OdBaseDragger._isGestureActive;
  }

  private static _needSkipPointerUp = false;

  private static get needSkipPointerUp() {
    if (OdBaseDragger._needSkipPointerUp) {
      OdBaseDragger.needSkipPointerUp = false;
      return true;
    }

    return false;
  }

  private static set needSkipPointerUp(value: boolean) {
    OdBaseDragger._needSkipPointerUp = value;
  }
}
