// CameraPlace: "the camera as a place". A scene has ONE camera (`scene.camera`); a CameraPlace marks
// a node as somewhere that camera can be — with the lens it uses there. Nothing is created engine-
// side (no second camera, no ABI): it is a pose + a projection record on any node: an empty, an
// empty mounted on a bone (`mount:`), a node a path aspect moves (a dolly shot), a model node.
//
//   • In a scene FILE that runs (`open()` / `load()`), the `active: true` place drives
//     `scene.camera` (exactly `scene.camera.follow(node)`); exactly one per file.
//   • Inside a prefab or an `instantiate()`d subtree a place is inert DATA — the hosting scene
//     owns its camera. The host reads it (`inst.nodes.eye.get(CameraPlace)` → world pose + fov)
//     or anchors on it (`inst.alignTo(inst.nodes.eye)`).
//   • From code: `scene.camera.follow(dolly.get(CameraPlace))` — a cutscene shot; `follow(null)`
//     releases.
//   • Edit mode: draws an anchored frustum gizmo (the editor's camera preview reads the same place).
// See docs/scene-camera-place-plan.md.

import { Aspect } from "../core/Aspect"
import type { FieldMeta } from "../core/fields"
import { Gizmos } from "../scene/gizmos"
import type { Camera } from "./Camera"
import type { Node } from "./Node"

export class CameraPlace extends Aspect<"cameraPlace", Node> {
  static readonly aspect = "cameraPlace"
  /** Vertical field of view in degrees (default 60) — smaller is a longer lens. */
  fov = 60
  /** Near clip distance. */
  near = 0.01
  /** Far clip distance = view range. */
  far = 1000
  /** The place that drives `scene.camera` when the file declaring it RUNS. Exactly one per file;
   *  ignored inside prefabs / instantiated subtrees (the host scene owns its camera). */
  active = false
  /** Aspect ratio of the frustum gizmo only (the real aspect is the viewport's). */
  static fields: FieldMeta<CameraPlace> = {
    fov: { label: "FOV", min: 1, max: 179, step: 1 },
    near: { min: 0.001, max: 10, step: 0.01 },
    far: { min: 1, max: 100000, step: 1 },
    active: { label: "Active camera" },
  }
  static editor = { rebuild: true }

  /** Copy this place's projection onto a camera (`camera.follow(place)` does it for you). */
  applyTo(camera: Camera): void {
    camera.setProjection({ fov: this.fov, near: this.near, far: this.far })
  }

  /** Edit mode: the frustum marker (−Z = view), anchored on the node so it follows drags live and
   *  clicking it selects the node. Play mode never calls this. */
  rebuild(): void {
    Gizmos.frustum(this.fov, { node: this.node, color: this.active ? "#ffffff" : "#9aa3b2" })
  }
}
