// Preset serialisation. A preset is just a param set plus the scene it belongs
// to, so "save the look I tuned" and "the look generator's output" are the same
// kind of object and can be diffed, stored and round-tripped identically.

import { clampValue, defaultValues } from './schema.js';

export const PRESET_VERSION = 1;

export function serializeParams(module, values) {
    const out = {};
    for (const [name, def] of Object.entries(module.params || {})) {
        if (def.type === 'palette') continue;   // palettes live on the look, not the scene
        const v = values[name];
        if (v === undefined) continue;
        out[name] = def.type === 'vec2' ? [v[0], v[1]] : v;
    }
    return out;
}

/**
 * Parse a stored param set back onto a module. Unknown keys are dropped and
 * missing ones fall back to defaults, so a preset saved before a scene gained or
 * lost a param still loads instead of throwing.
 */
export function deserializeParams(module, stored) {
    const values = defaultValues(module);
    if (!stored) return values;
    for (const [name, def] of Object.entries(module.params || {})) {
        if (def.type === 'palette') continue;
        if (stored[name] === undefined) continue;
        values[name] = clampValue(def, stored[name]);
    }
    return values;
}

function serializeLayer(layer) {
    return {
        scene: layer.module.name,
        blend: layer.blend,
        opacity: layer.opacity,
        seed: layer.seed,
        params: layer.params,
    };
}

export function serializeLook(look) {
    return {
        version: PRESET_VERSION,
        seed: look.seed,
        palette: look.palette,
        // The production design decides which scenes were even eligible, so a
        // preset without it cannot be reproduced.
        personality: look.personality,
        post: look.post,
        feedback: look.feedback,
        sections: look.sections.map((s) => ({
            index: s.index,
            kind: s.kind,
            locked: !!s.locked,
            // `layers` is the anchor stack and stays first for compatibility;
            // `variants` is the full roster the section's shots cut between.
            layers: s.layers.map(serializeLayer),
            variants: (s.variants || [s.layers]).map((stack) => stack.map(serializeLayer)),
            shots: (s.shots || []).map((shot) => ({
                startFrame: shot.startFrame,
                endFrame: shot.endFrame,
                variant: shot.variant,
                hardCut: !!shot.hardCut,
            })),
        })),
    };
}
