/**
 * Adapted from the 3D geometry datastuctured from demo-33 (depth mesh).
 *
 * Refactored the strucure into a class.
 *
 * @author   Ikaros Kappler
 * @date     2025-11-14
 * @modified 2025-11-17 Moved `getBounds` and `normalizeGeometry` from helper functions to this class.
 * @version  1.0.0
 */

export type Vertex2 = { x: number; y: number };
export type Vertex3 = { x: number; y: number; z: number };

export class GeometryMesh<T extends Vertex2 | Vertex3> {
  /**
   * A set (array) of vertices (points) used in this mesh. Can be 2d or 3d.
   *
   * @member {Array<T>}
   * @memberof GeometryMesh
   * @type {Array<T>}
   * @instance
   */
  vertices: Array<T>;

  /**
   * A set (array) of edges used in this mesh.
   * Each edge consist of two indices indicating the vertex position.
   *
   * @member {Array<T>}
   * @memberof GeometryMesh
   * @type {Array<T>}
   * @instance
   */
  edges: Array<[number, number]>;

  constructor(vertices?: Array<T>, edges?: Array<[number, number]>) {
    this.vertices = vertices || [];
    this.edges = edges || [];
  }

  normalize() {
    // Desired bounds is a max bounding box. The object will be scaled keeping aspect ratio
    // in that way that the largest axis size touches the desired bounds.
    var desiredBounds = { min: { x: -1, y: -1, z: -1 }, max: { x: 1, y: 1, z: 1 } };
    var bounds = this.getGeometryBounds();
    var sizeX = bounds.max.x - bounds.min.x;
    var sizeY = bounds.max.y - bounds.min.y;
    var sizeZ = bounds.max.z - bounds.min.z;
    // Scale uniform so the dimension with the max expansion fits into [-1,1]
    var maxDimension = Math.max(sizeX, sizeY, sizeZ);
    var desiredSizeX = (desiredBounds.max.x - desiredBounds.min.x) / maxDimension;
    var desiredSizeY = (desiredBounds.max.y - desiredBounds.min.y) / maxDimension;
    var desiredSizeZ = (desiredBounds.max.z - desiredBounds.min.z) / maxDimension;
    for (var i in this.vertices) {
      var vert = this.vertices[i] as Vertex3; // TODO: Check if this is safe
      vert.x = desiredBounds.min.x + (bounds.max.x - vert.x) * desiredSizeX;
      vert.y = desiredBounds.min.y + (bounds.max.y - vert.y) * desiredSizeY;
      vert.z = desiredBounds.min.z + (bounds.max.z - vert.z) * desiredSizeZ;
    }
  }

  getGeometryBounds(): { min: Vertex3; max: Vertex3; width: number; height: number; depth: number } {
    // var min = new Vert3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
    // var max = new Vert3(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
    var min = { x: Number.MAX_VALUE, y: Number.MAX_VALUE, z: Number.MAX_VALUE };
    var max = { x: Number.MIN_VALUE, y: Number.MIN_VALUE, z: Number.MIN_VALUE };
    for (var i in this.vertices) {
      var vert = this.vertices[i] as Vertex3;
      min.x = Math.min(vert.x, min.x);
      min.y = Math.min(vert.y, min.y);
      min.z = Math.min(vert.z, min.z);
      max.x = Math.max(vert.x, max.x);
      max.y = Math.max(vert.y, max.y);
      max.z = Math.max(vert.z, max.z);
    }
    return { min: min, max: max, width: max.x - min.x, height: max.y - min.y, depth: max.z - min.z };
  }
}
