/*
 * WingboneCallBuilder — interactive product configurator for Green Swamp Turkey Calls.
 *
 * SECTIONS
 *   1. WRAP_COLORS   — nine selectable wrap finishes (thread/lacquer tones + a few metallics)
 *   2. GEOMETRY       — the wingbone tube is built procedurally (no external artwork asset
 *                       exists for this call, unlike the box call). A cubic Bezier centerline
 *                       (one gentle bend, matching a real wingbone — no S-curve) is sampled at
 *                       fixed intervals and offset perpendicular to its tangent by a width that
 *                       tapers gently almost the entire length, dips into a subtle waist, then
 *                       flares into a solid, rounded foot — the natural joint head of a real
 *                       bone call, not an open trumpet bell. The three wrap bands sit tightly
 *                       clustered together at the joint (matching the reference photos) with a
 *                       crisscrossed thread overlay drawn across the whole cluster. The body
 *                       outline, edge strokes, band outlines, and thread lines are all
 *                       precomputed once as module-level constants since none of it depends on
 *                       user selection — only the fill color of each band changes at render time.
 *   3. WingboneCallBuilder (component)
 *      a. State — one selected color per wrap band, plus the previewFlash effect
 *      b. SVG illustration — left column, renders the precomputed paths with the selected
 *         wrap colors swapped in as gradient fills
 *      c. Configuration form — right column, mirrors the Box Call builder's layout
 *
 * NOTE: This file is loaded via Babel standalone in the plain HTML page (no bundler).
 */

/* global React, ReactDOM */
const { useState, useEffect, useRef } = React;

const BRAND  = '#5C3A1E';
const SELECT =
  'w-full border border-gray-300 rounded-lg px-3 py-2.5 text-base text-gray-800 ' +
  'bg-white focus:outline-none focus:ring-2 focus:ring-stone-600 focus:border-transparent ' +
  'appearance-none cursor-pointer';

/* ─────────────────────────── 1. WRAP COLORS ─────────────────────────── */
const WRAP_COLORS = {
  black:  { id: 'black',  label: 'Black',  hi: '#3D3D3D', base: '#1A1A1A', shadow: '#000000' },
  brown:  { id: 'brown',  label: 'Brown',  hi: '#6B4226', base: '#4A2E18', shadow: '#2A1810' },
  green:  { id: 'green',  label: 'Green',  hi: '#447F52', base: '#2F5D34', shadow: '#193821' },
  blue:   { id: 'blue',   label: 'Blue',   hi: '#3A6CB0', base: '#1F4E79', shadow: '#102A40' },
  orange: { id: 'orange', label: 'Orange', hi: '#E0832E', base: '#C2611D', shadow: '#7A3D10' },
  copper: { id: 'copper', label: 'Copper', hi: '#E0935A', base: '#B5651D', shadow: '#6E3A10' },
  silver: { id: 'silver', label: 'Silver', hi: '#EDEFF0', base: '#AEB4B8', shadow: '#6C7276' },
  gold:   { id: 'gold',   label: 'Gold',   hi: '#F0DDA0', base: '#C8A45A', shadow: '#8C6B2E' },
  red:    { id: 'red',    label: 'Red',    hi: '#B23A3A', base: '#8C1F1F', shadow: '#4F0E0E' },
};
const WRAP_LIST = Object.values(WRAP_COLORS);

/* ─────────────────────────── 2. GEOMETRY ─────────────────────────────
 * Centerline control points: one gentle bend from the thin mouthpiece tip
 * down to the wide, rounded foot — a real wingbone curves in a single
 * direction, not the dramatic S-curve a naive control-point layout produces.
 */
const P0 = [55, 35];
const P1 = [90, 160];
const P2 = [230, 330];
const P3 = [430, 390];

