export interface Cubism2MathStatic {
  DEGREES_TO_RADIANS: number
  RADIANS_TO_DEGREES: number
  PI: number
  SIN_LOOKUP_TABLE: number[]
  angleBetweenVectors: (fromVector: readonly number[], toVector: readonly number[]) => number
  cos: (radian: number) => number
  normalizeRadianDelta: (fromRadian: number, toRadian: number) => number
  randomUnit: () => number
  sin: (radian: number) => number
}

type Cubism2MathConstructor = {
  new (): Record<string, never>
} & Cubism2MathStatic

export interface CreateCubism2MathOptions {
  random?: () => number
}

const SIN_LOOKUP_TABLE = [
  0, 0.012368, 0.024734, 0.037097, 0.049454, 0.061803, 0.074143, 0.086471, 0.098786, 0.111087,
  0.12337, 0.135634, 0.147877, 0.160098, 0.172295, 0.184465, 0.196606, 0.208718, 0.220798,
  0.232844, 0.244854, 0.256827, 0.268761, 0.280654, 0.292503, 0.304308, 0.316066, 0.327776,
  0.339436, 0.351044, 0.362598, 0.374097, 0.385538, 0.396921, 0.408243, 0.419502, 0.430697,
  0.441826, 0.452888, 0.463881, 0.474802, 0.485651, 0.496425, 0.507124, 0.517745, 0.528287,
  0.538748, 0.549126, 0.559421, 0.56963, 0.579752, 0.589785, 0.599728, 0.609579, 0.619337, 0.629,
  0.638567, 0.648036, 0.657406, 0.666676, 0.675843, 0.684908, 0.693867, 0.70272, 0.711466,
  0.720103, 0.72863, 0.737045, 0.745348, 0.753536, 0.76161, 0.769566, 0.777405, 0.785125,
  0.792725, 0.800204, 0.807561, 0.814793, 0.821901, 0.828884, 0.835739, 0.842467, 0.849066,
  0.855535, 0.861873, 0.868079, 0.874153, 0.880093, 0.885898, 0.891567, 0.897101, 0.902497,
  0.907754, 0.912873, 0.917853, 0.922692, 0.92739, 0.931946, 0.936359, 0.940629, 0.944755,
  0.948737, 0.952574, 0.956265, 0.959809, 0.963207, 0.966457, 0.96956, 0.972514, 0.97532,
  0.977976, 0.980482, 0.982839, 0.985045, 0.987101, 0.989006, 0.990759, 0.992361, 0.993811,
  0.995109, 0.996254, 0.997248, 0.998088, 0.998776, 0.999312, 0.999694, 0.999924, 1,
]

/**
 * Creates the low-coupling Cubism2 math helper namespace used by matrices, easing, and physics.
 * @param options Optional host-provided random source used by legacy blink scheduling tests.
 * @returns Function-object namespace exposing only semantic math operations.
 */
export function createCubism2Math(options: CreateCubism2MathOptions = {}): Cubism2MathConstructor {
  const readRandomUnit = options.random ?? Math.random

  /**
   * Cubism2 math namespace constructor retained so prototype bootstrapping still has a target.
   */
  const Cubism2Math = function Cubism2Math(): void {} as unknown as Cubism2MathConstructor

  Cubism2Math.DEGREES_TO_RADIANS = Math.PI / 180
  Cubism2Math.RADIANS_TO_DEGREES = 180 / Math.PI
  Cubism2Math.PI = Math.PI
  Cubism2Math.SIN_LOOKUP_TABLE = SIN_LOOKUP_TABLE

  /**
   * Normalizes the angular delta into the `[-PI, PI]` range used by Cubism2 physics.
   * @param fromRadian First angle in radians.
   * @param toRadian Second angle in radians subtracted from the first angle.
   * @returns Wrapped angular difference in radians.
   */
  Cubism2Math.normalizeRadianDelta = function normalizeRadianDelta(
    fromRadian: number,
    toRadian: number,
  ): number {
    let radianDelta = fromRadian - toRadian
    while (radianDelta < -Math.PI) {
      radianDelta += 2 * Math.PI
    }
    while (radianDelta > Math.PI) {
      radianDelta -= 2 * Math.PI
    }
    return radianDelta
  }

  /**
   * Computes the signed angle from one 2D vector to another using legacy Cubism2 ordering.
   * @param fromVector Source vector as `[x, y]`.
   * @param toVector Target vector as `[x, y]`.
   * @returns Signed angular difference in radians.
   */
  Cubism2Math.angleBetweenVectors = function angleBetweenVectors(
    fromVector: readonly number[],
    toVector: readonly number[],
  ): number {
    const fromRadian = Math.atan2(fromVector[1]!, fromVector[0]!)
    const toRadian = Math.atan2(toVector[1]!, toVector[0]!)
    return Cubism2Math.normalizeRadianDelta(fromRadian, toRadian)
  }

  /**
   * Computes sine through the native Math implementation used by the original min.js path.
   * @param radian Angle in radians.
   * @returns Sine of the supplied angle.
   */
  Cubism2Math.sin = function sin(radian: number): number {
    return Math.sin(radian)
  }

  /**
   * Computes cosine through the native Math implementation used by the original min.js path.
   * @param radian Angle in radians.
   * @returns Cosine of the supplied angle.
   */
  Cubism2Math.cos = function cos(radian: number): number {
    return Math.cos(radian)
  }

  /**
   * Supplies the intentional random correction absent from the immutable Cubism2 source.
   * @returns Random number in the same `[0, 1)` domain as `Math.random`.
   */
  Cubism2Math.randomUnit = function randomUnit(): number {
    return readRandomUnit()
  }

  return Cubism2Math
}
