// characters.jsx
// Tiny top-down emoji-ish characters that walk along piecewise-linear paths,
// pause to chat with speech bubbles, and loop seamlessly.
//
// A character's "script" is an array of segments. Each segment is one of:
//   { type: 'walk', from: [x,y], to: [x,y], dur: seconds }
//   { type: 'wait', at:  [x,y], dur: seconds }
//   { type: 'chat', at:  [x,y], text: '안녕!', dur: seconds }
//
// Total duration of all segments must equal LOOP_DUR for seamless looping.

const LOOP_DUR = 30;

// Hair / shirt color palettes — soft, cute, twinly-ish.
const HAIR = ['#3a2a20', '#6b4a32', '#a86b3c', '#d4a76a', '#1a1a1a', '#8b5a3c'];
// 18 distinct character shirt colors — varied hues, not all pink
const SHIRTS = [
  '#f4a8b8', // pink
  '#a8c8e8', // sky blue
  '#c8b8e8', // lavender
  '#f4c890', // peach
  '#a8d8c0', // mint
  '#e8a8a8', // coral
  '#fff0a8', // butter
  '#b8a8e8', // periwinkle
  '#f8a0c8', // rose
  '#88c8b0', // sage
  '#e8b8d4', // dusty pink
  '#a0b8e0', // cornflower
  '#f4b888', // apricot
  '#c0e090', // lime
  '#d8a0c8', // orchid
  '#f8d098', // honey
  '#a0d8e0', // aqua
  '#e09088', // terracotta
];

// Resolve a character's position + facing at local time t (0..LOOP_DUR).
function resolveCharacter(script, t) {
  // t is already wrapped into [0, LOOP_DUR)
  let acc = 0;
  for (let i = 0; i < script.length; i++) {
    const seg = script[i];
    const segEnd = acc + seg.dur;
    if (t >= acc && t < segEnd) {
      const local = t - acc;
      if (seg.type === 'walk') {
        const k = seg.dur > 0 ? local / seg.dur : 0;
        // Smooth ease so they don't snap at junctions
        const eased = 0.5 - 0.5 * Math.cos(k * Math.PI);
        const x = seg.from[0] + (seg.to[0] - seg.from[0]) * eased;
        const y = seg.from[1] + (seg.to[1] - seg.from[1]) * eased;
        const dx = seg.to[0] - seg.from[0];
        const dy = seg.to[1] - seg.from[1];
        // Walking bob phase
        const bob = Math.sin(local * 12) * 1.2;
        return { x, y, bob, walking: true, facing: dirFromVec(dx, dy), bubble: null, k };
      } else if (seg.type === 'wait') {
        return { x: seg.at[0], y: seg.at[1], bob: 0, walking: false, facing: seg.facing || 'down', bubble: null };
      } else if (seg.type === 'chat') {
        // Idle bobble
        const bob = Math.sin(local * 4) * 0.6;
        return {
          x: seg.at[0], y: seg.at[1], bob,
          walking: false,
          facing: seg.facing || 'down',
          bubble: { text: seg.text, age: local, dur: seg.dur, side: seg.side || 'right' },
        };
      }
    }
    acc = segEnd;
  }
  // Should not happen if script sums to LOOP_DUR
  const last = script[script.length - 1];
  const at = last.to || last.at;
  return { x: at[0], y: at[1], bob: 0, walking: false, facing: 'down', bubble: null };
}

function dirFromVec(dx, dy) {
  if (Math.abs(dx) > Math.abs(dy)) return dx > 0 ? 'right' : 'left';
  return dy > 0 ? 'down' : 'up';
}

