/**
 * Legacy procedural compatibility engine — Video Mixer v4.1
 * Hidden from the v4 Stock browser; retained so v2/v3 saved sessions still play.
 * Twelve flat Canvas2D primitives intended as raw mixing material.
 * No particles, glow, noise, blur, feedback, gradients, or simulations.
 */

const SIMPLE_VISUAL_ALIASES = {
    colorwash: 'color-field', aurora: 'color-field', bokeh: 'dot-grid', lava: 'color-field',
    waves: 'line-grid', starfield: 'dot-grid', tunnel: 'frame-stack', neongrid: 'line-grid',
    rays: 'plain-rings', plasma: 'color-field', strobe: 'white-field', fireworks: 'dot-grid',
    plexus: 'line-grid', matrix: 'line-grid', vhs: 'diagonal-sweep', kaleido: 'plain-rings',
    'liquid-chrome': 'color-field', 'velvet-caustics': 'color-field',
    'crystal-tunnel': 'frame-stack', 'topographic-flow': 'line-grid',
    'binary-shift': 'checker-shift', 'kinetic-bars': 'bar-shift',
    'moire-orbit': 'plain-rings', 'polar-blades': 'plain-rings',
    'radial-crown': 'plain-rings', 'hex-reactor': 'line-grid', 'iso-stack': 'corner-blocks',
    'signal-shards': 'polygon-loop', 'infinite-frames': 'frame-stack',
    'bauhaus-sequencer': 'corner-blocks',
};

const VisualsRegistry = {
    _visuals: {},
    _order: [],

    register(definition) {
        this._visuals[definition.id] = definition;
        if (!this._order.includes(definition.id)) this._order.push(definition.id);
    },

    resolve(id) { return SIMPLE_VISUAL_ALIASES[id] || id; },

    get(id) { return this._visuals[this.resolve(id)] || null; },

    getAll() { return this._order.map(id => this._visuals[id]); },

    getByCategory() { return { Simple: this.getAll() }; },
};

const VISUAL_W = 854;
const VISUAL_H = 480;
const INK = '#020204';
const PAPER = '#f2efe6';
const ACCENT = '#5f7cff';
const ACCENT_WARM = '#ff5b48';
const FIELD_COLORS = ['#3758e8', '#d94c45', '#138b78', '#b88726', '#7556bf'];

function visualSeed(str) {
    let hash = 2166136261;
    for (let i = 0; i < str.length; i++) {
        hash ^= str.charCodeAt(i);
        hash = Math.imul(hash, 16777619);
    }
    return hash >>> 0;
}

function mulberry32(seed) {
    return function () {
        seed |= 0;
        seed = seed + 0x6D2B79F5 | 0;
        let value = Math.imul(seed ^ seed >>> 15, 1 | seed);
        value = value + Math.imul(value ^ value >>> 7, 61 | value) ^ value;
        return ((value ^ value >>> 14) >>> 0) / 4294967296;
    };
}

function beatPhase(t, bpm) {
    const tempo = bpm > 0 ? bpm : 120;
    return (t * tempo / 60) % 1;
}

function beatPulse(t, bpm) {
    return Math.pow(1 - beatPhase(t, bpm), 3);
}

function smoothstep(value) {
    const x = Math.max(0, Math.min(1, value));
    return x * x * (3 - 2 * x);
}

function mixHex(a, b, amount) {
    const parse = color => [1, 3, 5].map(index => parseInt(color.slice(index, index + 2), 16));
    const from = parse(a), to = parse(b);
    const values = from.map((value, index) => Math.round(value + (to[index] - value) * amount));
    return `rgb(${values[0]},${values[1]},${values[2]})`;
}

function polygonPath(ctx, cx, cy, radius, sides, rotation = 0) {
    ctx.beginPath();
    for (let i = 0; i < sides; i++) {
        const angle = rotation + i * Math.PI * 2 / sides;
        const x = cx + Math.cos(angle) * radius;
        const y = cy + Math.sin(angle) * radius;
        if (i === 0) ctx.moveTo(x, y);
        else ctx.lineTo(x, y);
    }
    ctx.closePath();
}

function createVisualInstance(id, w = VISUAL_W, h = VISUAL_H) {
    const canonicalId = VisualsRegistry.resolve(id);
    const definition = VisualsRegistry.get(canonicalId);
    if (!definition) return null;

    const canvas = document.createElement('canvas');
    canvas.width = w;
    canvas.height = h;
    const ctx = canvas.getContext('2d');
    const state = { rand: mulberry32(visualSeed(canonicalId)) };
    if (definition.init) definition.init(state, w, h);
    ctx.fillStyle = INK;
    ctx.fillRect(0, 0, w, h);

    return {
        id: canonicalId,
        name: definition.name,
        canvas,
        _t: 0,
        _last: null,
        tick(now, speed = 1, bpm = 0, level = 0) {
            if (this._last === null) this._last = now;
            const dt = Math.min((now - this._last) / 1000, .1) * (speed || 1);
            this._last = now;
            this._t += dt;
            definition.render(ctx, state, this._t, dt, w, h, { bpm, level });
        },
        renderAt(t) {
            definition.render(ctx, state, t, 1 / 30, w, h, { bpm: 120, level: 0 });
        },
    };
}

