#!/usr/bin/env bash
#
# Shared FFmpeg build recipe (GPL + libx264 + libmp3lame, fully static, no nonfree, no x265).
# Builds x264 (static) -> lame (static) -> ffmpeg (static) from the pinned sources in
# ffmpeg-build.config.json, then writes the binary + provenance manifest into
# resources/bin/<platform>/<arch>/.
#
# Invoked by build-ffmpeg-mac.sh (native) and build-ffmpeg-win.ps1 (via MSYS2/MinGW).
#
# Usage: build-ffmpeg-core.sh <platform: mac|win> <arch: arm64|x64>
set -euo pipefail

PLATFORM="${1:?platform arg required (mac|win)}"
ARCH="${2:?arch arg required (arm64|x64)}"

# Compute REPO_ROOT in a form both bash and the host's node.exe can read. On
# MSYS2/MinGW (Windows), `pwd` returns /c/... but Windows node.exe needs C:/...;
# `pwd -W` is MSYS-specific and returns the mixed Windows form, which both bash
# builtins and node accept.
if [[ "$PLATFORM" == "win" ]]; then
  REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -W)"
else
  REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
fi
CONFIG="$REPO_ROOT/ffmpeg-build.config.json"
OUT_DIR="$REPO_ROOT/resources/bin/$PLATFORM/$ARCH"
MANIFEST="$OUT_DIR/.ffmpeg-build-manifest.json"
BIN_NAME="ffmpeg"
[[ "$PLATFORM" == "win" ]] && BIN_NAME="ffmpeg.exe"

# --- read the version pointer (node is always present in this repo) ---
cfg() { node -e "const c=require('$CONFIG'); process.stdout.write(String($1))"; }
FFMPEG_VERSION="$(cfg 'c.ffmpeg.version')"
FFMPEG_URL="$(cfg 'c.ffmpeg.url')"
FFMPEG_SHA="$(cfg 'c.ffmpeg.sha256||""')"
X264_COMMIT="$(cfg 'c.x264.commit')"
X264_URL="$(cfg 'c.x264.url')"
LAME_VERSION="$(cfg 'c.lame.version')"
LAME_URL="$(cfg 'c.lame.url')"
LAME_SHA="$(cfg 'c.lame.sha256||""')"
CONFIGURE_FLAGS="$(cfg 'c.configureFlags.join(" ")')"

echo "==> Building FFmpeg $FFMPEG_VERSION ($PLATFORM/$ARCH) — x264@${X264_COMMIT:0:10}, lame $LAME_VERSION"
echo "    configure: $CONFIGURE_FLAGS"

# --- toolchain check ---
MISSING=()
for t in nasm pkg-config make git curl tar; do
  command -v "$t" >/dev/null 2>&1 || MISSING+=("$t")
done
if [[ "$PLATFORM" == "mac" ]]; then
  command -v clang >/dev/null 2>&1 || MISSING+=("clang")
