#!/bin/bash
#
# Build a minimal Windows x64 FFmpeg for Tagtool's bundled subprocess
# recorder (HAP, HAP Alpha, ProRes 422 HQ, ProRes 4444, Animation/qtrle).
# Designed to run in WSL Ubuntu 24.04 or any Linux box with mingw-w64
# cross-compiler installed.
#
# Output: ~/ffmpeg-build-win/ffmpeg.exe   (static, ~2 MB stripped)
#
# Configuration mirrors the macOS build script
# (../osx/build-macos-ffmpeg.sh) so HAP/ProRes files produced on Mac and
# Windows are byte-for-byte identical.
#
# Required apt packages (Ubuntu 24.04):
#   sudo apt install build-essential cmake nasm git pkg-config mingw-w64
#
# LGPL 2.1 compliance: built WITHOUT --enable-gpl and WITHOUT
# --enable-nonfree. Same compliance posture as the macOS build.

set -euo pipefail

WORK_DIR="${HOME}/ffmpeg-build-win"
SNAPPY_PREFIX="${WORK_DIR}/snappy-prefix"
FFMPEG_TAG="n7.1"
SNAPPY_TAG="1.2.1"

CROSS_PREFIX="x86_64-w64-mingw32-"
TARGET_OS="mingw32"
ARCH="x86_64"

echo "==> Tagtool FFmpeg builder for Windows x64 (WSL cross-compile)"
echo "    Output dir:    ${WORK_DIR}"
echo "    FFmpeg tag:    ${FFMPEG_TAG}"
echo "    Snappy tag:    ${SNAPPY_TAG}"
echo "    Cross prefix:  ${CROSS_PREFIX}"
echo

# --- Tool checks ---
for tool in git make cmake nasm "${CROSS_PREFIX}gcc" "${CROSS_PREFIX}g++" "${CROSS_PREFIX}strip" python3; do
  if ! command -v "${tool}" >/dev/null; then
    echo "ERROR: required tool '${tool}' not found in PATH."
    echo "       On Ubuntu/Debian:"
    echo "         sudo apt install build-essential cmake nasm git pkg-config mingw-w64"
    exit 1
  fi
done

mkdir -p "${WORK_DIR}"
cd "${WORK_DIR}"

# --- Fetch sources ---
if [ ! -d "snappy" ]; then
  echo "==> Cloning Snappy ${SNAPPY_TAG}"
  git clone --depth 1 --branch "${SNAPPY_TAG}" https://github.com/google/snappy.git
  ( cd snappy && git submodule update --init --recursive --depth 1 )
fi

if [ ! -d "ffmpeg-src" ]; then
  echo "==> Cloning FFmpeg ${FFMPEG_TAG}"
  git clone --depth 1 --branch "${FFMPEG_TAG}" https://github.com/FFmpeg/FFmpeg.git ffmpeg-src
fi

# --- Build Snappy as a mingw-w64 static library ---
# CMake toolchain file is the supported way to cross-compile a CMake-based
# project; explicit -DCMAKE_C_COMPILER + -DCMAKE_SYSTEM_NAME=Windows tells
# Snappy's CMake to target mingw-w64 and produce a Windows-format static
# lib that FFmpeg's link step can consume.
build_snappy() {
  if [ -f "${SNAPPY_PREFIX}/lib/libsnappy.a" ]; then
    echo "==> Snappy: already built, skipping"
    return
  fi
  echo "==> Building Snappy for mingw-w64 x86_64"
  local build_dir="snappy/build-mingw"
  rm -rf "${build_dir}"
  mkdir -p "${build_dir}"
  cmake -S snappy -B "${build_dir}" \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX="${SNAPPY_PREFIX}" \
    -DCMAKE_SYSTEM_NAME=Windows \
    -DCMAKE_C_COMPILER="${CROSS_PREFIX}gcc" \
    -DCMAKE_CXX_COMPILER="${CROSS_PREFIX}g++" \
    -DCMAKE_RC_COMPILER="${CROSS_PREFIX}windres" \
    -DBUILD_SHARED_LIBS=OFF \
    -DSNAPPY_BUILD_TESTS=OFF \
    -DSNAPPY_BUILD_BENCHMARKS=OFF \
    -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
    > "${build_dir}/configure.log" 2>&1
  cmake --build "${build_dir}" --target install -j "$(nproc)" \
    > "${build_dir}/build.log" 2>&1
}

