'use strict';

var jsxRuntime = require('react/jsx-runtime');
var React = require('react');

function luminance(r, g, b) {
    return 0.299 * r + 0.587 * g + 0.114 * b;
}
/**
 * Auto-colorize a grayscale image using heuristic color mapping.
 * Maps luminance regions to plausible colors (sky-blue for bright areas,
 * earth tones for dark areas, greens for mid-tones).
 */
function autoColorize(source, options) {
    const { intensity, temperature, saturation } = options;
    const result = new ImageData(new Uint8ClampedArray(source.data), source.width, source.height);
    const data = result.data;
    // Temperature bias
    const tempBias = temperature === 'warm' ? [20, 5, -15] : temperature === 'cool' ? [-10, 0, 15] : [0, 0, 0];
    for (let i = 0; i < data.length; i += 4) {
        const lum = luminance(data[i], data[i + 1], data[i + 2]);
        const isGray = Math.abs(data[i] - data[i + 1]) < 15 && Math.abs(data[i + 1] - data[i + 2]) < 15;
        if (isGray) {
            // Map grayscale to heuristic colors based on luminance
            let cr, cg, cb;
            if (lum > 200) {
                // Very bright → sky blue / white
                cr = 200 + (lum - 200) * 0.8;
                cg = 210 + (lum - 200) * 0.6;
                cb = 230;
            }
            else if (lum > 140) {
                // Upper mid → warm tones
                cr = lum * 1.05;
                cg = lum * 0.95;
                cb = lum * 0.85;
            }
            else if (lum > 80) {
                // Mid tones → greenish
                cr = lum * 0.85;
                cg = lum * 1.1;
                cb = lum * 0.8;
            }
            else {
                // Dark → earth/brown
                cr = lum * 1.15;
                cg = lum * 0.9;
                cb = lum * 0.7;
            }
            // Apply temperature bias
            cr = Math.max(0, Math.min(255, cr + tempBias[0]));
            cg = Math.max(0, Math.min(255, cg + tempBias[1]));
            cb = Math.max(0, Math.min(255, cb + tempBias[2]));
            // Apply saturation
            const avg = (cr + cg + cb) / 3;
            cr = avg + (cr - avg) * saturation;
            cg = avg + (cg - avg) * saturation;
            cb = avg + (cb - avg) * saturation;
            // Blend with original based on intensity
            data[i] = Math.max(0, Math.min(255, data[i] * (1 - intensity) + cr * intensity));
            data[i + 1] = Math.max(0, Math.min(255, data[i + 1] * (1 - intensity) + cg * intensity));
            data[i + 2] = Math.max(0, Math.min(255, data[i + 2] * (1 - intensity) + cb * intensity));
        }
    }
    return result;
}
/**
 * Remove background using edge-based foreground detection.
 * Uses Sobel edge detection + flood fill from edges to find background.
 */
