/*
 * tools/libde265_glue.c — Shot Ledger 的 libde265 WebAssembly 膠水（SPEC_M63，HEVC 相容模式）。
 *
 * 只把 libde265 的公開 C API 包成「全部是 32 位元整數」的幾個函式（不用 embind、不用 BigInt）：
 *   - 封包順序號（idx）當成 PTS 推進解碼器，解出來的格用 sl_img_idx() 拿回 idx（主程式再對回封包時間戳）。
 *   - sl_img_info() 一次回傳尺寸、色度格式、位元深度與 VUI 色彩資訊（full range、primaries、transfer、matrix）。
 *   - sl_img_i420() 把三個平面（去掉 stride）緊密排成 I420，給瀏覽器的 VideoFrame。
 *   - sl_img_i420p16()（v131，SPEC_M63 第 2 期）：10 位元（Main10、HDR）的三個平面緊密排成 16 位元 I420（VideoFrame 的 I420P10）。
 * libde265 本體一行沒改（LGPL-3.0）。這個檔與 tools/build_libde265.mjs 一起構成重建所需的全部原始碼，
 * 見 js/vendor/libde265/README.md（部署時複製到 /js/vendor/libde265/src/）。
 *
 * This file is part of Shot Ledger's build of libde265 and is distributed under the same terms
 * (GNU LGPL v3 or later), see js/vendor/libde265/COPYING.txt.
 */
#include <stdint.h>
#include <string.h>
#include <emscripten/emscripten.h>
#include "libde265/de265.h"

EMSCRIPTEN_KEEPALIVE void* sl_new(void) {
  de265_decoder_context* c = de265_new_decoder();
  if (c) {
    /* 不要 libde265 自己的多執行緒（沒有 SharedArrayBuffer）；多核由主程式多開 worker 解不同段落 */
    de265_set_verbosity(0);
  }
  return c;
}

EMSCRIPTEN_KEEPALIVE void sl_free(void* c) { if (c) de265_free_decoder((de265_decoder_context*)c); }
EMSCRIPTEN_KEEPALIVE void sl_reset(void* c) { de265_reset((de265_decoder_context*)c); }

/* 一個 NAL（不含長度前綴／起始碼）；idx＝封包順序號（當 PTS 用） */
EMSCRIPTEN_KEEPALIVE int sl_push(void* c, const uint8_t* data, int len, int idx) {
  return (int)de265_push_NAL((de265_decoder_context*)c, data, len, (de265_PTS)idx, NULL);
}

EMSCRIPTEN_KEEPALIVE void sl_end_frame(void* c) { de265_push_end_of_frame((de265_decoder_context*)c); }
EMSCRIPTEN_KEEPALIVE int sl_flush(void* c) { return (int)de265_flush_data((de265_decoder_context*)c); }

/* 回傳錯誤碼；*more 寫回要不要再呼叫 */
EMSCRIPTEN_KEEPALIVE int sl_decode(void* c, int* more) { return (int)de265_decode((de265_decoder_context*)c, more); }
EMSCRIPTEN_KEEPALIVE int sl_is_ok(int err) { return de265_isOK((de265_error)err); }
EMSCRIPTEN_KEEPALIVE const char* sl_err_text(int err) { return de265_get_error_text((de265_error)err); }

EMSCRIPTEN_KEEPALIVE const void* sl_peek(void* c) { return de265_peek_next_picture((de265_decoder_context*)c); }
EMSCRIPTEN_KEEPALIVE void sl_release(void* c) { de265_release_next_picture((de265_decoder_context*)c); }

EMSCRIPTEN_KEEPALIVE int sl_img_idx(const void* img) { return (int)de265_get_image_PTS((const de265_image*)img); }

/* out[0..9]：寬、高、色度格式（0 mono、1 4:2:0、2 4:2:2、3 4:4:4）、亮度位元深度、full range、
 * colour_primaries、transfer_characteristics、matrix_coeffs、色度寬、色度高 */
EMSCRIPTEN_KEEPALIVE void sl_img_info(const void* p, int* out) {
  const de265_image* img = (const de265_image*)p;
  out[0] = de265_get_image_width(img, 0);
  out[1] = de265_get_image_height(img, 0);
  out[2] = (int)de265_get_chroma_format(img);
  out[3] = de265_get_bits_per_pixel(img, 0);
  out[4] = de265_get_image_full_range_flag(img);
  out[5] = de265_get_image_colour_primaries(img);
  out[6] = de265_get_image_transfer_characteristics(img);
  out[7] = de265_get_image_matrix_coefficients(img);
  out[8] = de265_get_image_width(img, 1);
  out[9] = de265_get_image_height(img, 1);
}

/* 8 位元 4:2:0 → 緊密 I420（dst 至少 w*h + 2*cw*ch）。回傳寫了幾個位元組；格式不對回 0 */
EMSCRIPTEN_KEEPALIVE int sl_img_i420(const void* p, uint8_t* dst) {
  const de265_image* img = (const de265_image*)p;
  if (de265_get_chroma_format(img) != de265_chroma_420 || de265_get_bits_per_pixel(img, 0) != 8) return 0;
  int o = 0;
  for (int ch = 0; ch < 3; ch++) {
    int stride = 0;
    const uint8_t* src = de265_get_image_plane(img, ch, &stride);
    int w = de265_get_image_width(img, ch);
    int h = de265_get_image_height(img, ch);
    if (!src || w <= 0 || h <= 0) return 0;
    if (stride == w) {
      memcpy(dst + o, src, (size_t)w * h);
    } else {
      for (int y = 0; y < h; y++) memcpy(dst + o + (size_t)y * w, src + (size_t)y * stride, (size_t)w);
    }
    o += w * h;
  }
  return o;
}

/* 9–16 位元 4:2:0（亮度、色度位元深度一樣）→ 緊密 16 位元 I420（每個樣本 2 bytes、小端，值不縮放：10 位元就是 0–1023）。
 * dst 至少 2*(w*h + 2*cw*ch)。回傳寫了幾個位元組；格式不對回 0。v131（SPEC_M63 第 2 期） */
EMSCRIPTEN_KEEPALIVE int sl_img_i420p16(const void* p, uint8_t* dst) {
  const de265_image* img = (const de265_image*)p;
  int bits = de265_get_bits_per_pixel(img, 0);
  if (de265_get_chroma_format(img) != de265_chroma_420 || bits <= 8 || bits > 16) return 0;
  if (de265_get_bits_per_pixel(img, 1) != bits || de265_get_bits_per_pixel(img, 2) != bits) return 0;
  int o = 0;
  for (int ch = 0; ch < 3; ch++) {
    int stride = 0; /* 位元組 */
    const uint8_t* src = de265_get_image_plane(img, ch, &stride);
    int w = de265_get_image_width(img, ch);
    int h = de265_get_image_height(img, ch);
    if (!src || w <= 0 || h <= 0) return 0;
    int row = w * 2;
    if (stride == row) {
      memcpy(dst + o, src, (size_t)row * h);
    } else {
      for (int y = 0; y < h; y++) memcpy(dst + o + (size_t)y * row, src + (size_t)y * stride, (size_t)row);
    }
    o += row * h;
  }
  return o;
}

EMSCRIPTEN_KEEPALIVE const char* sl_version(void) { return de265_get_version(); }