fi
if ((${#MISSING[@]})); then
  echo "ERROR: missing build tools: ${MISSING[*]}" >&2
  [[ "$PLATFORM" == "mac" ]] && echo "Install with: brew install ${MISSING[*]}" >&2
  exit 1
fi

WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
PREFIX="$WORK/prefix"
mkdir -p "$PREFIX/lib/pkgconfig"
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig"
JOBS="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)"

EXTRA_CFLAGS="-I$PREFIX/include"
EXTRA_LDFLAGS="-L$PREFIX/lib"
if [[ "$PLATFORM" == "mac" ]]; then
  # Set the deployment target so the binary (and its codesignature) is accepted on
  # the same macOS floor as the app; clang honours this env var.
  export MACOSX_DEPLOYMENT_TARGET="11.0"
elif [[ "$PLATFORM" == "win" ]]; then
  # Without -static, mingw-w64 leaves an import-dependency on libgcc_s_seh-1.dll,
  # libstdc++-6.dll, libwinpthread-1.dll, libbz2-1.dll, libiconv-2.dll, liblzma-5.dll,
  # zlib1.dll — none of which exist on a clean Windows box, so ffmpeg.exe exits with
  # STATUS_DLL_NOT_FOUND. FFmpeg's --enable-static / --disable-shared only static-link
  # FFmpeg's *internal* libs; this -static covers the toolchain runtime as well.
  EXTRA_LDFLAGS="$EXTRA_LDFLAGS -static"
fi

sha256_of() {
  # macOS ships `shasum`; MSYS2/Linux ship `sha256sum`. Pick whichever is present.
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
  else
    shasum -a 256 "$1" | awk '{print $1}'
  fi
}

# Verify against a pinned sha256 if set; otherwise record (trust-on-first-use) and
# echo the actual hash so the manifest can capture it.
check_sha() {
  local file="$1" expected="$2" label="$3" actual
  actual="$(sha256_of "$file")"
  if [[ -n "$expected" && "$expected" != "null" ]]; then
    if [[ "$actual" != "$expected" ]]; then
      echo "ERROR: $label sha256 mismatch (expected $expected, got $actual)" >&2
      exit 1
    fi
    echo "    ✓ $label sha256 verified" >&2
  else
    echo "    • $label sha256 (not pinned; recorded): $actual" >&2
  fi
  printf '%s' "$actual"
}

cd "$WORK"

# ── 1. x264 (static, GPL) ─────────────────────────────────────────────
echo "==> x264"
git clone --quiet "$X264_URL" x264
git -C x264 checkout --quiet "$X264_COMMIT"
(
  cd x264
  ./configure --prefix="$PREFIX" --enable-static --enable-pic --disable-cli --disable-opencl
  make -j"$JOBS"
  make install
)

# ── 2. lame (static, LGPL) ────────────────────────────────────────────
echo "==> lame"
curl -fsSL "$LAME_URL" -o lame.tar.gz
LAME_SRC_SHA="$(check_sha lame.tar.gz "$LAME_SHA" lame)"
mkdir -p lame && tar xzf lame.tar.gz -C lame --strip-components=1
# Well-known lame 3.100 fix: drop the stale symbol that breaks the build on modern toolchains.
sed -i.bak '/lame_init_old/d' lame/include/libmp3lame.sym 2>/dev/null || true
(
  cd lame
  ./configure --prefix="$PREFIX" --enable-static --disable-shared --disable-frontend
  make -j"$JOBS"
  make install
)

# ── 3. ffmpeg (static, GPL + libx264 + libmp3lame) ────────────────────
echo "==> ffmpeg"
curl -fsSL "$FFMPEG_URL" -o ffmpeg.tar.xz
FFMPEG_SRC_SHA="$(check_sha ffmpeg.tar.xz "$FFMPEG_SHA" ffmpeg)"
mkdir -p ffmpeg && tar xJf ffmpeg.tar.xz -C ffmpeg --strip-components=1
(
  cd ffmpeg
  # shellcheck disable=SC2086
  ./configure --prefix="$PREFIX" $CONFIGURE_FLAGS \
    --pkg-config-flags="--static" \
    --extra-cflags="$EXTRA_CFLAGS" \
    --extra-ldflags="$EXTRA_LDFLAGS"
  make -j"$JOBS"
)

# ── 4. install binary + write provenance manifest ────────────────────
mkdir -p "$OUT_DIR"
cp "ffmpeg/$BIN_NAME" "$OUT_DIR/$BIN_NAME"
chmod +x "$OUT_DIR/$BIN_NAME"

BIN_SHA="$(sha256_of "$OUT_DIR/$BIN_NAME")"
BUILDCONF="$("$OUT_DIR/$BIN_NAME" -hide_banner -buildconf 2>/dev/null || true)"

FF_VER="$FFMPEG_VERSION" X264_C="$X264_COMMIT" LAME_VER="$LAME_VERSION" \
PLAT="$PLATFORM" ARCH_="$ARCH" BIN_SHA="$BIN_SHA" \
FF_SRC_SHA="$FFMPEG_SRC_SHA" LAME_SRC_SHA="$LAME_SRC_SHA" \
BUILDCONF="$BUILDCONF" CFG_FLAGS="$CONFIGURE_FLAGS" OUT="$MANIFEST" \
node -e '
const fs = require("fs")
const m = {
  manifestVersion: 1,
  ffmpegVersion: process.env.FF_VER,
  x264Commit: process.env.X264_C,
  lameVersion: process.env.LAME_VER,
  configureFlags: process.env.CFG_FLAGS.split(" ").filter(Boolean),
  platform: process.env.PLAT,
  arch: process.env.ARCH_,
  binarySha256: process.env.BIN_SHA,
  sources: {
    ffmpegSha256: process.env.FF_SRC_SHA,
    lameSha256: process.env.LAME_SRC_SHA
  },
  ffmpegConfiguration: process.env.BUILDCONF,
  builtAt: new Date().toISOString()
}
fs.writeFileSync(process.env.OUT, JSON.stringify(m, null, 2) + "\n")
'

echo ""
echo "==> Done: $OUT_DIR/$BIN_NAME"
echo "    sha256: $BIN_SHA"
echo "    manifest: $MANIFEST"