function removeBackground(source, options) {
    const { threshold, iterations, featherRadius, replacementColor } = options;
    const w = source.width;
    const h = source.height;
    const result = new ImageData(new Uint8ClampedArray(source.data), w, h);
    const data = result.data;
    // 1. Compute edge magnitude (Sobel)
    const edges = new Float32Array(w * h);
    for (let y = 1; y < h - 1; y++) {
        for (let x = 1; x < w - 1; x++) {
            const idx = (y * w + x) * 4;
            const tl = luminance(source.data[idx - w * 4 - 4], source.data[idx - w * 4 - 3], source.data[idx - w * 4 - 2]);
            const tc = luminance(source.data[idx - w * 4], source.data[idx - w * 4 + 1], source.data[idx - w * 4 + 2]);
            const tr = luminance(source.data[idx - w * 4 + 4], source.data[idx - w * 4 + 5], source.data[idx - w * 4 + 6]);
            const ml = luminance(source.data[idx - 4], source.data[idx - 3], source.data[idx - 2]);
            const mr = luminance(source.data[idx + 4], source.data[idx + 5], source.data[idx + 6]);
            const bl = luminance(source.data[idx + w * 4 - 4], source.data[idx + w * 4 - 3], source.data[idx + w * 4 - 2]);
            const bc = luminance(source.data[idx + w * 4], source.data[idx + w * 4 + 1], source.data[idx + w * 4 + 2]);
            const br = luminance(source.data[idx + w * 4 + 4], source.data[idx + w * 4 + 5], source.data[idx + w * 4 + 6]);
            const gx = -tl - 2 * ml - bl + tr + 2 * mr + br;
            const gy = -tl - 2 * tc - tr + bl + 2 * bc + br;
            edges[y * w + x] = Math.sqrt(gx * gx + gy * gy) / 255;
        }
    }
    // 2. Create foreground mask from edge density
    const mask = new Float32Array(w * h);
    const edgeThreshold = threshold * 0.5;
    // Mark edges as definite foreground
    for (let i = 0; i < edges.length; i++) {
        mask[i] = edges[i] > edgeThreshold ? 1 : 0;
    }
    // 3. Dilate foreground mask (iterations)
    for (let iter = 0; iter < iterations; iter++) {
        const temp = new Float32Array(mask);
        for (let y = 1; y < h - 1; y++) {
            for (let x = 1; x < w - 1; x++) {
                const idx = y * w + x;
                if (temp[idx] > 0)
                    continue;
                // Check 4-neighbors
                if (temp[idx - 1] > 0 || temp[idx + 1] > 0 || temp[idx - w] > 0 || temp[idx + w] > 0) {
                    // Check color similarity with neighbor
                    const pi = idx * 4;
                    let isFg = false;
                    for (const ni of [idx - 1, idx + 1, idx - w, idx + w]) {
                        if (temp[ni] > 0) {
                            const npi = ni * 4;
                            const diff = (Math.abs(source.data[pi] - source.data[npi]) +
                                Math.abs(source.data[pi + 1] - source.data[npi + 1]) +
                                Math.abs(source.data[pi + 2] - source.data[npi + 2])) / 3;
                            if (diff < threshold * 255) {
                                isFg = true;
                                break;
                            }
                        }
                    }
                    if (isFg)
                        mask[idx] = 1;
                }
            }
        }
    }
    // 4. Flood fill from border pixels as background (anything not foreground and reachable from edge)
    const visited = new Uint8Array(w * h);
    const queue = [];
    // Seed from border
    for (let x = 0; x < w; x++) {
        if (mask[x] === 0) {
            queue.push(x);
            visited[x] = 1;
        }
        const bi = (h - 1) * w + x;
        if (mask[bi] === 0) {
            queue.push(bi);
            visited[bi] = 1;
        }
    }
    for (let y = 0; y < h; y++) {
        const li = y * w;
        if (mask[li] === 0) {
            queue.push(li);
            visited[li] = 1;
        }
        const ri = y * w + w - 1;
        if (mask[ri] === 0) {
            queue.push(ri);
            visited[ri] = 1;
        }
    }
    // BFS flood fill
    let head = 0;
    while (head < queue.length) {
        const idx = queue[head++];
        const x = idx % w;
        const y = (idx - x) / w;
        for (const [dx, dy] of [[0, -1], [0, 1], [-1, 0], [1, 0]]) {
            const nx = x + dx;
            const ny = y + dy;
            if (nx < 0 || nx >= w || ny < 0 || ny >= h)
                continue;
            const ni = ny * w + nx;
            if (visited[ni] || mask[ni] > 0)
                continue;
            visited[ni] = 1;
            queue.push(ni);
        }
    }
    // 5. Apply mask with optional feathering
    const replacement = replacementColor ? hexToRGB(replacementColor) : null;
    for (let i = 0; i < w * h; i++) {
        let alpha = visited[i] ? 0 : 1; // background = 0, foreground = 1
        // Feather edges
        if (featherRadius > 0 && alpha > 0) {
            const x = i % w;
            const y = (i - x) / w;
            let minDistSq = featherRadius * featherRadius + 1;
            for (let fy = -featherRadius; fy <= featherRadius; fy++) {
                for (let fx = -featherRadius; fx <= featherRadius; fx++) {
                    const nx = x + fx;
                    const ny = y + fy;
                    if (nx >= 0 && nx < w && ny >= 0 && ny < h) {
                        if (visited[ny * w + nx]) {
                            const d = fx * fx + fy * fy;
                            if (d < minDistSq)
                                minDistSq = d;
                        }
                    }
                }
            }
            if (minDistSq <= featherRadius * featherRadius) {
                alpha = Math.sqrt(minDistSq) / featherRadius;
            }
        }
        const pi = i * 4;
        if (replacement) {
            data[pi] = Math.round(data[pi] * alpha + replacement[0] * (1 - alpha));
            data[pi + 1] = Math.round(data[pi + 1] * alpha + replacement[1] * (1 - alpha));
            data[pi + 2] = Math.round(data[pi + 2] * alpha + replacement[2] * (1 - alpha));
        }
        else {
            data[pi + 3] = Math.round(alpha * 255);
        }
    }
    return result;
}
function hexToRGB(hex) {
    const h = hex.replace('#', '');
    return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
}
/**
 * Upscale image using Lanczos interpolation.
 */
