/**
 * Cinema-mode camera math.
 *
 * Concept: in cinema mode every slide shares one large "world canvas" (defined
 * by Project.settings.worldWidth/Height). Each slide has a `camera` viewport —
 * a rect in world coordinates that we want to fill the visible canvas. The
 * transition between two slides = animating the camera (pan + zoom + roll).
 *
 * To render: we wrap all elements in a transform that maps the camera rect to
 * the visible canvas dimensions. Element positions stay in world coordinates;
 * only the wrapper transforms.
 *
 * Parallax: each element can declare a `depth` in [-1, +1]. Elements behind
 * the camera (negative depth) appear to move less; elements in front move
 * more. Implemented as an additional translate proportional to camera offset.
 */
/** Build the CSS transform that maps a camera rect over the world to fill the
 * given output viewport. Returned as a single transform string ready for
 * `style:transform="..."`. */
export function cameraTransform({ camera, viewportWidth, viewportHeight }) {
    const sx = viewportWidth / Math.max(1, camera.width);
    const sy = viewportHeight / Math.max(1, camera.height);
    // Uniform scale so circles stay circles. We pick the smaller axis so the
    // camera rect fully fits the viewport (letterboxes the rest); use Math.max
    // instead if "cover" semantics are wanted.
    const s = Math.min(sx, sy);
    const rotation = camera.rotation ?? 0;
    // Order: translate world → camera origin, then scale, then rotate. Reads
    // right-to-left in CSS so we write it scale → translate.
    return `scale(${s}) rotate(${-rotation}deg) translate(${-camera.x}px, ${-camera.y}px)`;
}
/** Lerp between two camera viewports for transitions. */
export function lerpCamera(a, b, t) {
    const lerp = (x, y) => x + (y - x) * t;
    return {
        x: lerp(a.x, b.x),
        y: lerp(a.y, b.y),
        width: lerp(a.width, b.width),
        height: lerp(a.height, b.height),
        rotation: lerp(a.rotation ?? 0, b.rotation ?? 0)
    };
}
/** Default camera = full world (no zoom, no pan). Used when a slide is in
 * cinema mode but its camera is unset. */
export function defaultCamera(worldWidth, worldHeight) {
    return { x: 0, y: 0, width: worldWidth, height: worldHeight, rotation: 0 };
}
/** Per-element parallax offset. Elements with depth = 0 move 1:1 with the
 * camera; depth > 0 moves more (closer); depth < 0 moves less (farther). The
 * factor maps depth → multiplier such that depth=1 doubles motion, depth=-1
 * halves it. We compute the offset from the *delta between the camera's
 * current position and a neutral world center* — gives a plausible parallax
 * without needing a separate reference camera per element.
 */
export function parallaxOffset(camera, depth, worldWidth, worldHeight) {
    if (!depth)
        return { x: 0, y: 0 };
    const cx = camera.x + camera.width / 2;
    const cy = camera.y + camera.height / 2;
    const wx = worldWidth / 2;
    const wy = worldHeight / 2;
    // Parallax magnitude is `depth × camera-offset-from-world-center × factor`.
    // The earlier 0.4 factor blew up on large worlds (6000×4000) because the
    // camera-offset term scales with world size — depth=0.7 gave ±840px shifts,
    // destroying composition.
    //
    // Fix: scale the factor by the CAMERA WIDTH ratio to a 1920-baseline so the
    // effect feels the same regardless of world size. A 1920-wide camera gets
    // the original feel; a 3200-wide camera (zoomed out) gets a smaller offset
    // per unit-of-world-distance so the layout doesn't fly apart.
    const baseFactor = depth * 0.15;
    const widthScale = Math.min(1, 1920 / Math.max(1, camera.width));
    const factor = baseFactor * widthScale;
    return { x: -(cx - wx) * factor, y: -(cy - wy) * factor };
}
