// Орбитальная геометрия: трассы КА (подспутниковая точка), полосы обзора (swath),
// контуры маршрутов съёмки, участки сброса, терминатор день/ночь.
const DEG = Math.PI / 180;
const RAD = 180 / Math.PI;
const RE_KM = 6371;
const MU = 398600.4418;            // км^3/с^2
const SIDEREAL = 86164;            // период вращения Земли, с

// Детерминированный хэш строки → [0,1).
function hash01(str, salt) {
  let h = 2166136261 ^ (salt || 0);
  for (let i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = Math.imul(h, 16777619); }
  return ((h >>> 0) % 100000) / 100000;
}

// Параметры орбиты КА (ССО). Высоту берём из sc.orbit, наклонение ~98°.
function orbitParams(sc) {
  const m = /(\d+)\s*км/.exec(sc.orbit);
  const h = m ? +m[1] : 550;
  const a = RE_KM + h;
  const T = 2 * Math.PI * Math.sqrt((a * a * a) / MU); // период, с
  const incl = (97.4 + hash01(sc.id, 7) * 1.6) * DEG;  // 97.4–99.0°
  const node0 = hash01(sc.id, 3) * 360 * DEG;          // долгота восх. узла @ эпоха
  const u0 = hash01(sc.id, 11) * 2 * Math.PI;          // фаза
  // Ширина полосы обзора (км) по типу аппаратуры.
  const swathKm = sc.type === "radar" ? 90 + hash01(sc.id, 5) * 110
    : sc.type === "combo" ? 50 + hash01(sc.id, 5) * 70
    : 24 + hash01(sc.id, 5) * 36;
  return { h, a, T, incl, node0, u0, swathKm };
}

const EPOCH = (window.MissionData ? window.MissionData.RANGE_START : 0);

// Подспутниковая точка в момент t (мс).
function subPoint(p, tMs) {
  const dt = (tMs - EPOCH) / 1000; // с
  const u = p.u0 + 2 * Math.PI * (dt / p.T);
  const lat = Math.asin(Math.sin(p.incl) * Math.sin(u)) * RAD;
  let lonRel = Math.atan2(Math.cos(p.incl) * Math.sin(u), Math.cos(u));
  let lon = (p.node0 + lonRel) * RAD - (360 / SIDEREAL) * dt;
  lon = ((((lon + 180) % 360) + 360) % 360) - 180;
  // курс (для перпендикуляра swath) — численно
  return { lat, lon, u };
}

// Трасса за [t0,t1] с адаптивным шагом. Возвращает сегменты (разрыв на ±180°).
function groundTrack(p, t0, t1, maxPts) {
  const span = t1 - t0;
  const idealStep = p.T * 1000 / 36;             // ~36 точек на виток
  let step = idealStep;
  const cap = maxPts || 900;
  if (span / step > cap) step = span / cap;
  const segs = [];
  let cur = [];
  let prevLon = null;
  for (let t = t0; t <= t1; t += step) {
    const s = subPoint(p, t);
    if (prevLon !== null && Math.abs(s.lon - prevLon) > 180) {
      if (cur.length) segs.push(cur);
      cur = [];
    }
    cur.push({ t, lat: s.lat, lon: s.lon });
    prevLon = s.lon;
  }
  if (cur.length) segs.push(cur);
  return segs;
}

// Точки трассы строго внутри [t0,t1] (для footprint работы), без разрыва.
function trackPoints(p, t0, t1, n) {
  const pts = [];
  const k = Math.max(2, n || 24);
  for (let i = 0; i <= k; i++) {
    const t = t0 + (t1 - t0) * (i / k);
    const s = subPoint(p, t);
    pts.push({ t, lat: s.lat, lon: s.lon });
  }
  return pts;
}

// Лента (ribbon) шириной halfKm по обе стороны трассы → замкнутый полигон [lon,lat].
function ribbon(points, halfKm) {
  const half = halfKm / 111; // градусы широты
  const left = [], right = [];
  for (let i = 0; i < points.length; i++) {
    const a = points[Math.max(0, i - 1)];
    const b = points[Math.min(points.length - 1, i + 1)];
    const cosLat = Math.cos(points[i].lat * DEG) || 0.01;
    let dx = (b.lon - a.lon) * cosLat;
    let dy = (b.lat - a.lat);
    const len = Math.hypot(dx, dy) || 1;
    dx /= len; dy /= len;
    // перпендикуляр
    const px = -dy, py = dx;
    const p = points[i];
    left.push([p.lon + (px * half) / cosLat, p.lat + py * half]);
    right.push([p.lon - (px * half) / cosLat, p.lat - py * half]);
  }
  return left.concat(right.reverse());
}

// Видимость станции из подспутниковой точки (грубо: по угловому расстоянию).
function nearestStation(lat, lon, stations) {
  let best = null, bd = 1e9;
  for (const st of stations) {
    const d = Math.hypot((st.lon - lon) * Math.cos(lat * DEG), st.lat - lat);
    if (d < bd) { bd = d; best = st; }
  }
  return { st: best, distDeg: bd };
}

// Подсолнечная точка (примитивно) для терминатора в момент tMs.
function subsolar(tMs) {
  const d = new Date(tMs);
  const dayMs = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
  const secs = (tMs - dayMs) / 1000;
  const lon = 180 - (secs / 86400) * 360;       // движется на запад
  const N = Math.floor((tMs - Date.UTC(d.getUTCFullYear(), 0, 0)) / 86400000);
  const decl = 23.44 * Math.sin(DEG * (360 / 365) * (N - 81)); // склонение
  return { lon: ((lon + 180) % 360 + 360) % 360 - 180, lat: decl };
}

// Кривая терминатора: для каждого lon — широта раздела день/ночь.
function terminatorCurve(tMs) {
  const ss = subsolar(tMs);
  const decl = ss.lat * DEG;
  const out = [];
  for (let lon = -180; lon <= 180; lon += 4) {
    const H = (lon - ss.lon) * DEG;
    // граница: tan(lat) = -cos(H)/tan(decl)
    let lat;
    if (Math.abs(decl) < 0.01) lat = 0;
    else lat = Math.atan(-Math.cos(H) / Math.tan(decl)) * RAD;
    out.push([lon, lat]);
  }
  return { curve: out, nightInNorth: ss.lat < 0, subsolar: ss };
}

Object.assign(window, {
  orbitParams, subPoint, groundTrack, trackPoints, ribbon,
  nearestStation, subsolar, terminatorCurve, DEG, RAD,
});
