// Math space: long axis on x, point at +x, y upward.

// Lamé, 1818: |x/a|^n + |y/b|^n = 1
window.lameEgg = function lameEgg({ a = 1, b = 0.72, n = 2.5 }, steps = 240) {
  const pts = [];
  for (let i = 0; i <= steps; i++) {
    const t = (i / steps) * Math.PI * 2;
    const ct = Math.cos(t);
    const st = Math.sin(t);
    const x = a * Math.sign(ct) * Math.pow(Math.abs(ct), 2 / n);
    const y = b * Math.sign(st) * Math.pow(Math.abs(st), 2 / n);
    pts.push({ x, y });
  }
  return pts;
};

// Hügelschäffer, 1944: y² = (a²b² − b²x²) / (a² + w² + 2wx)
window.hugelschafferEgg = function hugelschafferEgg({ a = 1, b = 0.72, w = 0.15 }, steps = 240) {
  const pts = [];
  for (let i = 0; i <= steps; i++) {
    const x = -a + (2 * a * i) / steps;
    const denom = a * a + w * w + 2 * w * x;
    const num = a * a * b * b - b * b * x * x;
    const y2 = num / denom;
    const y = y2 > 0 ? Math.sqrt(y2) : 0;
    pts.push({ x, y });
  }
  for (let i = steps; i >= 0; i--) {
    const x = -a + (2 * a * i) / steps;
    const denom = a * a + w * w + 2 * w * x;
    const num = a * a * b * b - b * b * x * x;
    const y2 = num / denom;
    const y = y2 > 0 ? -Math.sqrt(y2) : 0;
    pts.push({ x, y });
  }
  return pts;
};

// Preston, 1953: y = b·sinθ·(1 + c₁cosθ + c₂cos²θ + c₃cos³θ)
window.prestonEgg = function prestonEgg(
  { a = 1, b = 0.72, c1 = 0.12, c2 = -0.05, c3 = 0.0 },
  steps = 240
) {
  const pts = [];
  for (let i = 0; i <= steps; i++) {
    const t = (i / steps) * Math.PI * 2;
    const x = a * Math.cos(t);
    const shape = 1 + c1 * Math.cos(t) + c2 * Math.cos(t) * Math.cos(t) + c3 * Math.pow(Math.cos(t), 3);
    const y = b * Math.sin(t) * shape;
    pts.push({ x: -x, y });
  }
  return pts;
};

// Visual approximation of Narushin's universal egg: a Hügelschäffer base
// with a logistic pyriform adjustment. `p` is an interface parameter, not D_L4.
window.narushinEgg = function narushinEgg(
  { L = 2, B = 1.44, w = 0.15, p = 0.0 },
  steps = 240
) {
  const a = L / 2;
  const b = B / 2;
  const halfY = (x) => {
    const denom = a * a + w * w + 2 * w * x;
    const num = a * a * b * b - b * b * x * x;
    const y2 = num / denom;
    return y2 > 0 ? Math.sqrt(y2) : 0;
  };
  const pyri = (x) => {
    const t = (x + a) / (2 * a);
    const s = 1 / (1 + Math.exp(-10 * (t - 0.65)));
    return 1 - p * s;
  };

  const pts = [];
  for (let i = 0; i <= steps; i++) {
    const x = -a + (2 * a * i) / steps;
    const y = halfY(x) * pyri(x);
    pts.push({ x, y });
  }
  for (let i = steps; i >= 0; i--) {
    const x = -a + (2 * a * i) / steps;
    const y = -halfY(x) * pyri(x);
    pts.push({ x, y });
  }
  return pts;
};

window.pointsToPath = function pointsToPath(pts, scale = 80, cx = 100, cy = 100) {
  if (!pts.length) return '';
  let d = '';
  for (let i = 0; i < pts.length; i++) {
    const p = pts[i];
    const sx = cx + p.x * scale;
    const sy = cy - p.y * scale;
    d += (i === 0 ? 'M' : 'L') + sx.toFixed(2) + ',' + sy.toFixed(2) + ' ';
  }
  d += 'Z';
  return d;
};

window.eggBounds = function eggBounds(pts) {
  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
  for (const p of pts) {
    if (p.x < minX) minX = p.x;
    if (p.x > maxX) maxX = p.x;
    if (p.y < minY) minY = p.y;
    if (p.y > maxY) maxY = p.y;
  }
  return { minX, maxX, minY, maxY, w: maxX - minX, h: maxY - minY };
};

// Volume and surface area treat the profile as a solid of revolution.
window.eggMetrics = function eggMetrics(pts) {
  const b = window.eggBounds(pts);
  const half = pts.filter((p) => p.y >= 0).sort((a, b) => a.x - b.x);
  let V = 0;
  let A = 0;
  let maxY = 0, argmaxX = 0;
  for (let i = 1; i < half.length; i++) {
    const p0 = half[i - 1];
    const p1 = half[i];
    const dx = p1.x - p0.x;
    if (dx <= 0) continue;
    const yAvg = (p0.y + p1.y) / 2;
    V += Math.PI * yAvg * yAvg * dx;
    const dy = p1.y - p0.y;
    const dl = Math.sqrt(dx * dx + dy * dy);
    A += 2 * Math.PI * yAvg * dl;
    if (p1.y > maxY) { maxY = p1.y; argmaxX = p1.x; }
  }
  const L = b.w;
  const B = 2 * maxY;
  const midX = (b.minX + b.maxX) / 2;
  const asym = (argmaxX - midX) / (L / 2);
  const elongation = L / B;
  return { V, A, L, B, elongation, asymmetry: asym, argmaxX };
};