build_snappy

# --- Build FFmpeg for mingw-w64 ---
build_ffmpeg() {
  local out_bin="${WORK_DIR}/ffmpeg.exe"

  if [ -f "${out_bin}" ]; then
    echo "==> FFmpeg: already built, skipping (delete ${out_bin} to force rebuild)"
    return
  fi

  echo "==> Building FFmpeg for mingw-w64 x86_64"

  local stage_dir="ffmpeg-build-mingw"
  rm -rf "${stage_dir}"
  cp -R ffmpeg-src "${stage_dir}"
  cd "${stage_dir}"

  # No pkg-config used: --enable-libsnappy uses `require` (manual link probe
  # via check_lib), not `require_pkg_config`. As long as the Snappy include
  # and lib dirs are reachable via --extra-cflags / --extra-ldflags, the
  # link probe succeeds. Matches the macOS build's pattern.
  ./configure \
    --target-os="${TARGET_OS}" \
    --arch="${ARCH}" \
    --cross-prefix="${CROSS_PREFIX}" \
    --enable-cross-compile \
    --pkg-config=pkg-config \
    --extra-cflags="-I${SNAPPY_PREFIX}/include -O2" \
    --extra-ldflags="-L${SNAPPY_PREFIX}/lib -static" \
    --extra-libs="-lstdc++ -lpthread" \
    --disable-everything \
    --enable-encoder=hap --enable-decoder=hap \
    --enable-encoder=prores_ks --enable-decoder=prores \
    --enable-encoder=qtrle --enable-decoder=qtrle \
    --enable-encoder=rawvideo --enable-decoder=rawvideo \
    --enable-muxer=mov --enable-demuxer=mov \
    --enable-muxer=rawvideo --enable-demuxer=rawvideo \
    --enable-protocol=pipe --enable-protocol=file \
    --enable-filter=vflip \
    --enable-filter=format \
    --enable-filter=scale \
    --enable-filter=crop \
    --enable-libsnappy \
    --enable-small \
    --disable-doc --disable-network --disable-htmlpages --disable-manpages \
    --disable-podpages --disable-txtpages \
    --disable-shared --enable-static \
    > "${WORK_DIR}/ffmpeg-configure.log" 2>&1

  make -j "$(nproc)" ffmpeg.exe \
    > "${WORK_DIR}/ffmpeg-build.log" 2>&1

  cp ffmpeg.exe "${out_bin}"
  cd "${WORK_DIR}"
}

build_ffmpeg

# --- Strip ---
echo "==> Stripping"
"${CROSS_PREFIX}strip" "${WORK_DIR}/ffmpeg.exe"

echo "==> Verifying"
file "${WORK_DIR}/ffmpeg.exe"
ls -lh "${WORK_DIR}/ffmpeg.exe"

# --- Smoke tests ---
# We can't actually RUN ffmpeg.exe inside WSL without Wine installed, so
# the in-script smoke tests are limited to (a) DLL dependency inspection
# via mingw's objdump and (b) string-table grep for codec internal names.
# Cross-platform descriptive strings like "Vidvox Hap" / "Apple ProRes"
# get stripped from PE32+ binaries during link-time DCE — only the
# internal codec names (`hap`, `prores_ks`, `prores`) reliably survive.
#
# Real end-to-end verification happens AFTER copying the binary to
# Windows: run `ffmpeg.exe -encoders` and a 30-frame HAP+ProRes encode
# round-trip from PowerShell. The Mac build script's smoke tests use the
# same configure flags and exercise the encoders end-to-end — if Mac
# passes there's an extremely high confidence Windows will too.