function bezierPoint(t) {
  const mt = 1 - t, a = mt * mt * mt, b = 3 * mt * mt * t, c = 3 * mt * t * t, d = t * t * t;
  return [
    a * P0[0] + b * P1[0] + c * P2[0] + d * P3[0],
    a * P0[1] + b * P1[1] + c * P2[1] + d * P3[1],
  ];
}
function bezierTangent(t) {
  const mt = 1 - t;
  return [
    3 * mt * mt * (P1[0] - P0[0]) + 6 * mt * t * (P2[0] - P1[0]) + 3 * t * t * (P3[0] - P2[0]),
    3 * mt * mt * (P1[1] - P0[1]) + 6 * mt * t * (P2[1] - P1[1]) + 3 * t * t * (P3[1] - P2[1]),
  ];
}
// Tapers gently almost the entire length, dips into a subtle waist just
// above the foot, then flares into the rounded, bulbous foot — the natural
// joint head of a real wingbone, not a flared trumpet bell.
function widthAt(t) {
  const ramp  = 6 + 34 * Math.pow(t, 1.4);
  const waist = -6 * Math.exp(-Math.pow((t - 0.82) / 0.06, 2));
  const foot  = 16 * Math.pow(Math.max(0, (t - 0.93) / 0.07), 2);
  return Math.max(4, ramp + waist + foot);
}
function frameAt(t) {
  const [cx, cy] = bezierPoint(t);
  const [tx, ty] = bezierTangent(t);
  const len = Math.hypot(tx, ty) || 1;
  return { cx, cy, nx: -ty / len, ny: tx / len, fx: tx / len, fy: ty / len, w: widthAt(t) / 2 };
}
function sampleEdges(tStart, tEnd, steps) {
  const top = [], bottom = [], frames = [];
  for (let i = 0; i <= steps; i++) {
    const t = tStart + (tEnd - tStart) * (i / steps);
    const f = frameAt(t);
    top.push([f.cx + f.nx * f.w, f.cy + f.ny * f.w]);
    bottom.push([f.cx - f.nx * f.w, f.cy - f.ny * f.w]);
    frames.push(f);
  }
  return { top, bottom, frames };
}
function ptsToPath(top, bottom) {
  const fmt = ([x, y]) => `${x.toFixed(1)},${y.toFixed(1)}`;
  return (
    `M ${fmt(top[0])} ` +
    top.slice(1).map(p => `L ${fmt(p)}`).join(' ') + ' ' +
    bottom.slice().reverse().map(p => `L ${fmt(p)}`).join(' ') + ' Z'
  );
}
function lineStr(pts) {
  return pts.map(([x, y], i) => `${i === 0 ? 'M' : 'L'} ${x.toFixed(1)},${y.toFixed(1)}`).join(' ');
}
// Body outline ends in a solid, rounded cap instead of a flat chord — real
// wingbone calls have no open bell/rim, just the rounded end of the bone.
function bodyPathRounded(tStart, tEnd, steps) {
  const { top, bottom, frames } = sampleEdges(tStart, tEnd, steps);
  const fmt = ([x, y]) => `${x.toFixed(1)},${y.toFixed(1)}`;
  const last = frames[frames.length - 1];
  const capCtrl1 = [last.cx + last.nx * last.w + last.fx * last.w * 0.9, last.cy + last.ny * last.w + last.fy * last.w * 0.9];
  const capCtrl2 = [last.cx - last.nx * last.w + last.fx * last.w * 0.9, last.cy - last.ny * last.w + last.fy * last.w * 0.9];
  return (
    `M ${fmt(top[0])} ` +
    top.slice(1).map(p => `L ${fmt(p)}`).join(' ') + ' ' +
    `C ${fmt(capCtrl1)} ${fmt(capCtrl2)} ${fmt(bottom[bottom.length - 1])} ` +
    bottom.slice().reverse().slice(1).map(p => `L ${fmt(p)}`).join(' ') + ' Z'
  );
}

const BODY        = sampleEdges(0, 1, 80);
const BODY_PATH    = bodyPathRounded(0, 1, 80);
const TOP_EDGE     = lineStr(BODY.top);
const BOTTOM_EDGE  = lineStr(BODY.bottom);

// The three wrap bands sit tightly clustered together at the joint, just
// past the shaft's midpoint — real wingbone calls wrap the joint in one
// continuous banded section rather than spacing bands apart along the shaft.
const BAND_RANGES = [
  { t0: 0.455, t1: 0.495 },
  { t0: 0.500, t1: 0.585 },
  { t0: 0.590, t1: 0.630 },
];
const BANDS = BAND_RANGES.map(({ t0, t1 }) => {
  const { top, bottom } = sampleEdges(t0, t1, 10);
  return { path: ptsToPath(top, bottom), topEdge: lineStr(top), botEdge: lineStr(bottom) };
});

