///////////////////////////////////////////////////////////////////////////////
// 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 { Viewer } from "../Viewer";
import { Point2d } from "./Common/Geometry";
import { ViewParams } from "./Common/OdaGeAction";
import { OdBaseDragger } from "./Common/OdBaseDragger";

export class OrbitAroundBuildingDragger extends OdBaseDragger {
  protected maxPolarAngle: number;
  protected minPolarAngle: number;
  protected m_viewCenter: any;
  protected m_startPoint: Point2d;
  protected startCameraParams: ViewParams;
  protected m_delta: number;

  constructor(viewer: Viewer) {
    super(viewer);
    this.autoSelect = true;
    this.press = false;
    this.maxPolarAngle = Math.PI / 2;
    this.minPolarAngle = 0; // radians
  }

  override start(x: number, y: number): void {
    this.press = true;

    this.m_viewCenter = this.getCenter();
    this.m_startPoint = { x, y };

    const view = this.getViewer().activeView;

    this.startCameraParams = this.getViewParams();

    const corners = view.vportRect;

    this.m_delta = Math.max(corners[1] - corners[0], corners[2] - corners[3]);

    this.beginInteractivity();
  }

  setDefaultViewParams(): void {
    this.setViewParams(this.startCameraParams);
  }

  override drag(x: number, y: number): void {
    if (this.press) {
      let dX = x - this.m_startPoint.x;
      let dY = y - this.m_startPoint.y;

      dX *= Math.PI / this.m_delta;
      dY *= Math.PI / this.m_delta;

      this.setDefaultViewParams();

      const { Vector3d, Matrix3d } = this.m_module;

      const target = Vector3d.createFromArray(this.startCameraParams.target);
      const offset = Vector3d.createFromArray(this.startCameraParams.position).sub(target);
      const dir = offset.normalize();

      const zMatrix = new Matrix3d();
      zMatrix.setToIdentity();
      const xMatrix = new Matrix3d();
      xMatrix.setToIdentity();

      // ---- restore start rotation ----

      const yAxis = Vector3d.createFromArray([dir.x, dir.y, dir.z]);
      const zAxis = Vector3d.createFromArray(this.startCameraParams.upVector);
      const xAxis = yAxis.crossProduct(zAxis);

      // ----------- zAxis rotation restore -----------
      let xyDir = Vector3d.createFromArray([yAxis.x, yAxis.y, 0]);

      if (xyDir.length() <= 0.00001) {
        xyDir.set(-xAxis.y, xAxis.x, 0);
      } else {
        xyDir = xyDir.normalize();
      }
      const xyAngle =
        Math.sign(xyDir.dotProduct(Vector3d.createFromArray([-1, 0, 0]))) *
        xyDir.angleTo(Vector3d.createFromArray([0, 1, 0]));
      dX -= xyAngle;
      // ----------- zAxis rotation restore -----------

      // ----------- xAxis rotation restore -----------
      let yzDir = Vector3d.createFromArray([dir.x, dir.y, 0]);
      let yzAngle = 0;
      if (yzDir.length() <= 0.00001) {
        yzAngle = (-dir.z * Math.PI) / 2;
      } else {
        yzDir = yzDir.normalize();
        yzAngle = -yzDir.angleTo(dir);
      }
      dY -= yzAngle;
      // ----------- xAxis rotation restore -----------

      // ---- restore start rotation ----

      zMatrix.setToRotation(-dX, [0, 0, 1], [0, 0, 0]);

      const xAngle = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, dY));
      xMatrix.setToRotation(xAngle, [1, 0, 0], [0, 0, 0]);

      const endMatrix = zMatrix.postMultBy(xMatrix);

      let pos = Vector3d.createFromArray([0, 1, 0]).transformBy(endMatrix);
      const up = Vector3d.createFromArray([0, 0, 1]).transformBy(endMatrix);

      pos.setLength(offset.length());
      pos = target.add(pos);

      const current = this.getViewParams();

      current.position = pos.toArray();
      current.upVector = up.toArray();

      this.setViewParams(current);
    }
  }

  override end(): void {
    this.press = false;
    this.endInteractivity();
  }

  getCenter(): any {
    const viewer = this.getViewer();

    let ext = viewer.getActiveExtents();

    const pSet = viewer.getSelected();
    if (!pSet.isNull() && pSet.numItems() !== 0) {
      const itr = pSet.getIterator();
      const entId = itr.getEntity();

      if (entId.getType() === 1) {
        const obj = entId.openObject();

        ext.delete();
        ext = obj.getExtents();

        obj.delete();
      } else if (entId.getType() === 2) {
        const obj = entId.openObjectAsInsert();
        const extTuple = obj.getExtents();

        ext.delete();
        ext = extTuple.ext;

        extTuple.delete();
        obj.delete();
      }

      itr.delete();
    }

    const center = ext.center();

    ext.delete();
    return center;
  }
}
