// Controls generated from a scene's param schema.
//
// Nothing here knows about any specific scene. That is the point: a new scene
// gets a full editing UI for free, which is what makes "auto with override"
// affordable across a library of thirty. Adding a hand-written control for a
// scene would be a bug, not a feature.

const FORMATTERS = {
    float: (v) => (typeof v === 'number' ? v.toFixed(2) : '—'),
    int: (v) => String(v),
    bool: (v) => (v ? 'on' : 'off'),
    vec2: (v) => (Array.isArray(v) ? `${v[0].toFixed(2)}, ${v[1].toFixed(2)}` : '—'),
};

export class ParamPanel {
    /**
     * @param {HTMLElement} container
     * @param {(name: string, value: any) => void} onChange
     */
    constructor(container, onChange) {
        this.container = container;
        this.onChange = onChange;
        this.controls = new Map();
        this.module = null;
    }

    /** Rebuild for a scene module and its current values. */
    build(module, values) {
        this.container.innerHTML = '';
        this.controls.clear();
        this.module = module;
        if (!module) return;

        const heading = document.createElement('div');
        heading.className = 'pp-heading';
        heading.innerHTML = `<span class="pp-name">${module.name}</span>
                             <span class="pp-family">${module.family}</span>`;
        this.container.appendChild(heading);

        for (const [name, def] of Object.entries(module.params || {})) {
            if (def.type === 'palette') continue;   // edited in the look tab
            const control = this._buildControl(name, def, values[name]);
            if (control) {
                this.container.appendChild(control.element);
                this.controls.set(name, control);
            }
        }

        const reactive = module.reactive || {};
        if (Object.keys(reactive).length) {
            const note = document.createElement('div');
            note.className = 'pp-reactive';
            note.innerHTML = '<div class="pp-sub">audio-driven</div>' +
                Object.entries(reactive)
                    .map(([p, r]) => `<div class="pp-react-row"><span>${p}</span>` +
                        `<span class="pp-feature">${r.feature}</span>` +
                        `<span class="pp-amount">${r.amount > 0 ? '+' : ''}${r.amount}</span></div>`)
                    .join('');
            this.container.appendChild(note);
        }
    }

    _buildControl(name, def, value) {
        const element = document.createElement('div');
        element.className = 'pp-row';

        const label = document.createElement('label');
        label.className = 'pp-label';
        label.textContent = name;

        const readout = document.createElement('span');
        readout.className = 'pp-value';

        let input;
        if (def.type === 'bool') {
            input = document.createElement('input');
            input.type = 'checkbox';
            input.checked = !!value;
            input.addEventListener('change', () => {
                readout.textContent = FORMATTERS.bool(input.checked);
                this.onChange(name, input.checked);
            });
        } else {
            const [lo, hi] = def.range || [0, 1];
            input = document.createElement('input');
            input.type = 'range';
            input.min = String(lo);
            input.max = String(hi);
            input.step = def.type === 'int' ? '1' : String((hi - lo) / 200);
            input.value = String(def.type === 'vec2' ? (value ? value[0] : lo) : value ?? lo);
            input.addEventListener('input', () => {
                const v = def.type === 'int' ? Math.round(+input.value) : +input.value;
                const out = def.type === 'vec2' ? [v, v] : v;
                readout.textContent = (FORMATTERS[def.type] || FORMATTERS.float)(out);
                this.onChange(name, out);
            });
        }
        input.className = 'pp-input';

        readout.textContent = (FORMATTERS[def.type] || FORMATTERS.float)(value);

        element.append(label, input, readout);
        return { element, input, readout, def };
    }

    /** Push new values into the controls without emitting change events. */
    update(values) {
        for (const [name, control] of this.controls) {
            const v = values[name];
            if (v === undefined) continue;
            if (control.def.type === 'bool') control.input.checked = !!v;
            else control.input.value = String(control.def.type === 'vec2' ? v[0] : v);
            control.readout.textContent =
                (FORMATTERS[control.def.type] || FORMATTERS.float)(v);
        }
    }

    /** Names of every control currently rendered. Used by the Phase 2 gate. */
    controlNames() {
        return [...this.controls.keys()];
    }
}
