export function easeLinear(t) { return t; }
export function easeInCubic(t) { return t * t * t; }
export function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); }
export function easeInOutCubic(t) {
    return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
export function easeSpring(t) {
    const c4 = (2 * Math.PI) / 3;
    return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
}
export function getEasingFn(name) {
    switch (name) {
        case 'linear': return easeLinear;
        case 'ease-in': return easeInCubic;
        case 'ease-out': return easeOutCubic;
        case 'spring': return easeSpring;
        case 'ease': return easeOutCubic;
        case 'ease-in-out':
        default: return easeInOutCubic;
    }
}
export function interpolateColor(color1, color2, progress) {
    if (color1 === 'transparent')
        color1 = '#00000000';
    if (color2 === 'transparent')
        color2 = '#00000000';
    const hex1 = color1.replace('#', '');
    const hex2 = color2.replace('#', '');
    const r1 = parseInt(hex1.substring(0, 2), 16) || 0;
    const g1 = parseInt(hex1.substring(2, 4), 16) || 0;
    const b1 = parseInt(hex1.substring(4, 6), 16) || 0;
    const a1 = hex1.length === 8 ? parseInt(hex1.substring(6, 8), 16) / 255 : 1;
    const r2 = parseInt(hex2.substring(0, 2), 16) || 0;
    const g2 = parseInt(hex2.substring(2, 4), 16) || 0;
    const b2 = parseInt(hex2.substring(4, 6), 16) || 0;
    const a2 = hex2.length === 8 ? parseInt(hex2.substring(6, 8), 16) / 255 : 1;
    const r = Math.round(r1 + (r2 - r1) * progress);
    const g = Math.round(g1 + (g2 - g1) * progress);
    const b = Math.round(b1 + (b2 - b1) * progress);
    const a = a1 + (a2 - a1) * progress;
    if (a < 1) {
        return `rgba(${r}, ${g}, ${b}, ${a.toFixed(2)})`;
    }
    return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
}
export function hashFraction(str, salt = 0) {
    let hash = salt;
    for (let i = 0; i < str.length; i++) {
        hash = ((hash << 5) - hash) + str.charCodeAt(i);
        hash |= 0;
    }
    return (Math.abs(hash) % 1000) / 1000;
}
export function getFloatAnimName(direction, seed) {
    const h = Math.floor(hashFraction(seed) * 100);
    switch (direction) {
        case 'vertical': return 'float-vertical';
        case 'horizontal': return 'float-horizontal';
        case 'both':
        default:
            return `float-both-${h % 4}`;
    }
}
// ─── Phase 2: entrance/exit/emphasis preset → keyframe name ───
export function entranceRuntimeKeyframe(mode) {
    switch (mode) {
        case 'fade': return 'runtime-entrance-fade';
        case 'slide-up': return 'runtime-entrance-slide-up';
        case 'slide-down': return 'runtime-entrance-slide-down';
        case 'slide-left': return 'runtime-entrance-slide-left';
        case 'slide-right': return 'runtime-entrance-slide-right';
        case 'pop': return 'runtime-entrance-pop';
        case 'scale-in': return 'runtime-entrance-scale-in';
        case 'rotate-in': return 'runtime-entrance-rotate-in';
        case 'blur-in': return 'runtime-entrance-blur-in';
        case 'rise': return 'runtime-entrance-rise';
        case 'flip-x': return 'runtime-entrance-flip-x';
        case 'flip-y': return 'runtime-entrance-flip-y';
        case 'bounce-in': return 'runtime-entrance-bounce-in';
        default: return null;
    }
}
export function exitRuntimeKeyframe(mode) {
    switch (mode) {
        case 'fade': return 'runtime-exit-fade';
        case 'slide-up': return 'runtime-exit-slide-up';
        case 'slide-down': return 'runtime-exit-slide-down';
        case 'slide-left': return 'runtime-exit-slide-left';
        case 'slide-right': return 'runtime-exit-slide-right';
        case 'pop': return 'runtime-exit-pop';
        case 'scale-out': return 'runtime-exit-scale-out';
        case 'rotate-out': return 'runtime-exit-rotate-out';
        case 'blur-out': return 'runtime-exit-blur-out';
        case 'sink': return 'runtime-exit-sink';
        case 'flip-x': return 'runtime-exit-flip-x';
        case 'flip-y': return 'runtime-exit-flip-y';
        default: return null;
    }
}
export function emphasisKeyframeName(mode) {
    switch (mode) {
        case 'pulse': return 'emphasis-pulse';
        case 'shake': return 'emphasis-shake';
        case 'flash': return 'emphasis-flash';
        case 'wobble': return 'emphasis-wobble';
        case 'glow-flash': return 'emphasis-glow-flash';
        case 'bob-once': return 'emphasis-bob-once';
        case 'tada': return 'emphasis-tada';
        default: return null;
    }
}
// Map an idle preset to a keyframe name. Falls back to direction-based picker
// when no preset is set, preserving behavior for pre-preset projects.
export function getIdleAnimName(cfg, seed) {
    switch (cfg?.preset) {
        case 'breathe': return 'idle-breathe';
        case 'pulse': return 'idle-pulse';
        case 'wiggle': return 'idle-wiggle';
        case 'sway': return 'idle-sway';
        case 'tilt': return 'idle-tilt';
        case 'bob': return 'idle-bob';
        case 'drift': return 'idle-drift';
        case 'float':
        default:
            return getFloatAnimName(cfg?.direction ?? 'vertical', seed);
    }
}
export function computeFloatAmp(cfg, seed) {
    const r = cfg.amplitudeRandomness ?? 0;
    if (r === 0)
        return cfg.amplitude;
    return cfg.amplitude * (1 - r + r * hashFraction(seed, 1));
}
export function computeFloatSpeed(cfg, seed) {
    const r = cfg.speedRandomness ?? 0;
    if (r === 0)
        return cfg.speed;
    return cfg.speed * (1 - r + r * hashFraction(seed, 2));
}
function buildStops(colors, stops) {
    if (stops && stops.length > 0) {
        const sorted = [...stops].sort((a, b) => a.position - b.position);
        return sorted.map((s) => `${s.color} ${s.position}%`).join(', ');
    }
    if (colors.length <= 1)
        return colors[0] ?? '#000';
    return colors.map((c, i) => `${c} ${(i / (colors.length - 1)) * 100}%`).join(', ');
}
export function getBackgroundStyle(bg) {
    if (bg.type === 'transparent')
        return 'background: transparent';
    if (bg.type === 'solid')
        return `background-color: ${bg.color ?? 'transparent'}`;
    if (bg.type === 'gradient' && bg.gradient) {
        const css = gradientShapeToCss(bg.gradient);
        return `background: ${css}`;
    }
    if (bg.type === 'image' && bg.image)
        return `background-image: url(${bg.image}); background-size: cover; background-position: center`;
    return 'background: transparent';
}
/** Build a CSS gradient string from a standalone gradient shape (no canvas
 *  background wrapper). Reusable by elements that embed a gradient
 *  directly — text fills, progress bars, etc. */
export function gradientShapeToCss(g) {
    const { type, angle = 135, colors, stops, radialShape = 'circle', radialPosition = 'center' } = g;
    const stopList = buildStops(colors, stops);
    if (type === 'conic')
        return `conic-gradient(from ${angle}deg at ${radialPosition}, ${stopList})`;
    if (type === 'radial')
        return `radial-gradient(${radialShape} at ${radialPosition}, ${stopList})`;
    return `linear-gradient(${angle}deg, ${stopList})`;
}