function upscaleImage(source, options) {
    const { scale, sharpen } = options;
    const sw = source.width;
    const sh = source.height;
    const dw = sw * scale;
    const dh = sh * scale;
    const result = new ImageData(dw, dh);
    const srcData = source.data;
    const dstData = result.data;
    // Lanczos kernel
    const a = 3; // Lanczos-3
    function lanczos(x) {
        if (x === 0)
            return 1;
        if (Math.abs(x) >= a)
            return 0;
        const pix = Math.PI * x;
        return (a * Math.sin(pix) * Math.sin(pix / a)) / (pix * pix);
    }
    // Bicubic Lanczos resampling
    for (let dy = 0; dy < dh; dy++) {
        const sy = (dy + 0.5) / scale - 0.5;
        const sy0 = Math.floor(sy) - a + 1;
        const sy1 = Math.floor(sy) + a;
        for (let dx = 0; dx < dw; dx++) {
            const sx = (dx + 0.5) / scale - 0.5;
            const sx0 = Math.floor(sx) - a + 1;
            const sx1 = Math.floor(sx) + a;
            let r = 0, g = 0, b = 0, aa = 0, wSum = 0;
            for (let iy = sy0; iy <= sy1; iy++) {
                const wy = lanczos(sy - iy);
                const cy = Math.max(0, Math.min(sh - 1, iy));
                for (let ix = sx0; ix <= sx1; ix++) {
                    const wx = lanczos(sx - ix);
                    const w = wx * wy;
                    const cx = Math.max(0, Math.min(sw - 1, ix));
                    const si = (cy * sw + cx) * 4;
                    r += srcData[si] * w;
                    g += srcData[si + 1] * w;
                    b += srcData[si + 2] * w;
                    aa += srcData[si + 3] * w;
                    wSum += w;
                }
            }
            const di = (dy * dw + dx) * 4;
            if (wSum > 0) {
                dstData[di] = Math.max(0, Math.min(255, r / wSum));
                dstData[di + 1] = Math.max(0, Math.min(255, g / wSum));
                dstData[di + 2] = Math.max(0, Math.min(255, b / wSum));
                dstData[di + 3] = Math.max(0, Math.min(255, aa / wSum));
            }
        }
    }
    // Post-sharpen with unsharp mask
    if (sharpen > 0) {
        const blurred = new Float32Array(dw * dh * 4);
        // Quick box blur
        for (let y = 1; y < dh - 1; y++) {
            for (let x = 1; x < dw - 1; x++) {
                const di = (y * dw + x) * 4;
                for (let c = 0; c < 3; c++) {
                    blurred[di + c] = (dstData[di + c - dw * 4] +
                        dstData[di + c + dw * 4] +
                        dstData[di + c - 4] +
                        dstData[di + c + 4] +
                        dstData[di + c] * 4) / 8;
                }
            }
        }
        // Apply unsharp mask
        for (let i = 0; i < dstData.length; i += 4) {
            for (let c = 0; c < 3; c++) {
                const sharp = dstData[i + c] + (dstData[i + c] - blurred[i + c]) * sharpen * 2;
                dstData[i + c] = Math.max(0, Math.min(255, sharp));
            }
        }
    }
    return result;
}
/**
 * Denoise image using bilateral filter approximation.
 */