const simpleMeta = (id, name, tags, render, thumbTime = .8) => ({
    id, name, category: 'Simple', tags, crisp: true, thumbFrames: 0, thumbTime, render,
});

VisualsRegistry.register(simpleMeta(
    'white-field', 'White Field', ['solid', 'white', 'flat', 'luma'],
    (ctx, state, t, dt, w, h) => {
        ctx.fillStyle = PAPER;
        ctx.fillRect(0, 0, w, h);
    },
));

VisualsRegistry.register(simpleMeta(
    'color-field', 'Color Field', ['solid', 'color', 'flat', 'slow'],
    (ctx, state, t, dt, w, h, env) => {
        const cycle = t * (env.bpm > 0 ? env.bpm : 120) / 60 / 8;
        const index = Math.floor(cycle) % FIELD_COLORS.length;
        const next = (index + 1) % FIELD_COLORS.length;
        ctx.fillStyle = mixHex(FIELD_COLORS[index], FIELD_COLORS[next], smoothstep(cycle % 1));
        ctx.fillRect(0, 0, w, h);
    },
));

VisualsRegistry.register(simpleMeta(
    'hard-split', 'Hard Split', ['split', 'two-tone', 'flat', 'minimal'],
    (ctx, state, t, dt, w, h) => {
        const split = w * (.5 + Math.sin(t * .24) * .14);
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, split, h);
        ctx.fillStyle = PAPER;
        ctx.fillRect(split, 0, w - split, h);
        ctx.fillStyle = ACCENT;
        ctx.fillRect(split - Math.max(2, w * .004), 0, Math.max(4, w * .008), h);
    },
));

VisualsRegistry.register(simpleMeta(
    'bar-shift', 'Bar Shift', ['bars', 'stripes', 'monochrome', 'scroll'],
    (ctx, state, t, dt, w, h, env) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const barWidth = w / 7;
        const offset = (t * barWidth * .25) % (barWidth * 2);
        const inset = beatPulse(t, env.bpm) * barWidth * .05;
        for (let i = -2; i < 10; i += 2) {
            ctx.fillStyle = i === 4 ? ACCENT : PAPER;
            ctx.fillRect(i * barWidth + offset + inset, 0, barWidth - inset * 2, h);
        }
    },
));

VisualsRegistry.register(simpleMeta(
    'checker-shift', 'Checker Shift', ['checker', 'grid', 'monochrome', 'scroll'],
    (ctx, state, t, dt, w, h) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const cols = 8, rows = 5;
        const cw = w / cols, ch = h / rows;
        for (let row = 0; row < rows; row++) {
            const offset = (row % 2 ? 1 : -1) * ((t * cw * .2) % (cw * 2));
            for (let col = -2; col < cols + 2; col++) {
                if ((row + col) % 2 !== 0) continue;
                ctx.fillStyle = row === 2 && col === 4 ? ACCENT_WARM : PAPER;
                ctx.fillRect(col * cw + offset, row * ch, cw + 1, ch + 1);
            }
        }
    },
));

VisualsRegistry.register(simpleMeta(
    'line-grid', 'Line Grid', ['grid', 'lines', 'monochrome', 'pan'],
    (ctx, state, t, dt, w, h, env) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const dx = w / 10, dy = h / 6;
        const ox = (t * 11) % dx, oy = (t * 7) % dy;
        ctx.strokeStyle = PAPER;
        ctx.lineWidth = Math.max(1, h * (.0025 + beatPulse(t, env.bpm) * .002));
        ctx.beginPath();
        for (let x = -dx + ox; x <= w + dx; x += dx) { ctx.moveTo(x, 0); ctx.lineTo(x, h); }
        for (let y = -dy + oy; y <= h + dy; y += dy) { ctx.moveTo(0, y); ctx.lineTo(w, y); }
        ctx.stroke();
    },
));