echo "==> DLL dependency check (must contain only Windows system DLLs)"
deps=$("${CROSS_PREFIX}objdump" -p "${WORK_DIR}/ffmpeg.exe" | awk '/DLL Name/ {print $NF}')
echo "${deps}" | sed 's/^/    /'
# Anything outside this list = dynamic dependency on a non-system DLL
# (typically libstdc++-6.dll or libwinpthread-1.dll) and the binary
# won't run on a stock Windows box without those DLLs present.
allowed_pattern='^(KERNEL32\.dll|msvcrt\.dll|USER32\.dll|SHELL32\.dll|bcrypt\.dll|advapi32\.dll|ws2_32\.dll|secur32\.dll|iphlpapi\.dll|ole32\.dll|gdi32\.dll|psapi\.dll|powrprof\.dll)$'
if echo "${deps}" | grep -qvE "${allowed_pattern}"; then
  echo "ERROR: ffmpeg.exe has a non-system DLL dependency:"
  echo "${deps}" | grep -vE "${allowed_pattern}" | sed 's/^/        /'
  echo "       Re-run with --extra-ldflags='-static …' so libstdc++ /"
  echo "       libwinpthread / libgcc are statically linked."
  exit 1
fi

# Force-flush the stripped binary to disk before reading it. On WSL the
# preceding strip + strings inside the same shell session has been observed
# to race occasionally — `strings` reads the binary at a moment when the
# stripped content isn't yet committed and reports a false "encoder not
# found". Cheap defensive sync.
sync
echo "==> Encoder name presence (internal codec names in string table)"
# Sentinel strings per codec, picked because they survive `--enable-small` +
# `strip` and are unambiguous on lower-cased and mixed-case mingw-w64 builds:
#
#   Hap1       — FOURCC for plain HAP; HAP variants register as Hap1/HapA/
#                HapY/HapM/Hap5. Bare `hap` is NOT a standalone line on the
#                Windows build (concatenated as `Hap1Hap5HapYHapAHapM` in
#                the codec-tag table); using the FOURCC avoids that.
#   prores_ks  — encoder name, lowercase
#   prores     — decoder name, lowercase
#   qtrle      — QuickTime Animation (RLE) encoder/decoder name, lowercase
for name in Hap1 prores_ks prores qtrle; do
  if strings "${WORK_DIR}/ffmpeg.exe" | grep -qx "${name}"; then
    echo "    ${name}: present"
  else
    echo "ERROR: '${name}' encoder name not found in binary."
    exit 1
  fi
done

cat <<EOM

==> SUCCESS

Windows x64 binary at: ${WORK_DIR}/ffmpeg.exe
Size: $(stat -c%s "${WORK_DIR}/ffmpeg.exe" | awk '{ printf "%.1f MB\n", $1/1048576 }')

NEXT STEPS

Copy into the Tagtool repo from PowerShell (paths assume default WSL mount):

    Copy-Item "\\\\wsl.localhost\\Ubuntu\\${WORK_DIR/$HOME/\\\$HOME}\\ffmpeg.exe" \\
              "Assets\\StreamingAssets\\ffmpeg\\win-x64\\ffmpeg.exe"

Or from this WSL shell, write to the Windows path:

    cp "${WORK_DIR}/ffmpeg.exe" /mnt/e/OMAi-Werk/OMAi/Tagtool/src/Assets/StreamingAssets/ffmpeg/win-x64/ffmpeg.exe

Verify after copying by running it from PowerShell:

    .\\Assets\\StreamingAssets\\ffmpeg\\win-x64\\ffmpeg.exe -hide_banner -encoders | findstr -i "hap prores qtrle"

EOM