// Thread crisscrosses diagonally over the whole wrap cluster regardless of
// the chosen colors underneath — alternating diagonal segments between the
// top and bottom edges form the X weave seen on real wrapped wingbone calls.
function threadLines(t0, t1, n) {
  const lines = [];
  const step = (t1 - t0) / n;
  for (let i = 0; i < n; i++) {
    const fa = frameAt(t0 + i * step), fb = frameAt(t0 + (i + 1) * step);
    lines.push([[fa.cx + fa.nx * fa.w, fa.cy + fa.ny * fa.w], [fb.cx - fb.nx * fb.w, fb.cy - fb.ny * fb.w]]);
    lines.push([[fa.cx - fa.nx * fa.w, fa.cy - fa.ny * fa.w], [fb.cx + fb.nx * fb.w, fb.cy + fb.ny * fb.w]]);
  }
  return lines;
}
const THREAD = threadLines(BAND_RANGES[0].t0, BAND_RANGES[BAND_RANGES.length - 1].t1, 9);

// A few short, dark mottling marks on the rounded foot — echoes the natural
// blotching visible on real bone calls.
const FOOT_MARKS = [0.90, 0.95, 0.985].map(t => {
  const f = frameAt(t);
  return {
    x1: f.cx + f.nx * f.w * 0.4,  y1: f.cy + f.ny * f.w * 0.4,
    x2: f.cx + f.nx * f.w * 0.85, y2: f.cy + f.ny * f.w * 0.85,
  };
});

// Ground shadow sits just beyond the foot, in the direction the bone points.
const FOOT_SHADOW = (() => {
  const f = frameAt(1);
  return { cx: f.cx + f.fx * 10, cy: f.cy + f.fy * 10 + 12 };
})();

const VIEWBOX = '0 0 480 470';

function WingboneSVG({ wrap1, wrap2, wrap3 }) {
  const colors = [wrap1, wrap2, wrap3];
  return (
    <svg viewBox={VIEWBOX} xmlns="http://www.w3.org/2000/svg" style={{ width: '100%', height: 'auto', display: 'block', overflow: 'visible' }}>
      <defs>
        <linearGradient id="boneShade" x1="0%" y1="0%" x2="100%" y2="100%">
          <stop offset="0%"   stopColor="#FFFBF2" />
          <stop offset="50%"  stopColor="#F3E7CC" />
          <stop offset="100%" stopColor="#D9C49C" />
        </linearGradient>
        {WRAP_LIST.map(c => (
          <linearGradient key={c.id} id={`wrap-${c.id}`} x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%"   stopColor={c.hi} />
            <stop offset="45%"  stopColor={c.base} />
            <stop offset="100%" stopColor={c.shadow} />
          </linearGradient>
        ))}
        <filter id="wbBlur" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur stdDeviation="5" />
        </filter>
      </defs>

      {/* ground shadow beneath the foot */}
      <ellipse cx={FOOT_SHADOW.cx} cy={FOOT_SHADOW.cy} rx="40" ry="9" fill="black" opacity="0.18" filter="url(#wbBlur)" />

      {/* bone body — solid, rounded foot, no open bell */}
      <path d={BODY_PATH} fill="url(#boneShade)" stroke="#B8A271" strokeWidth="1.2" />

      {/* cylindrical highlight / shadow along the edges */}
      <path d={TOP_EDGE}    fill="none" stroke="#FFFFFF" strokeWidth="2" opacity="0.55" strokeLinecap="round" />
      <path d={BOTTOM_EDGE} fill="none" stroke="#8C7850" strokeWidth="2" opacity="0.45" strokeLinecap="round" />

      {/* natural bone mottling on the foot */}
      {FOOT_MARKS.map((m, i) => (
        <line key={i} x1={m.x1} y1={m.y1} x2={m.x2} y2={m.y2}
              stroke="#7A5230" strokeWidth="3" strokeLinecap="round" opacity="0.35" />
      ))}

      {/* the three colorable wrap bands */}
      {BANDS.map((band, i) => (
        <g key={i}>
          <path d={band.path} fill={`url(#wrap-${colors[i]})`} />
          <path d={band.topEdge} fill="none" stroke="#1C140A" strokeWidth="1.4" opacity="0.55" />
          <path d={band.botEdge} fill="none" stroke="#1C140A" strokeWidth="1.4" opacity="0.55" />
        </g>
      ))}

      {/* crisscrossed thread wrap over the whole band cluster */}
      <g opacity="0.55">
        {THREAD.map(([a, b], i) => (
          <line key={i} x1={a[0]} y1={a[1]} x2={b[0]} y2={b[1]} stroke="#EDE0BD" strokeWidth="1.6" />
        ))}
      </g>
    </svg>
  );
}

