export const G = 6;
export const DOT_R = 2.6;
export const CANVAS = 240;
export const FACE = { LE: [10, 13], RE: [29, 13], MO: [20, 27] };

// ── Core geometry helpers ─────────────────────────────────────────

function circleFill(cx, cy, r) {
  const pts = [];
  const ri = Math.ceil(r);
  for (let dx = -ri; dx <= ri; dx++)
    for (let dy = -ri; dy <= ri; dy++)
      if (dx * dx + dy * dy <= r * r) pts.push([cx + dx, cy + dy]);
  return pts;
}

function arcPts(cx, cy, r, a1, a2, step = 5) {
  const pts = [], seen = new Set();
  for (let a = a1; a <= a2; a += step) {
    const rad = (a * Math.PI) / 180;
    const x = Math.round(cx + r * Math.cos(rad));
    const y = Math.round(cy + r * Math.sin(rad));
    const k = `${x},${y}`;
    if (!seen.has(k)) { seen.add(k); pts.push([x, y]); }
  }
  return pts;
}

function hLine(cx, cy, half) {
  const pts = [];
  for (let x = Math.round(cx - half); x <= Math.round(cx + half); x++)
    pts.push([x, Math.round(cy)]);
  return pts;
}

function waveLine(cx, cy, half, amp) {
  const pts = [], seen = new Set();
  const h = Math.round(half);
  for (let dx = -h; dx <= h; dx++) {
    const t = (dx / h) * Math.PI;
    const y = Math.round(cy + amp * Math.sin(t));
    const k = `${cx + dx},${y}`;
    if (!seen.has(k)) { seen.add(k); pts.push([cx + dx, y]); }
  }
  return pts;
}

function zigzag(cx, cy, half, amp) {
  const pts = [];
  const h = Math.round(half);
  for (let dx = -h; dx <= h; dx++) {
    const seg = dx + h;
    pts.push([cx + dx, Math.round(cy + (seg % 4 < 2 ? amp : -amp))]);
  }
  return pts;
}

function sub(a, b) {
  const set = new Set(b.map(([x, y]) => `${x},${y}`));
  return a.filter(([x, y]) => !set.has(`${x},${y}`));
}

function heart(cx, cy) {
  return [
    [-1, -2], [1, -2],
    [-2, -1], [-1, -1], [0, -1], [1, -1], [2, -1],
    [-2, 0], [-1, 0], [0, 0], [1, 0], [2, 0],
    [-1, 1], [0, 1], [1, 1],
    [0, 2],
  ].map(([dx, dy]) => [cx + dx, cy + dy]);
}

function spiral(cx, cy, maxR, turns) {
  const pts = [], seen = new Set();
  const steps = Math.round(turns * 2 * Math.PI * 8);
  for (let i = 0; i <= steps; i++) {
    const angle = (i / steps) * turns * 2 * Math.PI;
    const r = (i / steps) * maxR;
    const x = Math.round(cx + r * Math.cos(angle));
    const y = Math.round(cy + r * Math.sin(angle));
    const k = `${x},${y}`;
    if (!seen.has(k)) { seen.add(k); pts.push([x, y]); }
  }
  return pts;
}

function star(cx, cy, r) {
  const pts = [], seen = new Set();
  for (let i = 0; i < 8; i++) {
    const angle = (i * 45 * Math.PI) / 180;
    for (let d = 0; d <= r; d += 0.5) {
      const x = Math.round(cx + d * Math.cos(angle));
      const y = Math.round(cy + d * Math.sin(angle));
      const k = `${x},${y}`;
      if (!seen.has(k)) { seen.add(k); pts.push([x, y]); }
    }
  }
  return pts;
}

function makeZ(cx, cy) {
  return [
    [cx - 1, cy - 1], [cx, cy - 1], [cx + 1, cy - 1],
    [cx, cy],
    [cx - 1, cy + 1], [cx, cy + 1], [cx + 1, cy + 1],
  ];
}

function questionMark(cx, cy) {
  return [
    ...arcPts(cx, cy - 1, 1.5, 200, 360, 30),
    [cx, cy + 1],
    [cx, cy + 3],
  ];
}

// ── Standard (interaction) eye builders ──────────────────────────

function femEye(cx, cy) {
  return {
    iris: circleFill(cx, cy, 3),
    pupil: circleFill(cx, cy, 1.5),
    hl: [[cx - 1, cy - 1]],
    lash: hLine(cx, cy - 3, 3),
  };
}

function wideEye(cx, cy) {
  return {
    iris: circleFill(cx, cy, 4),
    pupil: circleFill(cx, cy, 2),
    hl: [[cx - 1, cy - 1], [cx - 2, cy - 2]],
    lash: hLine(cx, cy - 4, 4),
  };
}