// One character: head, body, tiny limb hints, drawn top-down-ish (head visible from above).
function Character({ script, hair, shirt, name }) {
  const t = useTime();
  const tt = ((t % LOOP_DUR) + LOOP_DUR) % LOOP_DUR;
  const state = resolveCharacter(script, tt);

  const { x, y, bob, walking, facing, bubble } = state;

  // Simple emoji-ish person from above: a head circle on top of a rounded body.
  // No hair, no arms — just a clean two-shape silhouette.
  return (
    <div style={{
      position: 'absolute',
      left: 0, top: 0,
      transform: `translate(${x - 21}px, ${y - 27 + bob}px)`,
      width: 42, height: 48,
      pointerEvents: 'none',
      willChange: 'transform',
    }}>
      {/* Shadow */}
      <div style={{
        position: 'absolute',
        left: 6, top: 36,
        width: 30, height: 9,
        background: 'rgba(40,30,20,0.22)',
        borderRadius: '50%',
        filter: 'blur(3px)',
      }} />
      {/* Body */}
      <div style={{
        position: 'absolute',
        left: 7, top: 21,
        width: 27, height: 24,
        background: shirt,
        borderRadius: '13px 13px 12px 12px',
        border: '2px solid rgba(40,30,20,0.85)',
      }} />
      {/* Head */}
      <div style={{
        position: 'absolute',
        left: 10, top: 0,
        width: 21, height: 21,
        background: '#f4d4b0',
        borderRadius: '50%',
        border: '2px solid rgba(40,30,20,0.85)',
      }} />
      {/* Speech bubble */}
      {bubble && <SpeechBubble {...bubble} />}
    </div>
  );
}

function SpeechBubble({ text, age, dur, side }) {
  // Fade in fast, hold, fade out fast
  const fadeIn = 0.25;
  const fadeOut = 0.3;
  let alpha = 1;
  let pop = 1;
  if (age < fadeIn) {
    const k = age / fadeIn;
    alpha = k;
    pop = 0.6 + 0.4 * (1 - Math.pow(1 - k, 3));
  } else if (age > dur - fadeOut) {
    const k = (age - (dur - fadeOut)) / fadeOut;
    alpha = 1 - k;
    pop = 1 - 0.1 * k;
  }

  const offsetX = side === 'left' ? -120 : 28;
  const offsetY = -28;

  return (
    <div style={{
      position: 'absolute',
      left: offsetX, top: offsetY,
      opacity: alpha,
      transform: `scale(${pop})`,
      transformOrigin: side === 'left' ? '100% 100%' : '0% 100%',
      willChange: 'transform, opacity',
    }}>
      <div style={{
        background: '#fff',
        borderRadius: 18,
        padding: '8px 16px',
        fontSize: 20,
        fontFamily: 'ui-rounded, "Apple SD Gothic Neo", "Pretendard", system-ui, sans-serif',
        fontWeight: 700,
        color: '#2a1f15',
        whiteSpace: 'nowrap',
        boxShadow: '3px 4px 0 rgba(42,31,21,0.25)',
        lineHeight: 1.2,
      }}>
        {text}
      </div>
      {/* Tail */}
      <div style={{
        position: 'absolute',
        left: side === 'left' ? 'calc(100% - 24px)' : 12,
        bottom: -8,
        width: 14, height: 14,
        background: '#fff',
        transform: 'rotate(45deg)',
      }} />
    </div>
  );
}

// Floating heart pop — for chat moments
function HeartPop({ x, y, start, dur = 1.6 }) {
  const t = useTime();
  const tt = ((t % LOOP_DUR) + LOOP_DUR) % LOOP_DUR;
  // Allow start near loop boundary to wrap
  let local = tt - start;
  if (local < -LOOP_DUR / 2) local += LOOP_DUR;
  if (local < 0 || local > dur) return null;
  const k = local / dur;
  const alpha = k < 0.15 ? k / 0.15 : k > 0.7 ? 1 - (k - 0.7) / 0.3 : 1;
  const ty = -k * 32;
  const tx = Math.sin(k * 6) * 6;
  return (
    <div style={{
      position: 'absolute',
      left: x - 8, top: y - 30,
      transform: `translate(${tx}px, ${ty}px) scale(${0.7 + k * 0.5})`,
      opacity: alpha,
      fontSize: 16,
      pointerEvents: 'none',
      filter: 'drop-shadow(0 1px 0 rgba(0,0,0,0.15))',
    }}>💕</div>
  );
}

window.LOOP_DUR = LOOP_DUR;
window.Character = Character;
window.HeartPop = HeartPop;
window.HAIR = HAIR;
window.SHIRTS = SHIRTS;
