// Color Extractor — extract dominant colors from an uploaded image.

window.TOOL_HANDLERS['color-extractor'] = function ColorExtractorTool() {
  const [src, setSrc] = React.useState('');
  const [palette, setPalette] = React.useState([]);
  const [copied, setCopied] = React.useState('');
  const canvasRef = React.useRef(null);

  const rgbDist = (a, b) =>
    Math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2);

  const rgbToHex = (r, g, b) =>
    '#' + [r, g, b].map(v => Math.round(v).toString(16).padStart(2, '0')).join('');

  const handleFile = async (f) => {
    if (!f || !f.type.startsWith('image/')) return;
    const { img, url } = await window.loadImageFromFile(f);
    setSrc(url);

    // Draw to hidden canvas, sample pixels
    const c = document.createElement('canvas');
    const maxDim = 200; // downsample for speed
    const scale = Math.min(1, maxDim / Math.max(img.width, img.height));
    c.width = Math.round(img.width * scale);
    c.height = Math.round(img.height * scale);
    const ctx = c.getContext('2d');
    ctx.drawImage(img, 0, 0, c.width, c.height);
    const data = ctx.getImageData(0, 0, c.width, c.height).data;

    // Quantize to 5-bit per channel, bucket count
    const buckets = {};
    let totalPixels = 0;
    for (let i = 0; i < data.length; i += 16) { // sample every 4th pixel (stride 16 = 4 channels * 4)
      const a = data[i + 3];
      if (a < 128) continue; // skip transparent
      const r = data[i] >> 3;
      const g = data[i + 1] >> 3;
      const b = data[i + 2] >> 3;
      const key = (r << 10) | (g << 5) | b;
      buckets[key] = (buckets[key] || 0) + 1;
      totalPixels++;
    }

    // Sort by frequency
    const sorted = Object.entries(buckets)
      .map(([k, count]) => {
        const n = Number(k);
        return {
          rgb: [((n >> 10) & 31) << 3, ((n >> 5) & 31) << 3, (n & 31) << 3],
          count
        };
      })
      .sort((a, b) => b.count - a.count);

    // Merge similar colors (distance < 30) and pick top N distinct
    const distinct = [];
    for (const entry of sorted) {
      if (distinct.length >= 12) break;
      const isDuplicate = distinct.some(d => rgbDist(d.rgb, entry.rgb) < 30);
      if (!isDuplicate) {
        distinct.push(entry);
      }
    }

    // Compute percentages
    const totalCounts = distinct.reduce((s, d) => s + d.count, 0);
    setPalette(distinct.map(d => ({
      hex: rgbToHex(d.rgb[0], d.rgb[1], d.rgb[2]),
      pct: Math.round((d.count / totalPixels) * 100)
    })));
  };

  const copyHex = (hex) => {
    navigator.clipboard.writeText(hex);
    setCopied(hex);
    setTimeout(() => setCopied(''), 1200);
  };

  const textColor = (hex) => {
    const n = parseInt(hex.slice(1), 16);
    const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;
    return (0.299 * r + 0.587 * g + 0.114 * b) > 140 ? '#1a1a1a' : '#ffffff';
  };

  const reset = () => {
    if (src) URL.revokeObjectURL(src);
    setSrc('');
    setPalette([]);
    setCopied('');
  };

  if (!src) {
    return <window.Dropzone onFile={handleFile} title="Drop an image here" hint="extract its dominant colors" accept="image/*" />;
  }

  return (
    <div className="mini-tool">
      <div style={{
        display: 'flex', gap: 20, flexWrap: 'wrap',
        alignItems: 'flex-start', marginBottom: 16
      }}>
        <div style={{
          flex: '0 0 auto',
          borderRadius: 8, overflow: 'hidden',
          border: '1px solid var(--id-border)',
          maxWidth: 220,
        }}>
          <img src={src} alt="uploaded" style={{
            display: 'block', width: '100%', height: 'auto', maxHeight: 200, objectFit: 'cover'
          }} />
        </div>

        <div style={{ flex: 1, minWidth: 240 }}>
          <div className="mini-label" style={{ marginBottom: 10 }}>
            Extracted palette ({palette.length} colors)
          </div>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
            {palette.map((c, i) => (
              <div key={i} onClick={() => copyHex(c.hex)}
                   style={{
                     cursor: 'pointer',
                     display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
                     transition: 'transform 0.15s',
                   }}
                   onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.08)'}
                   onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}>
                <div style={{
                  width: 52, height: 52,
                  backgroundColor: c.hex,
                  borderRadius: 8,
                  border: '1px solid var(--id-border)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  {copied === c.hex && (
                    <span style={{
                      fontSize: 9, fontWeight: 700,
                      color: textColor(c.hex),
                    }}>Copied!</span>
                  )}
                </div>
                <span style={{
                  fontSize: 10, fontFamily: 'monospace',
                  color: 'var(--id-text)',
                }}>{c.hex.toUpperCase()}</span>
                <span style={{
                  fontSize: 9,
                  color: 'var(--id-text-sub, var(--id-text))',
                  opacity: 0.7,
                }}>{c.pct}%</span>
              </div>
            ))}
          </div>
        </div>
      </div>

      {copied && (
        <div style={{
          textAlign: 'center', marginBottom: 8,
          color: 'var(--id-primary)', fontWeight: 600, fontSize: 13,
        }}>
          Copied {copied.toUpperCase()}
        </div>
      )}

      <div className="cmp-actions">
        <button className="btn btn-secondary" onClick={reset}>
          <window.Icon name="upload" size={16} /> Upload another
        </button>
      </div>
    </div>
  );
};