function squintEye(cx, cy) {
  const full = circleFill(cx, cy, 3);
  return {
    iris: full.filter(([, y]) => y >= cy - 1 && y <= cy + 1),
    pupil: [[cx, cy]],
    lash: hLine(cx, cy - 1, 2),
  };
}

function sleepyEye(cx, cy) {
  const full = circleFill(cx, cy, 3);
  return {
    iris: full.filter(([, y]) => y >= cy),
    pupil: [[cx - 1, cy], [cx, cy], [cx + 1, cy]],
    lash: hLine(cx, cy, 3),
  };
}

function downcastEye(cx, cy) {
  return {
    iris: circleFill(cx, cy + 1, 3),
    pupil: circleFill(cx, cy + 2, 1.5),
    hl: [[cx - 1, cy]],
    lash: hLine(cx, cy - 2, 2),
  };
}

function sideEye(cx, cy, xOff = 2) {
  return {
    iris: circleFill(cx, cy, 3),
    pupil: circleFill(cx + xOff, cy, 1.5),
    hl: [[cx + xOff - 1, cy - 1]],
    lash: hLine(cx, cy - 3, 3),
  };
}

function winkEye(cx, cy) {
  return { wink: arcPts(cx, cy + 1, 3, 200, 340, 15) };
}

// ── Micro-expression eye builders (pts-based, minimalist) ────────

// Circle ring outline
function ringEye(cx, cy, r = 3) {
  return { pts: sub(circleFill(cx, cy, r), circleFill(cx, cy, Math.max(0.5, r - 1.5))) };
}

// ∩ upward arch
function archEye(cx, cy, r = 3) {
  return { pts: arcPts(cx, cy, r, 200, 340, 10) };
}

// Horizontal dash bar
function dashEye(cx, cy, half = 3) {
  return { pts: hLine(cx, cy, half) };
}

// ^ peak/tent shape
function peakEye(cx, cy) {
  return { pts: arcPts(cx, cy, 2.5, 228, 312, 12) };
}

// Filled block shifted sideways
function blockSideEye(cx, cy, xOff = 2) {
  const pts = [];
  for (let dx = -1; dx <= 1; dx++)
    for (let dy = -2; dy <= 2; dy++)
      pts.push([cx + dx + xOff, cy + dy]);
  return { pts };
}

// + cross shape (left-right scanning)
function crossEye(cx, cy) {
  const h = hLine(cx, cy, 2);
  const seen = new Set(h.map(([x, y]) => `${x},${y}`));
  const v = [];
  for (let dy = -2; dy <= 2; dy++) {
    const k = `${cx},${cy + dy}`;
    if (!seen.has(k)) v.push([cx, cy + dy]);
  }
  return { pts: [...h, ...v] };
}

// ── Mouth builders ────────────────────────────────────────────────

function smileMouth(cx, cy) { return arcPts(cx, cy, 5, 25, 155); }
function wideMouth(cx, cy)  { return arcPts(cx, cy, 7, 15, 165); }
function straightMouth(cx, cy) { return hLine(cx, cy, 5); }

function openMouth(cx, cy, openness) {
  const b = Math.max(1, openness);
  const top = arcPts(cx, cy - b, 4, 185, 355, 10);
  const bot = arcPts(cx, cy + b, 4, 5, 175, 10);
  const seen = new Set(top.map(([x, y]) => `${x},${y}`));
  return [...top, ...bot.filter(([x, y]) => !seen.has(`${x},${y}`))];
}

function smirkMouth(cx, cy) {
  const main = arcPts(cx - 2, cy, 4, 30, 120, 10);
  const dip  = arcPts(cx + 3, cy + 2, 2.5, 270, 360, 15);
  const seen = new Set(main.map(([x, y]) => `${x},${y}`));
  return [...main, ...dip.filter(([x, y]) => !seen.has(`${x},${y}`))];
}

function poutyMouth(cx, cy) {
  const lft = arcPts(cx - 2, cy, 2, 200, 340, 20);
  const rgt = arcPts(cx + 2, cy, 2, 200, 340, 20);
  const bot = hLine(cx, cy + 2, 3);
  const seen = new Set(lft.map(([x, y]) => `${x},${y}`));
  return [...lft, ...rgt.filter(([x, y]) => !seen.has(`${x},${y}`)), ...bot];
}

