// Lights. A directional sun (the engine's primary light) and punctual point lights. Add one to a
// scene like any node; `intensity` and `color` stay writable so a light can be animated.

import { Color, type ColorInput } from "../core/color"
import type { Vec3Like } from "../math/vec"
import { Node } from "./Node"

export type SunOptions = {
  /** Light direction (points where the light travels). Defaults to a typical key-light angle. */
  direction?: Vec3Like
  /** Luminous intensity. */
  intensity?: number
  color?: ColorInput
  /**
   * Shadow quality (default 1):
   *   0 - no shadows
   *   1 - 1024 map, hard edges (one filtered tap; the cheap level, ~3 % of a frame on an iGPU)
   *   2 - 1024 map, soft edges (variance shadows + blur; ~+15 % of a frame on an iGPU)
   *   3 - 2048 map, contact-hardening soft edges (PCSS: sharp where the caster touches, softer
   *       away; discrete-GPU territory, ~+60 % of a frame on an iGPU)
   * A lightmapped level already carries every static shadow, so 0 is a legitimate choice there.
   */
  shadowsQuality?: 0 | 1 | 2 | 3
  /**
   * Metres from the camera within which the sun casts shadows (default 100). Shadows fade out by
   * this distance and the shadow map covers only this range, so a shorter distance is crisper for
   * the same quality and puts fewer casters in the shadow pass. 30-40 is plenty in first person.
   */
  shadowDistance?: number
}

export type PointOptions = {
  /**
   * Luminous POWER in lumens — the same physical scale as the sun's lux and `environmentIntensity`,
   * so a light keeps its look when the scene's exposure changes. A candle is ~12 lm, a bare 60 W
   * bulb ~800, a car headlight ~1500, a fireball millions. Default 1000.
   */
  intensity?: number
  color?: ColorInput
  /**
   * Metres of influence — past it the light contributes nothing. This is the performance knob:
   * overlapping point lights are the expensive case, so keep it as small as the look allows.
   * Default 10.
   */
  range?: number
  /** Point-light shadows are a cubemap render per light; off by default. */
  castShadows?: boolean
}

export class Light extends Node {
  private _intensity = 0
  /** @internal sun direction as created (the engine keeps it; Lightmap.load reads it back). */
  _direction: [number, number, number] = [ 0, -1, 0 ]
  /** The most recently created sun — what `Lightmap.load` uses unless told otherwise. */
  static lastSun: Light | null = null

  /** A directional sun light. */
  static sun(options: SunOptions = {}): Light {
    const light = new Light()
    const [ dx, dy, dz ] = options.direction ?? [ 0.548267, -0.473983, -0.689016 ]
    light._direction = [ dx, dy, dz ]
    light._intensity = options.intensity ?? 100000
    Light.lastSun = light
    _creator.createSunLight(
      light.id, dx, dy, dz,
      light._intensity,
      Color.toPackedRgb(options.color ?? 0xffffff),
      options.shadowsQuality ?? 1,
      options.shadowDistance ?? 100,
    )
    return light
  }

  /**
   * A point light — a lamp, a muzzle flash, a fireball. Position it like any node.
   *
   * Feature-detected: hosts that predate it create the node and light nothing, which keeps a scene
   * that adds atmosphere on top of its sun renderable everywhere. Check `Light.supportsPoint`
   * before making one carry the scene.
   */
  static point(options: PointOptions = {}): Light {
    const light = new Light()
    light._intensity = options.intensity ?? 1000
    _creator.createPointLight?.(
      light.id,
      light._intensity,
      Color.toPackedRgb(options.color ?? 0xffffff),
      options.range ?? 10,
      options.castShadows ?? false,
    )
    return light
  }

  /** Whether this host can create point lights at all. */
  static get supportsPoint(): boolean { return _creator.createPointLight !== undefined }

  /** Live intensity (sun: lux, point: lumens) — animate a flash without rebuilding the light. */
  get intensity(): number { return this._intensity }
  set intensity(value: number) {
    this._intensity = value
    _creator.setLightIntensity?.(this.id, value)
  }

  set color(value: ColorInput) {
    _creator.setLightColor?.(this.id, Color.toPackedRgb(value))
  }
}
