#!/usr/bin/env bash
# Build a minimal, LGPL-2.1-or-later, statically linked ffmpeg.exe (Windows x64,
# also run by ARM64 Windows under emulation) for EPUB Reader's audiobook export
# (request 12): WAV in, AAC in M4B or MP3 (LAME) out, chapters from an
# ffmetadata file, cover picture copied. Same recipe and pins as Audio Converter
# (audio-converter/tools/ffmpeg/build-ffmpeg.sh), with fewer components.
#
# Run inside an MSYS2 MINGW64 shell (MSYSTEM=MINGW64), from anywhere:
#   bash tools/ffmpeg/build-ffmpeg.sh
# Re-runnable: it reuses out/ffmpeg-src (re-checking out the pinned tag) and
# rebuilds in out/ffmpeg-build. Output goes to tools/bin/ffmpeg/.
#
# Licence rules: no --enable-gpl, no --enable-nonfree, no --enable-version3.
set -euo pipefail

FFMPEG_TAG="n9.0.2"
FFMPEG_SHA="946fcce07b6dcd0331c8cc609192aeff5e1924f8"
FFMPEG_URL="https://github.com/FFmpeg/FFmpeg"

if [ "${MSYSTEM:-}" != "MINGW64" ]; then
  echo "error: run this from an MSYS2 MINGW64 shell (MSYSTEM=MINGW64)" >&2
  exit 1
fi

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
SRC="$REPO/out/ffmpeg-src"
BUILD="$REPO/out/ffmpeg-build"
DEST="$REPO/tools/bin/ffmpeg"
JOBS="${JOBS:-$(nproc)}"

# --- Dependencies (MSYS2 packages; static .a libraries are used) -----------
PKGS=(mingw-w64-x86_64-toolchain mingw-w64-x86_64-lame mingw-w64-x86_64-zlib
      make pkgconf nasm git diffutils)
if [ "${SKIP_PACMAN:-0}" != "1" ]; then
  pacman -S --noconfirm --needed "${PKGS[@]}"
fi
for lib in libmp3lame.a libz.a; do
  [ -f "/mingw64/lib/$lib" ] || { echo "error: missing static /mingw64/lib/$lib" >&2; exit 1; }
done

# --- Source -----------------------------------------------------------------
if [ ! -d "$SRC/.git" ]; then
  git clone --depth 1 --branch "$FFMPEG_TAG" "$FFMPEG_URL" "$SRC"
fi
git -c safe.directory="$SRC" -C "$SRC" -c advice.detachedHead=false checkout -q "$FFMPEG_TAG" 2>/dev/null || {
  git -c safe.directory="$SRC" -C "$SRC" fetch --depth 1 origin "refs/tags/$FFMPEG_TAG:refs/tags/$FFMPEG_TAG"
  git -c safe.directory="$SRC" -C "$SRC" -c advice.detachedHead=false checkout -q "$FFMPEG_TAG"
}
HEAD_SHA="$(git -c safe.directory="$SRC" -C "$SRC" rev-parse HEAD)"
if [ "$HEAD_SHA" != "$FFMPEG_SHA" ]; then
  echo "error: $FFMPEG_TAG resolved to $HEAD_SHA, expected $FFMPEG_SHA" >&2
  exit 1
fi