// ── Animated speaking mouth ───────────────────────────────────────
// Three-step animation: closed smile → slight open → more open
// Uses smaller radius (3–3.5) and offset (1–2) for a natural look.
export function getSpeakMouth(mx, my, t) {
  const phase = Math.abs(Math.sin(t * 1.4)); // 0 = closed, 1 = max open
  if (phase < 0.25) {
    return arcPts(mx, my, 4, 20, 160, 8);   // closed: gentle smile
  }
  const offset = phase < 0.65 ? 1 : 2;
  const top  = arcPts(mx, my - offset, 3.5, 190, 350, 8);
  const bot  = arcPts(mx, my + offset, 3.0,  10, 170, 8);
  const seen = new Set(top.map(([x, y]) => `${x},${y}`));
  return [...top, ...bot.filter(([x, y]) => !seen.has(`${x},${y}`))];
}

// ── Anchor coordinates ────────────────────────────────────────────

const [LEx, LEy] = FACE.LE;
const [REx, REy] = FACE.RE;
const [MOx, MOy] = FACE.MO;

const MC = '#22CCFF'; // micro-expression LED teal

// ── 8 Interaction Expressions ────────────────────────────────────

const INTERACTION_EXPRS = [
  {
    id: 'idle',       label: '😐 待机',
    color: '#00FFEE', category: 'interaction', canBlink: true,
    leftEye: femEye(LEx, LEy), rightEye: femEye(REx, REy),
    mouth: smileMouth(MOx, MOy),
  },
  {
    id: 'listening',  label: '👂 聆听',
    color: '#44AAFF', category: 'interaction', canBlink: true,
    leftEye: wideEye(LEx, LEy), rightEye: wideEye(REx, REy),
    mouth: arcPts(MOx, MOy, 3, 0, 180),
  },
  {
    id: 'thinking',   label: '🤔 思考',
    color: '#FFCC00', category: 'interaction', canBlink: true,
    leftEye: squintEye(LEx, LEy), rightEye: femEye(REx, REy),
    mouth: waveLine(MOx, MOy, 5, 1),
    extras: [{ dots: questionMark(LEx - 1, LEy - 6), color: '#FFCC00', alpha: 0.75 }],
  },
  {
    id: 'speaking',   label: '💬 说话',
    color: '#FFFFFF',  pupilColor: '#002233',
    category: 'interaction', canBlink: true,
    leftEye: femEye(LEx, LEy), rightEye: femEye(REx, REy),
    speakingMouth: true,
  },
  {
    id: 'happy',      label: '😊 开心',
    color: '#FF44AA', category: 'interaction', canBlink: true,
    leftEye:  { heart: heart(LEx, LEy) },
    rightEye: { heart: heart(REx, REy) },
    mouth: wideMouth(MOx, MOy),
  },
  {
    id: 'error',      label: '😵 错误',
    color: '#FF4400', category: 'interaction', canBlink: true,
    leftEye:  { spiral: spiral(LEx, LEy, 4, 2) },
    rightEye: { spiral: spiral(REx, REy, 4, 2) },
    mouth: zigzag(MOx, MOy, 5, 1.5),
  },
  {
    id: 'confused',   label: '😕 困惑',
    color: '#BB44FF', category: 'interaction', canBlink: true,
    leftEye: wideEye(LEx, LEy), rightEye: squintEye(REx, REy),
    mouth: waveLine(MOx, MOy, 5, 1.5),
    extras: [{ dots: questionMark(REx + 1, REy - 6), color: '#BB44FF', alpha: 0.75 }],
  },
  {
    id: 'dreaming',   label: '💤 做梦',
    color: '#5566FF', category: 'interaction', canBlink: true,
    leftEye: sleepyEye(LEx, LEy), rightEye: sleepyEye(REx, REy),
    mouth: smileMouth(MOx, MOy),
    extras: [
      { dots: makeZ(24, 10), color: '#5566FF', alpha: 0.85, zzzIndex: 0 },
      { dots: makeZ(28,  7), color: '#7788FF', alpha: 0.75, zzzIndex: 1 },
      { dots: makeZ(32,  4), color: '#99AAFF', alpha: 0.65, zzzIndex: 2 },
    ],
  },
];

// ── 16 Micro Auto-Expressions ─────────────────────────────────────
// All use the same full iris+pupil+lash eye builders as interaction
// expressions, giving them the same visual weight and size.