function denoiseImage(source, options) {
    const { strength, detailPreservation, luminanceWeight } = options;
    const w = source.width;
    const h = source.height;
    const result = new ImageData(new Uint8ClampedArray(source.data), w, h);
    const data = result.data;
    const radius = Math.max(1, Math.round(strength * 5));
    const sigmaSpace = radius * 0.5;
    const sigmaColor = (1 - detailPreservation) * 150 + 10;
    for (let y = 0; y < h; y++) {
        for (let x = 0; x < w; x++) {
            const ci = (y * w + x) * 4;
            let rSum = 0, gSum = 0, bSum = 0, wSum = 0;
            for (let dy = -radius; dy <= radius; dy++) {
                for (let dx = -radius; dx <= radius; dx++) {
                    const nx = x + dx;
                    const ny = y + dy;
                    if (nx < 0 || nx >= w || ny < 0 || ny >= h)
                        continue;
                    const ni = (ny * w + nx) * 4;
                    const spatialDist = Math.sqrt(dx * dx + dy * dy);
                    const spatialW = Math.exp(-(spatialDist * spatialDist) / (2 * sigmaSpace * sigmaSpace));
                    // Color distance (weighted luminance vs chroma)
                    const lumDiff = luminance(source.data[ni], source.data[ni + 1], source.data[ni + 2]) -
                        luminance(source.data[ci], source.data[ci + 1], source.data[ci + 2]);
                    const chromaDiff = Math.sqrt((source.data[ni] - source.data[ci]) ** 2 +
                        (source.data[ni + 1] - source.data[ci + 1]) ** 2 +
                        (source.data[ni + 2] - source.data[ci + 2]) ** 2);
                    const colorDiff = lumDiff * luminanceWeight + chromaDiff * (1 - luminanceWeight);
                    const colorW = Math.exp(-(colorDiff * colorDiff) / (2 * sigmaColor * sigmaColor));
                    const weight = spatialW * colorW;
                    rSum += source.data[ni] * weight;
                    gSum += source.data[ni + 1] * weight;
                    bSum += source.data[ni + 2] * weight;
                    wSum += weight;
                }
            }
            if (wSum > 0) {
                data[ci] = Math.round(rSum / wSum);
                data[ci + 1] = Math.round(gSum / wSum);
                data[ci + 2] = Math.round(bSum / wSum);
            }
        }
    }
    return result;
}
/* ── Hook ── */
function useAITools(sourceImage) {
    const [isProcessing, setIsProcessing] = React.useState(false);
    const [activeTool, setActiveTool] = React.useState('colorize');
    const [lastResult, setLastResult] = React.useState(null);
    const [colorizeOptions, setColorizeOptions] = React.useState({
        intensity: 0.7, temperature: 'neutral', saturation: 1.2,
    });
    const [bgRemoveOptions, setBgRemoveOptions] = React.useState({
        edgeSoftness: 2, threshold: 0.35, iterations: 8, featherRadius: 3, replacementColor: null,
    });
    const [upscaleOptions, setUpscaleOptions] = React.useState({
        scale: 2, method: 'lanczos', sharpen: 0.3,
    });
    const [denoiseOptions, setDenoiseOptions] = React.useState({
        strength: 0.5, detailPreservation: 0.6, luminanceWeight: 0.7,
    });
    const process = React.useCallback(async () => {
        if (!sourceImage)
            return null;
        setIsProcessing(true);
        const start = performance.now();
        let imageData;
        try {
            switch (activeTool) {
                case 'colorize':
                    imageData = autoColorize(sourceImage, colorizeOptions);
                    break;
                case 'background-remove':
                    imageData = removeBackground(sourceImage, bgRemoveOptions);
                    break;
                case 'upscale':
                    imageData = upscaleImage(sourceImage, upscaleOptions);
                    break;
                case 'denoise':
                    imageData = denoiseImage(sourceImage, denoiseOptions);
                    break;
                default:
                    return null;
            }
            const result = {
                imageData,
                processingTimeMs: performance.now() - start,
                tool: activeTool,
            };
            setLastResult(result);
            return result;
        }
        finally {
            setIsProcessing(false);
        }
    }, [sourceImage, activeTool, colorizeOptions, bgRemoveOptions, upscaleOptions, denoiseOptions]);
    return {
        activeTool, setActiveTool,
        colorizeOptions, setColorizeOptions,
        bgRemoveOptions, setBgRemoveOptions,
        upscaleOptions, setUpscaleOptions,
        denoiseOptions, setDenoiseOptions,
        process, isProcessing, lastResult,
    };
}
/* ── Component ── */
const TOOL_LABELS = {
    colorize: '🎨 Auto-Colorize',
    'background-remove': '✂️ Remove Background',
    upscale: '🔍 Upscale (ESRGAN)',
    denoise: '🔇 Denoise',
};
const NiceAITools = ({ sourceImage, onResult, className = '' }) => {
    var _a;
    const ai = useAITools(sourceImage);
    const handleProcess = async () => {
        const result = await ai.process();
        if (result && onResult)
            onResult(result);
    };
    return (jsxRuntime.jsxs("div", { className: `nice-ai-tools ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsx("strong", { children: "\uD83E\uDD16 AI Tools" }), jsxRuntime.jsx("div", { style: { display: 'flex', gap: 2, flexWrap: 'wrap' }, children: Object.entries(TOOL_LABELS).map(([key, label]) => (jsxRuntime.jsx("button", { type: "button", onClick: () => ai.setActiveTool(key), style: {
                        flex: 1, padding: '4px 6px', fontSize: 11, cursor: 'pointer', borderRadius: 4,
                        border: '1px solid var(--border, #e2e8f0)',
                        background: ai.activeTool === key ? 'var(--accent, #6366f1)' : 'transparent',
                        color: ai.activeTool === key ? '#fff' : 'inherit',
                        minWidth: 100,
                    }, children: label }, key))) }), ai.activeTool === 'colorize' && (jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Intensity: ", ai.colorizeOptions.intensity.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.05, value: ai.colorizeOptions.intensity, onChange: (e) => ai.setColorizeOptions({ ...ai.colorizeOptions, intensity: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Saturation: ", ai.colorizeOptions.saturation.toFixed(1), jsxRuntime.jsx("input", { type: "range", min: 0, max: 2, step: 0.1, value: ai.colorizeOptions.saturation, onChange: (e) => ai.setColorizeOptions({ ...ai.colorizeOptions, saturation: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Temperature:", jsxRuntime.jsxs("select", { value: ai.colorizeOptions.temperature, onChange: (e) => ai.setColorizeOptions({ ...ai.colorizeOptions, temperature: e.target.value }), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "warm", children: "Warm" }), jsxRuntime.jsx("option", { value: "neutral", children: "Neutral" }), jsxRuntime.jsx("option", { value: "cool", children: "Cool" })] })] })] })), ai.activeTool === 'background-remove' && (jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Threshold: ", ai.bgRemoveOptions.threshold.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0.1, max: 0.9, step: 0.05, value: ai.bgRemoveOptions.threshold, onChange: (e) => ai.setBgRemoveOptions({ ...ai.bgRemoveOptions, threshold: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Iterations: ", ai.bgRemoveOptions.iterations, jsxRuntime.jsx("input", { type: "range", min: 1, max: 20, step: 1, value: ai.bgRemoveOptions.iterations, onChange: (e) => ai.setBgRemoveOptions({ ...ai.bgRemoveOptions, iterations: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Feather: ", ai.bgRemoveOptions.featherRadius, "px", jsxRuntime.jsx("input", { type: "range", min: 0, max: 10, step: 1, value: ai.bgRemoveOptions.featherRadius, onChange: (e) => ai.setBgRemoveOptions({ ...ai.bgRemoveOptions, featherRadius: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { style: { display: 'flex', gap: 4, alignItems: 'center' }, children: ["Replace BG:", jsxRuntime.jsx("input", { type: "color", value: (_a = ai.bgRemoveOptions.replacementColor) !== null && _a !== void 0 ? _a : '#ffffff', onChange: (e) => ai.setBgRemoveOptions({ ...ai.bgRemoveOptions, replacementColor: e.target.value }) }), jsxRuntime.jsx("button", { type: "button", onClick: () => ai.setBgRemoveOptions({ ...ai.bgRemoveOptions, replacementColor: null }), style: { fontSize: 10 }, children: "Transparent" })] })] })), ai.activeTool === 'upscale' && (jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Scale:", jsxRuntime.jsxs("select", { value: ai.upscaleOptions.scale, onChange: (e) => ai.setUpscaleOptions({ ...ai.upscaleOptions, scale: +e.target.value }), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: 2, children: "2x" }), jsxRuntime.jsx("option", { value: 3, children: "3x" }), jsxRuntime.jsx("option", { value: 4, children: "4x" })] })] }), jsxRuntime.jsxs("label", { children: ["Sharpen: ", ai.upscaleOptions.sharpen.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.05, value: ai.upscaleOptions.sharpen, onChange: (e) => ai.setUpscaleOptions({ ...ai.upscaleOptions, sharpen: +e.target.value }), style: { width: '100%' } })] })] })), ai.activeTool === 'denoise' && (jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Strength: ", ai.denoiseOptions.strength.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.05, value: ai.denoiseOptions.strength, onChange: (e) => ai.setDenoiseOptions({ ...ai.denoiseOptions, strength: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Detail: ", ai.denoiseOptions.detailPreservation.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.05, value: ai.denoiseOptions.detailPreservation, onChange: (e) => ai.setDenoiseOptions({ ...ai.denoiseOptions, detailPreservation: +e.target.value }), style: { width: '100%' } })] })] })), jsxRuntime.jsx("button", { type: "button", onClick: handleProcess, disabled: ai.isProcessing || !sourceImage, style: {
                    background: ai.isProcessing ? '#94a3b8' : 'var(--accent, #6366f1)',
                    color: '#fff', border: 'none', borderRadius: 4, padding: '8px 12px',
                    cursor: ai.isProcessing ? 'wait' : 'pointer',
                }, children: ai.isProcessing ? '⏳ Processing…' : `▶️ ${TOOL_LABELS[ai.activeTool]}` }), ai.lastResult && (jsxRuntime.jsxs("div", { style: { fontSize: 11, color: 'var(--muted, #64748b)' }, children: ["Processed in ", ai.lastResult.processingTimeMs.toFixed(0), "ms", ai.activeTool === 'upscale' && ` → ${ai.lastResult.imageData.width}×${ai.lastResult.imageData.height}`] }))] }));
};

exports.NiceAITools = NiceAITools;
exports.autoColorize = autoColorize;
exports.denoiseImage = denoiseImage;
exports.removeBackground = removeBackground;
exports.upscaleImage = upscaleImage;
exports.useAITools = useAITools;
//# sourceMappingURL=NiceAITools.js.map