/* ─────────────────────────── 3. COMPONENT ───────────────────────────── */
function WingboneCallBuilder() {
  const [wrap1, setWrap1] = useState('green');
  const [wrap2, setWrap2] = useState('red');
  const [wrap3, setWrap3] = useState('copper');
  const [previewFlash, setPreviewFlash] = useState(false);
  const isMounted = useRef(false);

  useEffect(() => {
    if (!isMounted.current) { isMounted.current = true; return; }
    setPreviewFlash(true);
    const t = setTimeout(() => setPreviewFlash(false), 200);
    return () => clearTimeout(t);
  }, [wrap1, wrap2, wrap3]);

  return (
    <div className="max-w-[1100px] mx-auto bg-white rounded-lg shadow-md overflow-hidden">
      <div className="flex flex-col md:flex-row">

        {/* Left: SVG Preview */}
        <div
          className="md:w-[55%] p-6 flex items-center justify-center bg-gray-50 transition duration-150"
          style={{ boxShadow: previewFlash ? 'inset 0 0 0 2px #C8A45A' : 'none' }}
        >
          <div className="w-full max-w-[560px]">
            <WingboneSVG wrap1={wrap1} wrap2={wrap2} wrap3={wrap3} />
          </div>
        </div>

        {/* Right: Options */}
        <div className="md:w-[45%] p-8 border-l border-gray-200 flex flex-col">
          <div className="mb-6">
            <h1
              className="text-2xl font-semibold text-gray-900 leading-tight"
              style={{ fontFamily: "'Oswald', sans-serif" }}
            >
              Wingbone Call
            </h1>
            <p className="text-sm text-gray-500 mt-1">Green Swamp Turkey Calls</p>
          </div>

          <h2 className="text-xs font-semibold text-gray-700 mb-5 uppercase tracking-wide">
            Configure Your Call
          </h2>

          <div className="space-y-5 flex-1">

            {/* 1. Wrap Band 1 Color (nearest the mouthpiece) */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-1.5">
                Wrap Band 1 Color
              </label>
              <select
                value={wrap1}
                onChange={e => setWrap1(e.target.value)}
                className={SELECT}
              >
                {WRAP_LIST.map(c => <option key={c.id} value={c.id}>{c.label}</option>)}
              </select>
            </div>

            {/* 2. Wrap Band 2 Color (middle) */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-1.5">
                Wrap Band 2 Color
              </label>
              <select
                value={wrap2}
                onChange={e => setWrap2(e.target.value)}
                className={SELECT}
              >
                {WRAP_LIST.map(c => <option key={c.id} value={c.id}>{c.label}</option>)}
              </select>
            </div>

            {/* 3. Wrap Band 3 Color (nearest the bell) */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-1.5">
                Wrap Band 3 Color
              </label>
              <select
                value={wrap3}
                onChange={e => setWrap3(e.target.value)}
                className={SELECT}
              >
                {WRAP_LIST.map(c => <option key={c.id} value={c.id}>{c.label}</option>)}
              </select>
            </div>

            {/* Custom Lanyard */}
            <div className="border-t border-gray-200 pt-5 !mt-7">
              <h3 className="text-sm font-semibold text-gray-800 mb-1">
                Custom Lanyard
              </h3>
              <p className="text-xs text-gray-500 leading-relaxed mb-4">
                Add a hand-tied leather or paracord lanyard to your call for an additional charge.
                Contact us to discuss your design and receive a quote.
              </p>
              <a
                href="contact.html"
                className="flex items-center justify-center w-full py-2.5 px-4 rounded-lg text-sm font-medium text-white transition-colors"
                style={{ backgroundColor: BRAND }}
                onMouseEnter={e => e.currentTarget.style.backgroundColor = '#4A2C14'}
                onMouseLeave={e => e.currentTarget.style.backgroundColor = BRAND}
              >
                Contact Us About Lanyards
              </a>
            </div>

          </div>
        </div>
      </div>
    </div>
  );
}

const _mountEl = document.getElementById('wingbone-call-builder');
if (_mountEl) ReactDOM.createRoot(_mountEl).render(React.createElement(WingboneCallBuilder));
