// optics.js — модуль хелперов геометрической оптики для учебника Физика 8
// Экспорт в window.OPTICS = { ... }
(function(){
'use strict';

// === Палитра ===
const COLOR = {
  ray:        '#fbbf24',
  rayIncident:'#0891b2',
  rayReflected:'#10b981',
  rayRefracted:'#a855f7',
  normal:     '#94a3b8',
  lensConv:   '#22c55e',
  lensDiv:    '#f97316',
  mirror:     '#475569',
  hatch:      '#94a3b8',
  axis:       '#cbd5e1',
  focus:      '#1d4ed8',
  imageReal:  '#7c3aed',
  imageVirtual:'#a78bfa'
};

// === Стрелка-наконечник (внутренний помощник) ===
function arrowHead(x, y, ux, uy, size, color){
  const w = size * 0.55;
  const px = -uy, py = ux;
  const bx = x - ux*size, by = y - uy*size;
  const lx = bx + px*w,   ly = by + py*w;
  const rx = bx - px*w,   ry = by - py*w;
  return `<polygon points="${x.toFixed(1)},${y.toFixed(1)} ${lx.toFixed(1)},${ly.toFixed(1)} ${rx.toFixed(1)},${ry.toFixed(1)}" fill="${color}"/>`;
}

// === Луч (линия + стрелка посередине) ===
// dashed: true → пунктир (для виртуальных продолжений)
function ray(x1, y1, x2, y2, color, dashed){
  color = color || COLOR.ray;
  const dx = x2 - x1, dy = y2 - y1;
  const len = Math.sqrt(dx*dx + dy*dy);
  if (len < 1e-6) return '';
  const ux = dx/len, uy = dy/len;
  const dash = dashed ? ' stroke-dasharray="6 4"' : '';
  let s = '';
  s += `<line x1="${x1.toFixed(1)}" y1="${y1.toFixed(1)}" x2="${x2.toFixed(1)}" y2="${y2.toFixed(1)}" stroke="${color}" stroke-width="2" stroke-linecap="round"${dash}/>`;
  // Стрелка-наконечник в точке (x2,y2)
  if (!dashed) s += arrowHead(x2, y2, ux, uy, 9, color);
  return s;
}

// === Падающий, отражённый, преломлённый лучи + нормаль ===
// На границе двух сред (горизонтальной) с нормалью вверх
function refractRay(x0, y0, angleInDeg, n1, n2, len){
  // angleInDeg — угол падения от нормали (в градусах)
  // Возвращает SVG: падающий луч (сверху-слева к (x0,y0)) + нормаль + отражённый + преломлённый
  len = len || 80;
  const a1 = angleInDeg * Math.PI / 180;
  const sinA2 = (n1/n2) * Math.sin(a1);
  const tir = Math.abs(sinA2) > 1;  // полное внутреннее отражение
  const a2 = tir ? 0 : Math.asin(sinA2);

  let s = '';
  // Граница
  s += `<line x1="${x0-len*1.5}" y1="${y0}" x2="${x0+len*1.5}" y2="${y0}" stroke="${COLOR.mirror}" stroke-width="1.5"/>`;
  // Нормаль (вверх и вниз пунктиром)
  s += `<line x1="${x0}" y1="${y0-len*0.9}" x2="${x0}" y2="${y0+len*0.9}" stroke="${COLOR.normal}" stroke-width="1.2" stroke-dasharray="5 3"/>`;
  // Падающий луч (приходит сверху-слева)
  const xi = x0 - len*Math.sin(a1), yi = y0 - len*Math.cos(a1);
  s += ray(xi, yi, x0, y0, COLOR.rayIncident);
  // Отражённый луч (вверх-вправо)
  const xr = x0 + len*Math.sin(a1), yr = y0 - len*Math.cos(a1);
  s += ray(x0, y0, xr, yr, COLOR.rayReflected);
  // Преломлённый луч (вниз)
  if (!tir){
    const xt = x0 + len*Math.sin(a2), yt = y0 + len*Math.cos(a2);
    s += ray(x0, y0, xt, yt, COLOR.rayRefracted);
  }
  return { svg: s, a1: a1, a2: a2, tir: tir };
}

// === Отражение от плоского зеркала ===
// Зеркало горизонтальное на y = y0
function reflectRay(x0, y0, angleInDeg, len){
  len = len || 80;
  const a = angleInDeg * Math.PI / 180;
  let s = '';
  // Зеркало
  s += `<line x1="${x0-len*1.4}" y1="${y0}" x2="${x0+len*1.4}" y2="${y0}" stroke="${COLOR.mirror}" stroke-width="2"/>`;
  // Штриховка с обратной стороны
  for (let i = -len; i <= len; i += 8){
    s += `<line x1="${x0+i}" y1="${y0}" x2="${x0+i+5}" y2="${y0+6}" stroke="${COLOR.hatch}" stroke-width="1"/>`;
  }
  // Нормаль
  s += `<line x1="${x0}" y1="${y0-len*0.9}" x2="${x0}" y2="${y0+10}" stroke="${COLOR.normal}" stroke-width="1.2" stroke-dasharray="5 3"/>`;
  // Падающий
  const xi = x0 - len*Math.sin(a), yi = y0 - len*Math.cos(a);
  s += ray(xi, yi, x0, y0, COLOR.rayIncident);
  // Отражённый
  const xr = x0 + len*Math.sin(a), yr = y0 - len*Math.cos(a);
  s += ray(x0, y0, xr, yr, COLOR.rayReflected);
  return s;
}

// === Плоское зеркало (отдельный элемент) ===
function mirrorPlane(x, y, len, angleDeg){
  angleDeg = angleDeg || 0;
  const a = angleDeg * Math.PI / 180;
  const ca = Math.cos(a), sa = Math.sin(a);
  const x1 = x - len/2*ca, y1 = y - len/2*sa;
  const x2 = x + len/2*ca, y2 = y + len/2*sa;
  const nx = -sa, ny = ca;  // нормаль "наружу"
  let s = `<line x1="${x1.toFixed(1)}" y1="${y1.toFixed(1)}" x2="${x2.toFixed(1)}" y2="${y2.toFixed(1)}" stroke="${COLOR.mirror}" stroke-width="2.5"/>`;
  // Штриховка с тыльной стороны
  for (let t = -len/2 + 3; t <= len/2; t += 8){
    const bx = x + t*ca, by = y + t*sa;
    s += `<line x1="${bx.toFixed(1)}" y1="${by.toFixed(1)}" x2="${(bx - nx*6).toFixed(1)}" y2="${(by - ny*6).toFixed(1)}" stroke="${COLOR.hatch}" stroke-width="1"/>`;
  }
  return s;
}

// === Сферическое зеркало (вогнутое/выпуклое) ===
// kind: 'concave' (вогнутое — фокусирует) | 'convex' (выпуклое — рассеивает)
function mirrorSpherical(cx, cy, R, kind, halfH){
  halfH = halfH || R * 0.6;
  const sign = (kind === 'convex') ? -1 : 1;
  // Дуга
  const x1 = cx - sign*R*0.15, y1 = cy - halfH;
  const x2 = cx - sign*R*0.15, y2 = cy + halfH;
  const sweep = sign > 0 ? 0 : 1;
  let s = `<path d="M ${x1} ${y1} A ${R} ${R} 0 0 ${sweep} ${x2} ${y2}" fill="none" stroke="${COLOR.mirror}" stroke-width="2.5"/>`;
  // Главная оптическая ось
  s += `<line x1="${cx-R}" y1="${cy}" x2="${cx+R}" y2="${cy}" stroke="${COLOR.axis}" stroke-width="1" stroke-dasharray="4 3"/>`;
  return s;
}

// === Тонкая линза ===
// kind: 'converging' (двусторонне-выпуклая, |) | 'diverging' (двусторонне-вогнутая, |)
// Возвращает SVG с осью, фокусами F и 2F
function thinLens(cx, cy, halfH, F, kind){
  halfH = halfH || 70;
  F = F || 80;
  const color = (kind === 'diverging') ? COLOR.lensDiv : COLOR.lensConv;
  let s = '';
  // Главная оптическая ось
  s += `<line x1="${cx - 2.5*F}" y1="${cy}" x2="${cx + 2.5*F}" y2="${cy}" stroke="${COLOR.axis}" stroke-width="1.2"/>`;
  // Линза — вертикальный овал
  s += `<line x1="${cx}" y1="${cy-halfH}" x2="${cx}" y2="${cy+halfH}" stroke="${color}" stroke-width="2.5"/>`;
  if (kind === 'diverging'){
    // Стрелки наружу (рассеивающая)
    s += arrowHead(cx, cy - halfH, 0, -1, 10, color);
    s += arrowHead(cx, cy + halfH, 0,  1, 10, color);
  } else {
    // Стрелки внутрь (собирающая) — вершины на оси
    s += arrowHead(cx - 6, cy - halfH + 8, -0.6, -0.8, 10, color);
    s += arrowHead(cx + 6, cy - halfH + 8,  0.6, -0.8, 10, color);
    s += arrowHead(cx - 6, cy + halfH - 8, -0.6,  0.8, 10, color);
    s += arrowHead(cx + 6, cy + halfH - 8,  0.6,  0.8, 10, color);
  }
  // Фокусы F и 2F
  s += `<circle cx="${cx-F}" cy="${cy}" r="3" fill="${COLOR.focus}"/>`;
  s += `<circle cx="${cx+F}" cy="${cy}" r="3" fill="${COLOR.focus}"/>`;
  s += `<text x="${cx-F-3}" y="${cy+18}" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="${COLOR.focus}">F</text>`;
  s += `<text x="${cx+F-3}" y="${cy+18}" font-family="JetBrains Mono,monospace" font-size="12" font-weight="700" fill="${COLOR.focus}">F</text>`;
  s += `<circle cx="${cx-2*F}" cy="${cy}" r="2.5" fill="${COLOR.focus}" opacity=".6"/>`;
  s += `<circle cx="${cx+2*F}" cy="${cy}" r="2.5" fill="${COLOR.focus}" opacity=".6"/>`;
  s += `<text x="${cx-2*F-5}" y="${cy+18}" font-family="JetBrains Mono,monospace" font-size="11" opacity=".7" fill="${COLOR.focus}">2F</text>`;
  s += `<text x="${cx+2*F-5}" y="${cy+18}" font-family="JetBrains Mono,monospace" font-size="11" opacity=".7" fill="${COLOR.focus}">2F</text>`;
  return s;
}

// === Построение изображения в тонкой линзе ===
// F — фокусное расстояние (положительное для собирающей, отрицательное для рассеивающей)
// d — расстояние от предмета до линзы (положительное)
// h — высота предмета (положительная)
// Возвращает { f: расстояние до изображения, h2: высота, virtual: bool, kind: 'real'|'virtual' }
function buildLensImage(F, d, h){
  // 1/F = 1/d + 1/f  →  f = d·F / (d - F)
  if (Math.abs(d - F) < 1e-6){
    return { f: Infinity, h2: -Infinity, virtual: false, kind: 'infinity' };
  }
  const f = d * F / (d - F);
  const h2 = -h * f / d;  // увеличение: h2/h = -f/d
  const virtual = (F > 0) ? (d < F) : true;  // для рассеивающей всегда мнимое
  return { f: f, h2: h2, virtual: virtual, kind: virtual ? 'virtual' : 'real' };
}

// === Три "золотых" луча для построения изображения ===
// Возвращает SVG трёх лучей: через центр, параллельно оси, через передний фокус.
// objX, objY — координата предмета (вершина стрелки)
// lensX, lensY — центр линзы
// F (px) — фокусное расстояние (в пикселях)
function goldenRays(objX, objY, lensX, lensY, F){
  let s = '';
  const dx = lensX - objX;
  const dy = lensY - objY;
  // Луч 1: параллельно оси → проходит через F с другой стороны
  const y1At = objY;  // приходит на линзу на высоте objY
  s += ray(objX, y1At, lensX, y1At, COLOR.rayIncident);
  s += ray(lensX, y1At, lensX + 2.5*F, lensY + (y1At - lensY) * (-2.5*F)/(F * (objY < lensY ? 1 : -1)), COLOR.rayIncident);
  // Луч 2: через центр — продолжается без преломления
  s += ray(objX, objY, lensX + dx, lensY + dy, COLOR.rayReflected);
  // Луч 3: через передний фокус → выходит параллельно оси
  // (вычислим точку пересечения с линзой)
  const slope = (lensY - objY) / (objX - (lensX - F));
  const yAtLens = lensY - slope * F;  // упрощённо
  s += ray(objX, objY, lensX, yAtLens, COLOR.rayRefracted);
  s += ray(lensX, yAtLens, lensX + 2*F, yAtLens, COLOR.rayRefracted);
  return s;
}

// === Глаз (упрощённая схема) ===
// accommodation: 0..1, где 0 — расслабленный (дальний предмет), 1 — напряжённый (близкий)
function eyeDiagram(cx, cy, R, accommodation){
  accommodation = accommodation || 0;
  let s = '';
  // Глазное яблоко
  s += `<circle cx="${cx}" cy="${cy}" r="${R}" fill="#fff" stroke="#0f172a" stroke-width="1.6"/>`;
  // Роговица — выпуклость спереди (слева)
  s += `<path d="M ${cx-R*0.95} ${cy-R*0.5} Q ${cx-R*1.15} ${cy} ${cx-R*0.95} ${cy+R*0.5}" fill="#bfdbfe" stroke="#0f172a" stroke-width="1.4"/>`;
  // Хрусталик — эллипс изнутри, форма зависит от accommodation
  const lensHalfH = R * 0.45;
  const lensW = R * (0.10 + 0.10 * accommodation);  // толще при напряжении
  s += `<ellipse cx="${cx-R*0.6}" cy="${cy}" rx="${lensW}" ry="${lensHalfH}" fill="#fef3c7" stroke="${COLOR.lensConv}" stroke-width="1.6"/>`;
  // Сетчатка — задняя часть
  s += `<path d="M ${cx+R*0.95} ${cy-R*0.8} A ${R} ${R} 0 0 1 ${cx+R*0.95} ${cy+R*0.8}" fill="none" stroke="${COLOR.imageReal}" stroke-width="2"/>`;
  // Зрительный нерв
  s += `<path d="M ${cx+R} ${cy} L ${cx+R*1.4} ${cy+R*0.3}" stroke="#0f172a" stroke-width="2.5"/>`;
  return s;
}

// === Источник света / предмет ===
// kind: 'point' (точечный звезда) | 'arrow' (вертикальная стрелка-предмет)
function lightObject(x, y, h, kind){
  kind = kind || 'arrow';
  if (kind === 'point'){
    let s = `<circle cx="${x}" cy="${y}" r="6" fill="${COLOR.ray}" stroke="#0f172a" stroke-width="1"/>`;
    // Лучики
    for (let i = 0; i < 8; i++){
      const a = i * Math.PI / 4;
      const x1 = x + 8*Math.cos(a), y1 = y + 8*Math.sin(a);
      const x2 = x + 13*Math.cos(a), y2 = y + 13*Math.sin(a);
      s += `<line x1="${x1.toFixed(1)}" y1="${y1.toFixed(1)}" x2="${x2.toFixed(1)}" y2="${y2.toFixed(1)}" stroke="${COLOR.ray}" stroke-width="1.5" stroke-linecap="round"/>`;
    }
    return s;
  }
  // Стрелка-предмет: основание на оси (y), вершина на (y - h)
  const tipY = y - h;
  const color = '#0f172a';
  let s = `<line x1="${x}" y1="${y}" x2="${x}" y2="${tipY}" stroke="${color}" stroke-width="2.5"/>`;
  s += arrowHead(x, tipY, 0, -1, 9, color);
  return s;
}

// === Тень и полутень ===
// Источник в (sx, sy), непрозрачный объект — отрезок [ox-or .. ox+or] на высоте oy.
// Возвращает SVG полупрозрачных треугольников тени/полутени, падающих на экран y = screenY.
function shadowTriangle(sx, sy, ox, oy, or, screenY){
  // Касательные от источника к краям объекта дают полутень.
  // Лучи через центр объекта дают полную тень.
  // Простейшая реализация для точечного источника: только тень.
  const dy = screenY - sy;
  const k = dy / (oy - sy);
  const x1 = sx + (ox - or - sx) * k;
  const x2 = sx + (ox + or - sx) * k;
  let s = '';
  s += `<polygon points="${(ox-or).toFixed(1)},${oy} ${(ox+or).toFixed(1)},${oy} ${x2.toFixed(1)},${screenY} ${x1.toFixed(1)},${screenY}" fill="rgba(15,23,42,.35)" stroke="none"/>`;
  return s;
}

// === Экспорт ===
window.OPTICS = {
  COLOR: COLOR,
  ray: ray,
  refractRay: refractRay,
  reflectRay: reflectRay,
  mirrorPlane: mirrorPlane,
  mirrorSpherical: mirrorSpherical,
  thinLens: thinLens,
  buildLensImage: buildLensImage,
  goldenRays: goldenRays,
  eyeDiagram: eyeDiagram,
  lightObject: lightObject,
  shadowTriangle: shadowTriangle
};

})();