VisualsRegistry.register(simpleMeta(
    'dot-grid', 'Dot Grid', ['dots', 'grid', 'monochrome', 'pulse'],
    (ctx, state, t, dt, w, h, env) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const cols = 12, rows = 7, cw = w / cols, ch = h / rows;
        const beat = beatPulse(t, env.bpm);
        for (let row = 0; row < rows; row++) {
            for (let col = 0; col < cols; col++) {
                const wave = .5 + .5 * Math.sin(col * .55 + row * .7 - t * 1.2);
                const radius = Math.min(cw, ch) * (.08 + wave * .22 + beat * .035);
                ctx.fillStyle = row === 3 && col === 6 ? ACCENT : PAPER;
                ctx.beginPath();
                ctx.arc((col + .5) * cw, (row + .5) * ch, radius, 0, Math.PI * 2);
                ctx.fill();
            }
        }
    },
));

VisualsRegistry.register(simpleMeta(
    'plain-rings', 'Plain Rings', ['rings', 'radial', 'lines', 'monochrome'],
    (ctx, state, t, dt, w, h, env) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const cx = w / 2, cy = h / 2;
        const spacing = h * (.075 + beatPulse(t, env.bpm) * .006);
        const travel = (t * spacing * .22) % spacing;
        ctx.strokeStyle = PAPER;
        ctx.lineWidth = Math.max(2, h * .006);
        ctx.beginPath();
        for (let i = 0; i < 12; i++) {
            const radius = travel + i * spacing;
            ctx.moveTo(cx + radius, cy);
            ctx.arc(cx, cy, radius, 0, Math.PI * 2);
        }
        ctx.stroke();
    },
));

VisualsRegistry.register(simpleMeta(
    'polygon-loop', 'Polygon Loop', ['polygon', 'outline', 'rotate', 'minimal'],
    (ctx, state, t, dt, w, h, env) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const size = h * (.22 + beatPulse(t, env.bpm) * .02);
        ctx.lineWidth = Math.max(2, h * .008);
        [[3, size * 1.55, .13, PAPER], [4, size * 1.08, -.1, ACCENT], [6, size * .62, .16, PAPER]].forEach(item => {
            polygonPath(ctx, w / 2, h / 2, item[1], item[0], t * item[2] - Math.PI / 2);
            ctx.strokeStyle = item[3];
            ctx.stroke();
        });
    },
));

VisualsRegistry.register(simpleMeta(
    'corner-blocks', 'Corner Blocks', ['blocks', 'corners', 'window', 'minimal'],
    (ctx, state, t, dt, w, h, env) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const pulse = beatPulse(t, env.bpm);
        const bw = w * (.25 + Math.sin(t * .27) * .035 + pulse * .012);
        const bh = h * (.28 + Math.cos(t * .23) * .035 + pulse * .012);
        ctx.fillStyle = PAPER;
        ctx.fillRect(0, 0, bw, bh);
        ctx.fillRect(w - bw, 0, bw, bh);
        ctx.fillRect(0, h - bh, bw, bh);
        ctx.fillStyle = ACCENT_WARM;
        ctx.fillRect(w - bw, h - bh, bw, bh);
    },
));

VisualsRegistry.register(simpleMeta(
    'frame-stack', 'Frame Stack', ['frames', 'rectangles', 'outline', 'pulse'],
    (ctx, state, t, dt, w, h, env) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const pulse = beatPulse(t, env.bpm);
        ctx.lineWidth = Math.max(2, h * .007);
        for (let i = 0; i < 5; i++) {
            const scale = .18 + i * .145 + pulse * .012 * (i + 1);
            const fw = w * scale, fh = h * scale;
            ctx.strokeStyle = i === 2 ? ACCENT : PAPER;
            ctx.strokeRect((w - fw) / 2, (h - fh) / 2, fw, fh);
        }
    },
));

VisualsRegistry.register(simpleMeta(
    'diagonal-sweep', 'Diagonal Sweep', ['diagonal', 'lines', 'scroll', 'monochrome'],
    (ctx, state, t, dt, w, h, env) => {
        ctx.fillStyle = INK;
        ctx.fillRect(0, 0, w, h);
        const spacing = h * (.095 + beatPulse(t, env.bpm) * .004);
        const offset = (t * spacing * .28) % spacing;
        ctx.strokeStyle = PAPER;
        ctx.lineWidth = Math.max(3, h * .018);
        ctx.beginPath();
        for (let p = -w - h + offset; p < w + h; p += spacing) {
            ctx.moveTo(p, h);
            ctx.lineTo(p + h, 0);
        }
        ctx.stroke();
        ctx.strokeStyle = ACCENT;
        ctx.beginPath();
        ctx.moveTo(-w * .2 + ((t * 45) % (w * 1.4)), h);
        ctx.lineTo(h - w * .2 + ((t * 45) % (w * 1.4)), 0);
        ctx.stroke();
    },
));

window.VisualsRegistry = VisualsRegistry;
window.createVisualInstance = createVisualInstance;
