/**
 * DJI 영상 카메라 메타데이터 자동 감지
 *
 * DJI 드론 MP4 에는 'djmd' 데이터 트랙(프레임별 protobuf 텔레메트리)이 mdat 에
 * 비디오 프레임과 인터리브되어 있다. 파일 앞부분(8MB)에서 첫 djmd 샘플을 찾아 추출:
 *   - 카메라 모델 문자열 (필드 1.1.2, 예: "DJI ZenmuseP1")
 *   - 렌즈 초점거리 (필드 8.6, 0.1mm 단위 35mm 환산)
 *   - 해상도/fps (필드 1.7/1.6/1.8)
 *
 * 실측 검증(2026-07-09, 제주 데이터): DJI_20260629121835_0002.MP4 →
 *   model "DJI ZenmuseP1", 8.6=299(=29.9mm — DL 24mm 렌즈 × 4K 비디오 크롭 ≈1.25),
 *   3840×2160, 59.94fps. (djmd 필드 6.4/6.5 는 위경도 라디안, 6.6 은 타원체고 mm)
 */

import type { DetectedCameraInfo } from '../types/geo';

const HEAD_BYTES = 8 * 1024 * 1024;

function readVarint(b: Uint8Array, i: number): [number, number] | null {
  let v = 0, s = 0;
  while (i < b.length && (b[i] & 0x80)) {
    v += (b[i] & 0x7f) * 2 ** s;
    s += 7; i++;
    if (s > 42) return null;
  }
  if (i >= b.length) return null;
  v += (b[i] & 0x7f) * 2 ** s;
  return [v, i + 1];
}

/** [start,end) 구간을 protobuf 필드로 파싱해 경로("8.6")→값 수집. 깨진 구간은 조용히 중단. */
function parseFields(
  b: Uint8Array, dv: DataView,
  start: number, end: number,
  prefix: string, out: Map<string, number>,
): void {
  let i = start;
  while (i < end) {
    const kv = readVarint(b, i);
    if (!kv) return;
    const [key, ni] = kv;
    i = ni;
    const tag = key >> 3, wire = key & 7;
    if (tag === 0 || tag > 60) return;
    const path = prefix + tag;
    if (wire === 0) {
      const r = readVarint(b, i); if (!r) return;
      out.set(path, r[0]); i = r[1];
    } else if (wire === 5) {
      if (i + 4 > end) return;
      out.set(path, dv.getFloat32(i, true)); i += 4;
    } else if (wire === 1) {
      if (i + 8 > end) return;
      out.set(path, dv.getFloat64(i, true)); i += 8;
    } else if (wire === 2) {
      const r = readVarint(b, i); if (!r) return;
      const [len, p] = r;
      if (p + len > end) return;
      // 하위 메시지 시도 — 문자열/바이너리면 내부에서 알아서 중단됨(경로 prefix 로 충돌 없음)
      parseFields(b, dv, p, p + len, path + '.', out);
      i = p + len;
    } else return;
  }
}

/** 영상 File 앞부분에서 DJI djmd 카메라 정보를 감지한다. DJI 영상이 아니면 null. */
export async function detectCameraInfo(file: File): Promise<DetectedCameraInfo | null> {
  try {
    const buf = new Uint8Array(await file.slice(0, HEAD_BYTES).arrayBuffer());
    const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);

    // 모델 문자열: 0x12 <len> "DJI ..." (필드 1.1.2)
    let m = -1;
    for (let i = 2; i < buf.length - 4; i++) {
      if (buf[i] === 0x44 && buf[i + 1] === 0x4a && buf[i + 2] === 0x49 && buf[i + 3] === 0x20 &&
          buf[i - 2] === 0x12 && buf[i - 1] >= 4 && buf[i - 1] <= 40) { m = i; break; }
    }
    if (m < 0) return null;
    const model = new TextDecoder().decode(buf.slice(m, m + buf[m - 1]));

    // 샘플 시작(최상위 필드1, 0x0a <varint len> 0x0a ...)을 모델 문자열에서 역방향 탐색
    let sampleStart = -1;
    for (let back = 4; back <= 16; back++) {
      const i = m - 2 - back;
      if (i < 0) break;
      if (buf[i] !== 0x0a) continue;
      const r = readVarint(buf, i + 1);
      if (!r) continue;
      const [len, p] = r;
      if (buf[p] === 0x0a && p < m && p + len > m && p + len <= buf.length) { sampleStart = i; break; }
    }
    if (sampleStart < 0) return { model };

    const out = new Map<string, number>();
    parseFields(buf, dv, sampleStart, Math.min(buf.length, sampleStart + 512), '', out);

    const info: DetectedCameraInfo = { model };
    const focal10 = out.get('8.6');
    if (focal10 !== undefined && focal10 > 80 && focal10 < 3000) info.focalLen35 = focal10 / 10; // 8~300mm 만 신뢰
    const w = out.get('1.7'), h = out.get('1.6'), fps = out.get('1.8');
    if (w && w > 100) info.width = w;
    if (h && h > 100) info.height = h;
    if (typeof fps === 'number' && fps > 1 && fps < 1000) info.fps = Math.round(fps * 100) / 100;
    // 6.6 = 드론 타원체고(mm) — 영상 시작 시점. CSV 정표고와의 차 = 지역 지오이드고(datum 변환용).
    const altMm = out.get('6.6');
    if (typeof altMm === 'number' && altMm > 0 && altMm < 10_000_000) {
      info.ellipsoidalAltM = Math.round(altMm) / 1000;
    }
    return info;
  } catch {
    return null;
  }
}
