///////////////////////////////////////////////////////////////////////////////
// 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 {
  Camera,
  CylinderGeometry,
  CanvasTexture,
  Color,
  Mesh,
  MeshBasicMaterial,
  Object3D,
  OrthographicCamera,
  Sprite,
  SpriteMaterial,
  SRGBColorSpace,
  Vector4,
  WebGLRenderer,
} from "three";

export class WCSHelper extends Object3D {
  public camera: Camera;
  private orthoCamera: OrthographicCamera;
  public size: number;

  constructor(camera: Camera) {
    super();

    (this as any).type = "WCSHelper";
    this.camera = camera;
    this.size = 160;

    this.orthoCamera = new OrthographicCamera(-2, 2, 2, -2, 0, 4);
    this.orthoCamera.position.set(0, 0, 2);

    const matRed = new MeshBasicMaterial({ toneMapped: false, color: "#dd0000" });
    const matGreen = new MeshBasicMaterial({ toneMapped: false, color: "#00dd00" });
    const matBlue = new MeshBasicMaterial({ toneMapped: false, color: "#0000dd" });

    const spriteRed = this.getSpriteMaterial(matRed.color, "X");
    const spriteGreen = this.getSpriteMaterial(matGreen.color, "Y");
    const spriteBlue = this.getSpriteMaterial(matBlue.color, "Z");

    const lineGeometry = new CylinderGeometry(0.01, 0.01, 1, 3);
    lineGeometry.translate(0, 0.5, 0);

    const arrowGeometry = new CylinderGeometry(0, 0.1, 0.25, 12);
    arrowGeometry.translate(0, 0.625, 0);

    const axesMap = {
      X: [
        [new Mesh(arrowGeometry, matRed), [0.5, 0, 0], [0, 0, -Math.PI / 2]],
        [new Mesh(lineGeometry, matRed), [0, 0, 0], [0, 0, -Math.PI / 2]],
        [new Sprite(spriteRed), [1.55, 0, 0]],
      ],
      Y: [
        [new Mesh(arrowGeometry, matGreen), [0, 0.5, 0], null],
        [new Mesh(lineGeometry, matGreen), null, null],
        [new Sprite(spriteGreen), [0, 1.55, 0]],
      ],
      Z: [
        [new Mesh(arrowGeometry, matBlue), [0, 0, 0.5], [Math.PI / 2, 0, 0]],
        [new Mesh(lineGeometry, matBlue), null, [Math.PI / 2, 0, 0]],
        [new Sprite(spriteBlue), [0, 0, 1.55]],
      ],
    };

    Object.keys(axesMap).forEach((key) => {
      axesMap[key].forEach((objects: any) => {
        const object = objects[0];
        const position = objects[1];
        const rotation = objects[2];

        object.name = key;
        if (position) object.position.set(position[0], position[1], position[2]);
        if (rotation) object.rotation.set(rotation[0], rotation[1], rotation[2]);
        object.updateMatrixWorld();

        this.add(object);
      });
    });
  }

  dispose() {
    this.traverse((object: any) => {
      if (object.geometry) object.geometry.dispose();
      if (object.material) object.material.dispose();
    });
  }

  getSpriteMaterial(color: Color, text: string) {
    const canvas = document.createElement("canvas");
    canvas.width = 64;
    canvas.height = 64;

    const context = canvas.getContext("2d");
    if (context) {
      context.clearRect(0, 0, 64, 64);
      context.font = "24px Arial";
      context.textAlign = "center";
      context.fillStyle = color.getStyle();
      context.fillText(text, 32, 41);
    }

    const texture = new CanvasTexture(canvas);
    texture.colorSpace = SRGBColorSpace;

    return new SpriteMaterial({ map: texture, toneMapped: false });
  }

  render(renderer: WebGLRenderer) {
    this.quaternion.copy(this.camera.quaternion).invert();
    this.updateMatrixWorld();

    const oldAutoClear = renderer.autoClear;
    const oldClippingPlanes = renderer.clippingPlanes;
    const oldViewport = renderer.getViewport(new Vector4());

    renderer.autoClear = false;
    renderer.clippingPlanes = [];
    renderer.setViewport(this.position.x, this.position.y, this.size, this.size);
    renderer.clearDepth();
    renderer.render(this, this.orthoCamera);

    renderer.setViewport(oldViewport);
    renderer.clippingPlanes = oldClippingPlanes;
    renderer.autoClear = oldAutoClear;
  }
}
