///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2026, 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-2026 by Open Design Alliance.
//   All rights reserved.
//
// By use of this software, its documentation or related materials, you
// acknowledge and accept the above terms.
///////////////////////////////////////////////////////////////////////////////

import { Euler, Quaternion, Object3D, Vector3 } from "three";

import { CommandEvent, IComponent, RenderEvent } from "@inweb/viewer-core";
import type { Viewer } from "../Viewer";

export const defaultViewPositions = {
  top: new Vector3(0, 0, 1),
  bottom: new Vector3(0, 0, -1),
  left: new Vector3(-1, 0, 0),
  right: new Vector3(1, 0, 0),
  front: new Vector3(0, 1, 0),
  back: new Vector3(0, -1, 0),
  sw: new Vector3(-0.5, -0.5, 1.0).normalize(),
  se: new Vector3(0.5, -0.5, 1.0).normalize(),
  ne: new Vector3(0.5, 0.5, 1.0).normalize(),
  nw: new Vector3(-0.5, 0.5, 1.0).normalize(),
};

export class ViewPositionComponent implements IComponent {
  private position: string;
  private center: Vector3;
  private targetPosition: Vector3;
  private targetQuaternion: Quaternion;
  private radius: number;
  private q1: Quaternion;
  private q2: Quaternion;
  private animating: boolean;
  private viewer: Viewer;

  constructor(viewer: Viewer) {
    this.animating = false;
    // this.center = new THREE.Vector3();
    this.targetPosition = new Vector3();
    this.targetQuaternion = new Quaternion();
    this.q1 = new Quaternion();
    this.q2 = new Quaternion();
    this.viewer = viewer;
    this.viewer.addEventListener("render", this.onRender);
    this.viewer.addEventListener("command", this.onCommand);

    console.log("--- ViewPositionComponent.constructor");
  }

  dispose() {
    this.viewer.off("render", this.onRender);
    this.viewer.off("viewposition", this.onCommand);

    console.log("--- ViewPositionComponent.dispose");
  }

  onCommand = (event: CommandEvent) => {
    if (this.animating) return;

    this.position = event.data === "setDefaultViewPosition" ? event.args[0] : event.data;

    console.log("--- ViewPositionComponent:onCommand", this.position);

    switch (this.position) {
      case "left":
        this.targetPosition.set(-1, 0, 0);
        this.targetQuaternion.setFromEuler(new Euler(0, -Math.PI * 0.5, 0));
        break;

      case "right":
        this.targetPosition.set(1, 0, 0);
        this.targetQuaternion.setFromEuler(new Euler(0, Math.PI * 0.5, 0));
        break;

      case "front":
        this.targetPosition.set(0, 1, 0);
        this.targetQuaternion.setFromEuler(new Euler(-Math.PI * 0.5, 0, 0));
        // this.targetQuaternion.setFromEuler(new Euler(Math.PI * 0.5, 0, 0));
        break;

      case "back":
        this.targetPosition.set(0, -1, 0);
        this.targetQuaternion.setFromEuler(new Euler(Math.PI * 0.5, 0, 0));
        break;

      case "top":
      default:
        this.targetPosition.set(0, 0, 1);
        this.targetQuaternion.setFromEuler(new Euler());
        break;

      case "bottom":
        this.targetPosition.set(0, 0, -1);
        this.targetQuaternion.setFromEuler(new Euler(0, Math.PI, 0));
        break;
    }

    this.center = this.viewer.extents.getCenter(new Vector3());

    this.radius = this.viewer.camera.position.distanceTo(this.center);
    this.targetPosition.multiplyScalar(this.radius).add(this.center);

    const dummy = new Object3D();
    dummy.position.copy(this.center);

    dummy.lookAt(this.viewer.camera.position);
    this.q1.copy(dummy.quaternion);

    dummy.lookAt(this.targetPosition);
    this.q2.copy(dummy.quaternion);

    this.animating = true;

    // this.viewer.camera.position.set(0, 0, 1).applyQuaternion(this.q2).multiplyScalar(this.radius).add(this.center);
    // this.viewer.camera.quaternion.copy(this.targetQuaternion);
    // this.viewer.camera.updateProjectionMatrix();

    this.viewer.update();
  };

  onRender = (event: RenderEvent) => {
    if (!this.animating) return;

    const turnRate = 2 * Math.PI; // turn rate in angles per second
    const step = (event.deltaTime * turnRate) / 10;

    // animate position by doing a slerp and then scaling the position on the unit sphere
    this.q1.rotateTowards(this.q2, step);
    this.viewer.camera.position.set(0, 0, 1).applyQuaternion(this.q1).multiplyScalar(this.radius).add(this.center);

    // animate orientation
    this.viewer.camera.quaternion.rotateTowards(this.targetQuaternion, step);

    this.viewer.update();

    if (this.q1.angleTo(this.q2) === 0) {
      this.animating = false;

      // console.log("--- ViewPositionComponent.onRender", event.deltaTime, step, this.animating);

      this.viewer.emit({ type: "viewposition", data: this.position });
      return;
    }

    // console.log("--- ViewPositionComponent.onRender", event.deltaTime, step, this.animating);
  };
}