# --- Component selection ----------------------------------------------------
join() { local IFS=,; echo "$*"; }
# Print a command line readably (quote only args that need it).
showcmd() { local a out=(); for a in "$@"; do
  if [[ "$a" =~ [[:space:]\"\'] ]]; then out+=("'$a'"); else out+=("$a"); fi; done; echo "${out[*]}"; }

# per chapter: 24 kHz mono float samples piped in (f32le) -> ADTS AAC or MP3 file;
# then the chapter files piped back to back into the aac / mp3 demuxer and joined without re-encoding, with the
# chapters/tags from an ffmetadata file and the cover (PNG or JPEG, stream-copied) -> M4B or MP3.
# WAV in is kept for tests.
DECODERS=(pcm_s16le pcm_f32le aac mp3float mp3 png mjpeg)
ENCODERS=(aac libmp3lame)
DEMUXERS=(pcm_f32le wav aac mp3 ffmetadata image2 image_png_pipe image_jpeg_pipe)
MUXERS=(adts ipod mp4 mp3 null)
PARSERS=(aac mpegaudio png mjpeg)
BSFS=(aac_adtstoasc null)
FILTERS=(abuffer abuffersink buffer buffersink aresample aformat anull format null)

CONFIGURE=(
  "$SRC/configure"
  --arch=x86_64 --target-os=mingw32
  --pkg-config-flags=--static
  --extra-cflags=-static
  --extra-ldflags=-static
  --disable-shared --enable-static
  --disable-autodetect
  --disable-everything
  --disable-programs --enable-ffmpeg
  --disable-doc --disable-debug --disable-network
  --disable-devices --disable-hwaccels
  --enable-small
  --enable-zlib
  --enable-libmp3lame
  --enable-protocol=file,pipe
  --enable-decoder="$(join "${DECODERS[@]}")"
  --enable-encoder="$(join "${ENCODERS[@]}")"
  --enable-demuxer="$(join "${DEMUXERS[@]}")"
  --enable-muxer="$(join "${MUXERS[@]}")"
  --enable-parser="$(join "${PARSERS[@]}")"
  --enable-bsf="$(join "${BSFS[@]}")"
  --enable-filter="$(join "${FILTERS[@]}")"
)

echo "=== configure line ==="
showcmd "${CONFIGURE[@]}"
echo "======================"

rm -rf "$BUILD"
mkdir -p "$BUILD" "$DEST"
cd "$BUILD"
"${CONFIGURE[@]}" || { tail -50 ffbuild/config.log; exit 1; }

# Assert messages carry __FILE__ (an absolute path with the user name): map the source folder to a neutral name.
# Added to config.mak, not to the configure line, because ffmpeg -buildconf prints that line.
PREFIX_MAP="-ffile-prefix-map=$(cygpath -m "$SRC")=ffmpeg-src -ffile-prefix-map=$SRC=ffmpeg-src"
sed -i "s#^CFLAGS=#CFLAGS= $PREFIX_MAP #" ffbuild/config.mak
grep -q 'ffile-prefix-map' ffbuild/config.mak

# Licence guard: refuse to ship anything that is not LGPL-2.1-or-later.
grep -q '^#define CONFIG_GPL 0' config.h
grep -q '^#define CONFIG_NONFREE 0' config.h
grep -q '^#define CONFIG_VERSION3 0' config.h
grep -q 'FFMPEG_LICENSE "LGPL version 2.1 or later"' config.h

make -j"$JOBS" ffmpeg.exe

# make already strips ffmpeg.exe (the *_g.exe file keeps symbols);
# strip again to be explicit. Only the stripped exes are shipped.
rm -f "$DEST"/*-unstripped.exe
strip -o "$DEST/ffmpeg.exe" ffmpeg.exe
echo "--- sizes (bytes): unstripped *_g.exe vs shipped"
stat -c '%n %s' ffmpeg_g.exe "$DEST/ffmpeg.exe"

# Imports must be Windows system DLLs only.
for exe in ffmpeg.exe; do
  echo "--- imports of $exe"
  objdump -p "$DEST/$exe" | grep 'DLL Name' | tee "$BUILD/$exe.imports"
  if grep -Eiq 'libgcc|libwinpthread|libstdc|libmp3lame|libogg|libvorbis|libopus|zlib1|libz' "$BUILD/$exe.imports"; then
    echo "error: $exe imports a non-system DLL" >&2; exit 1
  fi
done

# No local paths in the shipped exe (brand check: user names must not ship).
if grep -aiqE '[A-Za-z]:/Users/|/c/Users/|/home/' "$DEST/ffmpeg.exe"; then
  echo "error: ffmpeg.exe contains a local path" >&2; grep -aoiE '[A-Za-z]:/Users/[^ ]{0,40}|/home/[^ ]{0,40}' "$DEST/ffmpeg.exe" | head -3 >&2; exit 1
fi

# --- Licence and provenance files --------------------------------------------
cp "$SRC/COPYING.LGPLv2.1" "$DEST/LICENSE.txt"
"$DEST/ffmpeg.exe" -hide_banner -buildconf > "$DEST/buildconf.txt"
"$DEST/ffmpeg.exe" -hide_banner -L | grep -q 'GNU Lesser General Public'
if grep -Eq -- '--enable-(gpl|nonfree|version3)' "$DEST/buildconf.txt"; then
  echo "error: buildconf contains a non-LGPL-2.1 flag" >&2; exit 1
fi

{
  echo "EPUB Reader - bundled FFmpeg for audiobook export (LGPL-2.1-or-later)"
  echo
  echo "FFmpeg $FFMPEG_TAG"
  echo "  commit:  $HEAD_SHA"
  echo "  source:  $FFMPEG_URL/tree/$FFMPEG_TAG"
  echo "  tarball: https://ffmpeg.org/releases/ffmpeg-${FFMPEG_TAG#n}.tar.xz"
  echo "  licence: LGPL-2.1-or-later (see LICENSE.txt, COPYING.LGPLv2.1 from the source tree)"
  echo
  echo "Statically linked libraries (MSYS2 MINGW64 packages, https://packages.msys2.org/):"
  for p in mingw-w64-x86_64-lame mingw-w64-x86_64-zlib mingw-w64-x86_64-gcc \
           mingw-w64-x86_64-crt-git mingw-w64-x86_64-winpthreads; do
    v="$(pacman -Q "$p" 2>/dev/null || echo "$p (not installed)")"
    echo "  $v"
  done
  echo "  upstream: LAME (LGPL-2.0-or-later) https://lame.sourceforge.io/"
  echo "            zlib (zlib licence) https://zlib.net/"
  echo "            MinGW-w64 runtime / winpthreads (ZPL/MIT/BSD), libgcc (GPL + runtime exception)"
  echo "  package sources: https://github.com/msys2/MINGW-packages"
  echo
  echo "Configure line:"
  showcmd "${CONFIGURE[@]}" | sed "s#$SRC#./ffmpeg-src#"
} > "$DEST/SOURCE.txt"

ls -l "$DEST"
echo "BUILD OK"
