/* geom7_svg.js — библиотека SVG-хелперов для всей Геометрии 7
 * Используется во всех 5 главах. Намеренно без зависимостей.
 *
 * Координатная система SVG: y растёт вниз — мы учитываем это в хелперах углов.
 * Все функции возвращают строку SVG-фрагмента, готовую к вставке в innerHTML.
 *
 * Публичный API: window.GEOM7.{point,segment,ray,line,circle,arc,angle,
 *   rightAngleMark,protractor,polyline,polygon,parallelMark,
 *   midPoint,distance,vec,unit,perp,rotate,svgBox}
 */
(function(){
'use strict';

if (window.GEOM7 && window.GEOM7.__installed) return;
const G = window.GEOM7 = window.GEOM7 || {};
G.__installed = true;

/* === Математика === */
G.distance = (p1, p2) => Math.hypot(p2.x - p1.x, p2.y - p1.y);
G.midPoint = (p1, p2) => ({ x:(p1.x+p2.x)/2, y:(p1.y+p2.y)/2 });
G.vec = (p1, p2) => ({ x:p2.x-p1.x, y:p2.y-p1.y });
G.unit = (v) => { const L = Math.hypot(v.x, v.y) || 1; return { x:v.x/L, y:v.y/L }; };
G.perp = (v) => ({ x:-v.y, y:v.x });
G.rotate = (p, a) => ({ x:p.x*Math.cos(a) - p.y*Math.sin(a), y:p.x*Math.sin(a) + p.y*Math.cos(a) });

/* === SVG-обёртка (даёт область чертежа с фоном/сеткой) === */
G.svgBox = function(w, h, opts){
  opts = opts || {};
  const grid = opts.grid !== false;
  const bg = opts.bg || '#fafafa';
  const cellSize = opts.cell || 20;
  let gridSvg = '';
  if(grid){
    gridSvg = '<defs><pattern id="g7-grid-'+(opts.id||'def')+'" width="'+cellSize+'" height="'+cellSize+'" patternUnits="userSpaceOnUse">'
      +'<path d="M '+cellSize+' 0 L 0 0 0 '+cellSize+'" fill="none" stroke="#e2e8f0" stroke-width="0.6"/>'
      +'</pattern></defs>'
      +'<rect width="'+w+'" height="'+h+'" fill="url(#g7-grid-'+(opts.id||'def')+')"/>';
  }
  return { open:'<svg viewBox="0 0 '+w+' '+h+'" preserveAspectRatio="xMidYMid meet" style="width:100%;height:auto;display:block;margin:'+(opts.margin||'0 auto')+';background:'+bg+';border-radius:10px;border:1px solid #e2e8f0">'+gridSvg, close:'</svg>' };
};

/* === Точка === */
G.point = function(x, y, label, opts){
  opts = opts || {};
  const r = opts.r || 4;
  const color = opts.color || '#d97706';
  const lx = x + (opts.dx !== undefined ? opts.dx : 8);
  const ly = y + (opts.dy !== undefined ? opts.dy : -6);
  let s = '<circle cx="'+x+'" cy="'+y+'" r="'+r+'" fill="'+color+'" stroke="#fff" stroke-width="1.5"/>';
  if(label) s += '<text x="'+lx+'" y="'+ly+'" font-size="'+(opts.fontSize||14)+'" font-family="Unbounded,Inter,sans-serif" font-weight="800" fill="'+(opts.labelColor||color)+'">'+label+'</text>';
  return s;
};

/* === Отрезок === */
G.segment = function(p1, p2, opts){
  opts = opts || {};
  const color = opts.color || '#0891b2';
  const w = opts.width || 2.5;
  let s = '<line x1="'+p1.x+'" y1="'+p1.y+'" x2="'+p2.x+'" y2="'+p2.y+'" stroke="'+color+'" stroke-width="'+w+'" stroke-linecap="round"';
  if(opts.dash) s += ' stroke-dasharray="'+opts.dash+'"';
  s += '/>';
  /* Метки длины (ticks) */
  if(opts.ticks){
    const mid = G.midPoint(p1, p2);
    const v = G.unit(G.vec(p1, p2));
    const n = G.perp(v);
    const tickLen = opts.tickLen || 5;
    for(let i = 0; i < opts.ticks; i++){
      const off = (i - (opts.ticks - 1)/2) * 4;
      const cx = mid.x + v.x * off;
      const cy = mid.y + v.y * off;
      s += '<line x1="'+(cx + n.x * tickLen)+'" y1="'+(cy + n.y * tickLen)+'" x2="'+(cx - n.x * tickLen)+'" y2="'+(cy - n.y * tickLen)+'" stroke="'+color+'" stroke-width="1.5"/>';
    }
  }
  /* Подпись длины */
  if(opts.label){
    const mid = G.midPoint(p1, p2);
    const n = G.perp(G.unit(G.vec(p1, p2)));
    const off = opts.labelOffset || 14;
    s += '<text x="'+(mid.x + n.x * off)+'" y="'+(mid.y + n.y * off + 4)+'" text-anchor="middle" font-size="12" font-family="JetBrains Mono,monospace" fill="'+(opts.labelColor||'#475569')+'" font-weight="600">'+opts.label+'</text>';
  }
  return s;
};

/* === Луч (от p1 в направлении p2, продолжается за p2 до края — приблизительно) === */
G.ray = function(p1, p2, opts){
  opts = opts || {};
  const v = G.unit(G.vec(p1, p2));
  const extend = opts.extend || 400; /* продолжить на N px за p2 */
  const end = { x:p2.x + v.x * extend, y:p2.y + v.y * extend };
  const color = opts.color || '#0891b2';
  const w = opts.width || 2.5;
  return '<line x1="'+p1.x+'" y1="'+p1.y+'" x2="'+end.x+'" y2="'+end.y+'" stroke="'+color+'" stroke-width="'+w+'" stroke-linecap="round"/>';
};

/* === Прямая (бесконечная — продолжается в обе стороны) === */
G.line = function(p1, p2, opts){
  opts = opts || {};
  const v = G.unit(G.vec(p1, p2));
  const extend = opts.extend || 400;
  const start = { x:p1.x - v.x * extend, y:p1.y - v.y * extend };
  const end = { x:p2.x + v.x * extend, y:p2.y + v.y * extend };
  return G.segment(start, end, opts);
};

/* === Окружность === */
G.circle = function(c, r, opts){
  opts = opts || {};
  const color = opts.color || '#7c3aed';
  let s = '<circle cx="'+c.x+'" cy="'+c.y+'" r="'+r+'" fill="'+(opts.fill||'none')+'" stroke="'+color+'" stroke-width="'+(opts.width||2.5)+'"';
  if(opts.dash) s += ' stroke-dasharray="'+opts.dash+'"';
  s += '/>';
  if(opts.center) s += G.point(c.x, c.y, opts.centerLabel || '', { color:color, dx:6, dy:-6 });
  /* Радиус */
  if(opts.radius){
    const angle = (opts.radiusAngle !== undefined) ? opts.radiusAngle : Math.PI / 4;
    const rEnd = { x:c.x + r * Math.cos(angle), y:c.y - r * Math.sin(angle) };
    s += G.segment(c, rEnd, { color:color, width:1.5, dash:'4 3' });
    if(opts.radiusLabel){
      const mid = G.midPoint(c, rEnd);
      s += '<text x="'+(mid.x+8)+'" y="'+(mid.y-4)+'" font-size="11" font-family="JetBrains Mono,monospace" fill="'+color+'" font-weight="600">'+opts.radiusLabel+'</text>';
    }
  }
  return s;
};

/* === Дуга угла (от V с двумя направлениями) ===
 * vAng1, vAng2 — углы в радианах (стандартная мат-конвенция: 0 = вправо, ↑ положительный CCW)
 * НО: в SVG y вниз, поэтому работаем напрямую с углами SVG (0 = вправо, ↑ против часовой если y вверх).
 * Мы передаём углы в SVG-конвенции (для нашей системы — y вниз).
 */
G.arc = function(c, r, a1, a2, opts){
  opts = opts || {};
  const x1 = c.x + r * Math.cos(a1), y1 = c.y + r * Math.sin(a1);
  const x2 = c.x + r * Math.cos(a2), y2 = c.y + r * Math.sin(a2);
  let delta = a2 - a1; while(delta < 0) delta += 2 * Math.PI; while(delta >= 2 * Math.PI) delta -= 2 * Math.PI;
  const large = delta > Math.PI ? 1 : 0;
  const sweep = opts.sweep !== undefined ? opts.sweep : 1;
  const color = opts.color || '#dc2626';
  return '<path d="M '+x1+' '+y1+' A '+r+' '+r+' 0 '+large+' '+sweep+' '+x2+' '+y2+'" fill="'+(opts.fill||'none')+'" stroke="'+color+'" stroke-width="'+(opts.width||2)+'"/>';
};

/* === Угол: вершина V, на которой угол; точки A, B — на сторонах. Возвращает дугу + (опционально) подпись. === */
G.angle = function(V, A, B, opts){
  opts = opts || {};
  const r = opts.r || 24;
  const a1 = Math.atan2(A.y - V.y, A.x - V.x);
  const a2 = Math.atan2(B.y - V.y, B.x - V.x);
  /* Берём кратчайшую дугу */
  let delta = a2 - a1; while(delta > Math.PI) delta -= 2 * Math.PI; while(delta < -Math.PI) delta += 2 * Math.PI;
  const sweep = delta > 0 ? 1 : 0;
  const large = Math.abs(delta) > Math.PI ? 1 : 0;
  const x1 = V.x + r * Math.cos(a1), y1 = V.y + r * Math.sin(a1);
  const x2 = V.x + r * Math.cos(a2), y2 = V.y + r * Math.sin(a2);
  const color = opts.color || '#dc2626';
  let s = '<path d="M '+x1+' '+y1+' A '+r+' '+r+' 0 '+large+' '+sweep+' '+x2+' '+y2+'" fill="'+(opts.fill||'none')+'" stroke="'+color+'" stroke-width="'+(opts.width||2)+'"/>';
  if(opts.label){
    /* Центр подписи — середина ДУГИ, в направлении sweep */
    const midA = a1 + delta / 2;
    const lr = r + (opts.labelOffset || 12);
    const lx = V.x + lr * Math.cos(midA);
    const ly = V.y + lr * Math.sin(midA);
    s += '<text x="'+lx+'" y="'+(ly+5)+'" text-anchor="middle" font-size="'+(opts.fontSize||13)+'" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="'+color+'">'+opts.label+'</text>';
  }
  return s;
};

/* === Маркер прямого угла (L-форма ВНУТРЬ угла) === */
G.rightAngleMark = function(V, P1, P2, opts){
  opts = opts || {};
  const s = opts.size || 12;
  const u1 = G.unit(G.vec(V, P1));
  const u2 = G.unit(G.vec(V, P2));
  const p1 = { x:V.x + s * u1.x, y:V.y + s * u1.y };
  const c  = { x:p1.x + s * u2.x, y:p1.y + s * u2.y };
  const p2 = { x:V.x + s * u2.x, y:V.y + s * u2.y };
  const color = opts.color || '#dc2626';
  return '<polyline points="'+p1.x+','+p1.y+' '+c.x+','+c.y+' '+p2.x+','+p2.y+'" fill="none" stroke="'+color+'" stroke-width="'+(opts.width||1.8)+'"/>';
};

/* === Транспортир (полукруг 180°) === */
G.protractor = function(c, R, opts){
  opts = opts || {};
  const color = opts.color || '#0891b2';
  const fill = opts.fill || '#fef3c7';
  /* Полукруг по горизонтали (повернуть на 180°) */
  let s = '<path d="M '+(c.x - R)+' '+c.y+' A '+R+' '+R+' 0 0 1 '+(c.x + R)+' '+c.y+' Z" fill="'+fill+'" stroke="'+color+'" stroke-width="2"/>';
  /* Деления */
  for(let deg = 0; deg <= 180; deg += 10){
    const a = Math.PI - deg * Math.PI / 180; /* 180° → слева, 0° → справа */
    const r2 = (deg % 30 === 0) ? R - 12 : R - 6;
    const x1 = c.x + R * Math.cos(a), y1 = c.y - R * Math.sin(a);
    const x2 = c.x + r2 * Math.cos(a), y2 = c.y - r2 * Math.sin(a);
    s += '<line x1="'+x1+'" y1="'+y1+'" x2="'+x2+'" y2="'+y2+'" stroke="'+color+'" stroke-width="1.2"/>';
    if(deg % 30 === 0){
      const xl = c.x + (R - 22) * Math.cos(a), yl = c.y - (R - 22) * Math.sin(a);
      s += '<text x="'+xl+'" y="'+(yl+4)+'" text-anchor="middle" font-size="9" font-family="JetBrains Mono,monospace" fill="'+color+'">'+deg+'</text>';
    }
  }
  /* Центральная точка */
  s += G.point(c.x, c.y, '', { r:3, color:color });
  return s;
};

/* === Ломаная === */
G.polyline = function(pts, opts){
  opts = opts || {};
  const color = opts.color || '#0891b2';
  const ptsStr = pts.map(p => p.x + ',' + p.y).join(' ');
  let s = '<polyline points="'+ptsStr+'" fill="none" stroke="'+color+'" stroke-width="'+(opts.width||2.5)+'" stroke-linecap="round" stroke-linejoin="round"';
  if(opts.dash) s += ' stroke-dasharray="'+opts.dash+'"';
  s += '/>';
  if(opts.points) pts.forEach((p, i) => { s += G.point(p.x, p.y, opts.labels ? opts.labels[i] : '', { color:color }); });
  return s;
};

/* === Полигон (замкнутый) === */
G.polygon = function(pts, opts){
  opts = opts || {};
  const color = opts.color || '#0891b2';
  const ptsStr = pts.map(p => p.x + ',' + p.y).join(' ');
  let s = '<polygon points="'+ptsStr+'" fill="'+(opts.fill||'rgba(8,145,178,.10)')+'" stroke="'+color+'" stroke-width="'+(opts.width||2.5)+'" stroke-linejoin="round"/>';
  if(opts.points) pts.forEach((p, i) => { s += G.point(p.x, p.y, opts.labels ? opts.labels[i] : '', { color:color }); });
  return s;
};

/* === Маркер параллельности (стрелки на отрезках) === */
G.parallelMark = function(p1, p2, opts){
  opts = opts || {};
  const count = opts.count || 1;
  const v = G.unit(G.vec(p1, p2));
  const n = G.perp(v);
  const mid = G.midPoint(p1, p2);
  const color = opts.color || '#7c3aed';
  let s = '';
  const arrow = 6;
  for(let i = 0; i < count; i++){
    const off = (i - (count - 1)/2) * 5;
    const cx = mid.x + v.x * off;
    const cy = mid.y + v.y * off;
    /* Стрелка → > указывает в направлении v */
    s += '<polyline points="'+(cx - v.x * arrow - n.x * arrow * 0.7)+','+(cy - v.y * arrow - n.y * arrow * 0.7)+' '+cx+','+cy+' '+(cx - v.x * arrow + n.x * arrow * 0.7)+','+(cy - v.y * arrow + n.y * arrow * 0.7)+'" fill="none" stroke="'+color+'" stroke-width="1.5" stroke-linecap="round"/>';
  }
  return s;
};

/* === KaTeX render (если доступен) — с правильными делимитерами === */
G.renderMath = function(root){
  if(!root || !window.renderMathInElement) return;
  try{
    window.renderMathInElement(root, {
      delimiters: [
        { left:'$$', right:'$$', display:true },
        { left:'$',  right:'$',  display:false },
        { left:'\\[', right:'\\]', display:true },
        { left:'\\(', right:'\\)', display:false }
      ],
      throwOnError: false
    });
  }catch(e){}
};

})();