const AUTO_EXPRS = [
  {
    // Natural resting smile — standard open eyes + gentle upward arc
    id: 'natural_smile', label: '01 自然微笑',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  femEye(LEx, LEy),
    rightEye: femEye(REx, REy),
    mouth: smileMouth(MOx, MOy),
  },
  {
    // Calm / relaxed — half-closed sleepy eyes + neutral straight line
    id: 'calm_relax', label: '02 平静放松',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  sleepyEye(LEx, LEy),
    rightEye: sleepyEye(REx, REy),
    mouth: straightMouth(MOx, MOy),
  },
  {
    // Curious observing — wide-open eyes + medium smile
    id: 'curious_observe', label: '03 好奇观察',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  wideEye(LEx, LEy),
    rightEye: wideEye(REx, REy),
    mouth: arcPts(MOx, MOy, 4, 30, 150, 8),
  },
  {
    // Light joy — happy squint (crescent) + wide smile
    id: 'light_joy', label: '04 轻松愉悦',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  squintEye(LEx, LEy),
    rightEye: squintEye(REx, REy),
    mouth: wideMouth(MOx, MOy),
  },
  {
    // Side-eye thinking — pupils shifted sideways + thinking wave
    id: 'side_think', label: '05 侧目思考',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  sideEye(LEx, LEy, 2),
    rightEye: sideEye(REx, REy, 2),
    mouth: waveLine(MOx, MOy, 4, 1),
  },
  {
    // Warm & friendly — downcast soft gaze + gentle smile
    id: 'warm_friendly', label: '06 温和友善',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  downcastEye(LEx, LEy),
    rightEye: downcastEye(REx, REy),
    mouth: smileMouth(MOx, MOy),
  },
  {
    // Quietly listening — attentive standard eyes + neutral mouth
    id: 'quiet_listen', label: '07 静静聆听',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  femEye(LEx, LEy),
    rightEye: femEye(REx, REy),
    mouth: hLine(MOx, MOy, 4),
  },
  {
    // Focused gaze — wide intent eyes + flat straight mouth
    id: 'focus_gaze', label: '08 专注注视',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  wideEye(LEx, LEy),
    rightEye: wideEye(REx, REy),
    mouth: straightMouth(MOx, MOy),
  },
  {
    // Quietly waiting — sleepy patient eyes + tiny patient smile
    id: 'quiet_wait', label: '09 安静等待',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  sleepyEye(LEx, LEy),
    rightEye: sleepyEye(REx, REy),
    mouth: arcPts(MOx, MOy, 4, 30, 150, 8),
  },
  {
    // Slow blink / drowsy — happy squint + content smile
    id: 'slow_blink', label: '10 慢眨眼',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  squintEye(LEx, LEy),
    rightEye: squintEye(REx, REy),
    mouth: smileMouth(MOx, MOy),
  },
  {
    // Look around — pupils shifted left + neutral mouth
    id: 'look_around', label: '11 左右扫视',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  sideEye(LEx, LEy, -2),
    rightEye: sideEye(REx, REy, -2),
    mouth: hLine(MOx, MOy, 4),
  },
  {
    // Playful — one wink + wide eye + smirk
    id: 'playful', label: '12 小小调皮',
    color: MC, category: 'auto', canBlink: false,
    leftEye:  winkEye(LEx, LEy),
    rightEye: wideEye(REx, REy),
    mouth: smirkMouth(MOx, MOy),
  },
  {
    // Joyful & expectant — big wide excited eyes + wide smile
    id: 'joyful_wait', label: '13 愉悦期待',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  wideEye(LEx, LEy),
    rightEye: wideEye(REx, REy),
    mouth: wideMouth(MOx, MOy),
  },
  {
    // Slight surprise — wide eyes + small open-O mouth
    id: 'slight_surprise', label: '14 轻微惊喜',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  wideEye(LEx, LEy),
    rightEye: wideEye(REx, REy),
    mouth: arcPts(MOx, MOy, 3, 0, 180, 10),
  },
  {
    // Gentle gaze — downcast warm eyes + soft small smile
    id: 'gentle_gaze', label: '15 温柔注视',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  downcastEye(LEx, LEy),
    rightEye: downcastEye(REx, REy),
    mouth: arcPts(MOx, MOy, 4, 30, 150, 8),
  },
  {
    // Spaced out / daydreaming — very droopy sleepy eyes + pouty mouth
    id: 'spaceout', label: '16 发呆放空',
    color: MC, category: 'auto', canBlink: true,
    leftEye:  sleepyEye(LEx, LEy),
    rightEye: sleepyEye(REx, REy),
    mouth: poutyMouth(MOx, MOy),
  },
];

// ── Exports ───────────────────────────────────────────────────────

export const EXPRESSIONS  = [...INTERACTION_EXPRS, ...AUTO_EXPRS];
export const ALL_AUTO_IDS = AUTO_EXPRS.map(e => e.id);
export const byId = Object.fromEntries(EXPRESSIONS.map(e => [e.id, e]));
