/**
 * motionphoto.js — 华为/Google 动态照片（Live Photo / Motion Photo）解析拆分
 *
 * 华为动态照片：JPEG 文件本体 + 在 JPEG EOI 之后追加的完整 MP4 视频。
 * Google Motion Photo：JPEG + 追加 MP4（XMP 中记录偏移）。
 *
 * 纯字节操作，浏览器/Node 通用。
 */

const TYPE_SIZES = { 1: 1, 2: 1, 3: 2, 4: 4, 5: 8, 6: 1, 7: 1, 8: 2, 9: 4, 10: 8, 11: 4, 12: 8 };

function readU32BE(b, o) {
  return ((b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]) >>> 0;
}
function fourCC(b, o) {
  return String.fromCharCode(b[o], b[o + 1], b[o + 2], b[o + 3]);
}

/** 在文件中查找所有 box 对齐的 ftyp（即 MP4 起始） */
function findMp4Starts(bytes) {
  const starts = [];
  const len = bytes.length;
  for (let i = 4; i + 8 <= len; i++) {
    if (
      bytes[i] === 0x66 && bytes[i + 1] === 0x74 && // 'ft'
      bytes[i + 2] === 0x79 && bytes[i + 3] === 0x70    // 'yp'
    ) {
      const boxStart = i - 4;
      if (boxStart < 0) continue;
      const size = readU32BE(bytes, boxStart);
      // 合理性检查：size 至少覆盖 ftyp box 本身，且不超过文件末尾（部分文件有少量尾随字节，容忍 16 字节）
      if (size >= 8 && boxStart + size <= len + 16) {
        starts.push({ start: boxStart, brand: fourCC(bytes, i + 4) });
      }
    }
  }
  return starts;
}

/** 从 JPEG 提取 XMP（APP1 http://ns.adobe.com/xap/1.0/） */
function extractXmp(jpeg) {
  if (jpeg[0] !== 0xff || jpeg[1] !== 0xd8) return null;
  let p = 2;
  while (p + 4 <= jpeg.length) {
    if (jpeg[p] !== 0xff) break;
    const marker = jpeg[p + 1];
    if (marker === 0xd8 || (marker >= 0xd0 && marker <= 0xd7) || marker === 0x01) { p += 2; continue; }
    if (marker === 0xda) break; // SOS，后面是图像数据
    const segLen = (jpeg[p + 2] << 8) | jpeg[p + 3];
    if (marker === 0xe1) {
      // 检查 XMP 标识
      const head = String.fromCharCode(...jpeg.slice(p + 4, p + 4 + 29));
      if (head === 'http://ns.adobe.com/xap/1.0/\0') {
        return new TextDecoder().decode(jpeg.slice(p + 33, p + 2 + segLen));
      }
    }
    p += 2 + segLen;
  }
  return null;
}

/** 解析 Google Motion Photo XMP 中的视频偏移 */
function parseGoogleMotionPhotoXmp(xmp) {
  if (!xmp) return null;
  const mOffset = xmp.match(/GCamera:MicroVideoOffset\s*=\s*["']?(\d+)["']?/);
  if (mOffset) {
    const offset = parseInt(mOffset[1], 10); // 相对文件末尾的长度
    return { type: 'google', offset };
  }
  // Container:Directory 形式（新 Motion Photo 格式）
  if (xmp.includes('GCamera:Container:Directory')) {
    const mLen = xmp.match(/GCamera:Container:Item(?:[^>]*?)Length\s*=\s*["']?(\d+)["']?/);
    const mItem = xmp.match(/Item\s+semantic="MotionPhoto"\s+length="(\d+)"/) ||
      xmp.match(/length="(\d+)"[^>]*semantic="MotionPhoto"/);
    if (mLen || mItem) {
      const length = parseInt((mItem || mLen)[1], 10);
      return { type: 'google-container', length };
    }
  }
  if (xmp.includes('Camera:MicroVideo')) {
    const mOff = xmp.match(/Camera:MicroVideoOffset\s*=\s*["']?(\d+)["']?/);
    if (mOff) return { type: 'google', offset: parseInt(mOff[1], 10) };
  }
  return null;
}

/**
 * 解析动态照片文件
 * @param {Uint8Array} bytes
 * @returns {{ isLivePhoto: boolean, reason?: string, image?: Uint8Array, video?: Uint8Array, videoBrand?: string, format?: string }}
 */
export function parseMotionPhoto(bytes) {
  if (!bytes || bytes.length < 16) {
    return { isLivePhoto: false, reason: '文件过小或为空' };
  }
  // 必须是 JPEG
  if (!(bytes[0] === 0xff && bytes[1] === 0xd8)) {
    return { isLivePhoto: false, reason: '不是 JPEG 文件（华为动态照片通常为 .jpg / .jpeg）' };
  }

  // 1) 找 MP4 起点
  const candidates = findMp4Starts(bytes);
  if (candidates.length === 0) {
    return { isLivePhoto: false, reason: '未检测到内嵌视频（可能只是普通照片）' };
  }

  // 优先选能覆盖到文件末尾的、最后一个候选
  let chosen = null;
  for (const c of candidates) {
    if (chosen === null || c.start > chosen.start) chosen = c;
  }

  // 2) Google XMP 偏移校验（若存在则优先使用）
  let videoStart = chosen.start;
  let format = `huawei/${chosen.brand}`;
  try {
    const xmp = extractXmp(bytes.subarray(0, Math.min(bytes.length, videoStart)));
    const gp = parseGoogleMotionPhotoXmp(xmp);
    if (gp) {
      if (gp.type === 'google' && gp.offset > 0 && gp.offset < bytes.length) {
        const s = bytes.length - gp.offset;
        if (s > 0 && s < bytes.length) { videoStart = s; format = 'google-motion'; }
      } else if (gp.length > 0 && gp.length < bytes.length) {
        const s = bytes.length - gp.length;
        if (s > 0) { videoStart = s; format = 'google-container'; }
      }
    }
  } catch (e) { /* XMP 解析失败不影响主流程 */ }

  // 3) 图片部分 = videoStart 之前；同时截去 JPEG 尾部可能的填充
  let imageEnd = videoStart;
  // 从 videoStart 向前找 JPEG EOI (FFD9)，收紧图片边界
  for (let i = videoStart - 2; i >= Math.max(0, videoStart - 4096); i--) {
    if (bytes[i] === 0xff && bytes[i + 1] === 0xd9) { imageEnd = i + 2; break; }
  }
  if (imageEnd <= 4) imageEnd = videoStart; // 未找到 EOI，退回原始切分

  const image = bytes.slice(0, imageEnd);
  let video = bytes.slice(videoStart);
  video = trimTrailingJunk(video); // 截掉华为 LIVE_ 等末尾非 MP4 标记

  if (video.length < 64) {
    return { isLivePhoto: false, reason: '内嵌视频数据异常（过短）' };
  }

  return {
    isLivePhoto: true,
    image,
    video,
    videoBrand: chosen.brand,
    format,
  };
}

/**
 * 截断动态照片视频末尾的非 MP4 尾随数据。
 * 华为 HarmonyOS 4.2+ 的动态照片在 MP4 之后会追加一段纯文本标记
 * （形如 `v2_f35 409:1000 LIVE_1915884`），这段文本不属于 MP4 容器，
 * 必须截掉，否则下游 MP4 解析器（exiftool / iOS / Synology）会报错。
 *
 * 做法：从 MP4 末尾反向扫描最后一个合法 box 的结束边界，截断其后数据。
 * 仅当末尾确实存在非 box 数据时才截断，纯标准 MP4 不受影响。
 */
function trimTrailingJunk(video) {
  const len = video.length;
  // 正向重建 top-level box 链，得到最后一个合法 box 的结束偏移
  let off = 0;
  let lastBoxEnd = 0;
  while (off + 8 <= len) {
    let size = readU32BE(video, off);
    const type = fourCC(video, off + 4);
    let hdr = 8;
    if (size === 1) {
      if (off + 16 > len) break;
      size = Number((BigInt(readU32BE(video, off + 8)) << 32n) | BigInt(readU32BE(video, off + 12)));
      hdr = 16;
    } else if (size === 0) {
      size = len - off;
    }
    if (size < hdr || off + size > len) break; // 非法 box，停止
    lastBoxEnd = off + size;
    off += size;
  }
  if (lastBoxEnd > 0 && lastBoxEnd < len) {
    return video.slice(0, lastBoxEnd);
  }
  return video;
}

/** 从视频字节解析 MP4/MOV 品牌、时长等信息（轻量解析，仅 top-level） */
export function probeVideo(video) {
  const info = { brand: '', duration: 0, size: video.length, hasMoov: false, moovAtEnd: false };
  let off = 0;
  const len = video.length;
  while (off + 8 <= len) {
    let size = readU32BE(video, off);
    const type = fourCC(video, off + 4);
    let hdr = 8;
    if (size === 1) {
      // 64 位
      size = Number((BigInt(readU32BE(video, off + 8)) << 32n) | BigInt(readU32BE(video, off + 12)));
      hdr = 16;
    } else if (size === 0) {
      size = len - off;
    }
    if (size < 8) break;
    if (type === 'ftyp') info.brand = fourCC(video, off + 8);
    if (type === 'moov') { info.hasMoov = true; }
    if (type === 'mdat') {
      // moov 在 mdat 之后 → moov 位于文件末尾
      info.moovAtEnd = off > 0 && info.hasMoov === false;
    }
    off += size;
  }
  info.moovAtEnd = !info.moovBeforeMdat === undefined ? info.moovAtEnd : info.moovAtEnd;
  return info;
}
