#!/usr/bin/env bash
#
# Build a minimal, static, audio-only ffmpeg from source for embedding inside
# the moosic binary (see build.rs `bundle_ffmpeg`). Versions are pinned in
# scripts/ffmpeg-version.env.
#
# The result is a single self-contained executable (~5-8 MB, ~2-4 MB gzipped)
# with exactly the pieces device sync uses:
#   in : flac / mp3 / ogg+vorbis / opus / m4a+aac+alac / wav / aiff / dsf
#   out: aac / alac / mp3 (lame) / flac / vorbis / opus / wav
#   art: jpeg / png / bmp / webp decode -> scale -> raw RGB565 (ithmb)
# plus aac_at (Apple AudioToolbox AAC) on macOS. LGPL configuration (no GPL
# components), so shipping it alongside/inside the moosic binary is clean.
#
# Usage:
#   scripts/build-ffmpeg.sh                 # build for the host platform
#   scripts/build-ffmpeg.sh windows-x86_64  # cross-build (needs mingw-w64)
#
# Targets: macos-aarch64, macos-x86_64, linux-x86_64, linux-aarch64 (native
# only), windows-x86_64 (cross from Linux with x86_64-w64-mingw32-gcc, as in
# Dockerfile.windows).
#
# Output: vendor/ffmpeg/<target>/ffmpeg[.exe] + VERSION stamp.
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=ffmpeg-version.env
source "$repo_root/scripts/ffmpeg-version.env"

host_target() {
  local os arch
  case "$(uname -s)" in
    Linux) os=linux ;;
    Darwin) os=macos ;;
    MINGW*|MSYS*|CYGWIN*) os=windows ;;
    *) echo "unknown OS: $(uname -s)" >&2; exit 1 ;;
  esac
  case "$(uname -m)" in
    x86_64|amd64) arch=x86_64 ;;
    arm64|aarch64) arch=aarch64 ;;
    *) echo "unknown arch: $(uname -m)" >&2; exit 1 ;;
  esac
  echo "${os}-${arch}"
}

target="${1:-$(host_target)}"
dest_dir="$repo_root/vendor/ffmpeg/$target"
work="${MOOSIC_FFMPEG_BUILD_DIR:-$repo_root/target/ffmpeg-build/$target}"
prefix="$work/prefix"
jobs="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)"

mkdir -p "$work/src" "$prefix" "$dest_dir"
cd "$work/src"

fetch() { # url, outfile; skip if already downloaded
  [[ -f "$2" ]] || curl -fL --retry 3 -o "$2" "$1"
}

untar() { # tarball, dirname; idempotent extract
  [[ -d "$2" ]] || tar -xf "$1"
}

# ── cross / platform setup ──────────────────────────────────────────────────
# Plain strings, not arrays: macOS ships bash 3.2 where empty-array expansion
# under `set -u` is an error.
cross_args=""
dep_host_args=""
exe_suffix=""
extra_cflags="-I$prefix/include"
extra_ldflags="-L$prefix/lib"
platform_args=""
aac_encoders="aac"

case "$target" in
  windows-x86_64)
    cross="x86_64-w64-mingw32"
    command -v "$cross-gcc" >/dev/null || { echo "$cross-gcc not found (install mingw-w64)" >&2; exit 1; }
    # --pkg-config: Debian/Ubuntu ship no x86_64-w64-mingw32-pkg-config, and
    # without it ffmpeg's configure silently degrades to `false` and every
    # dependency probe fails. The host pkg-config is fine; PKG_CONFIG_PATH
    # points at our cross-built prefix.
    cross_args="--arch=x86_64 --target-os=mingw32 --cross-prefix=$cross- --enable-cross-compile --pkg-config=pkg-config"
    dep_host_args="--host=$cross"
    exe_suffix=".exe"
    ;;
  macos-*)
    [[ "$(uname -s)" == "Darwin" ]] || { echo "$target must be built on macOS" >&2; exit 1; }
    # AudioToolbox gives the aac_at encoder (Apple's AAC, the qaac/iTunes
    # encoder) for free; frameworks are system-provided, still portable.
    platform_args="--enable-audiotoolbox"
    aac_encoders="aac,aac_at"
    if [[ "$target" == "macos-x86_64" && "$(uname -m)" == "arm64" ]]; then
      extra_cflags+=" -arch x86_64"; extra_ldflags+=" -arch x86_64"
      cross_args="--arch=x86_64 --enable-cross-compile"
      dep_host_args="--host=x86_64-apple-darwin"
      export CC="clang -arch x86_64"
    fi
    ;;
  linux-*)
    [[ "$(uname -s)" == "Linux" ]] || { echo "$target must be built on Linux (use the Dockerfiles)" >&2; exit 1; }
    # Fully static so the binary runs on any distro (musl base recommended;
    # glibc -static works too since networking/dlopen are disabled).
    extra_ldflags+=" -static"
    ;;
  *)
    echo "unsupported target: $target" >&2; exit 1 ;;
esac

# ── static encoder libs (all LGPL/BSD) ──────────────────────────────────────
build_dep() { # dirname, extra configure args...
  local dir="$1"; shift
  ( cd "$dir"
    # shellcheck disable=SC2086 # dep_host_args is intentionally word-split
    ./configure --prefix="$prefix" --disable-shared --enable-static $dep_host_args "$@" >/dev/null
    make -j"$jobs" >/dev/null && make install >/dev/null )
}

echo "== lame $LAME_VERSION"
fetch "https://downloads.sourceforge.net/project/lame/lame/$LAME_VERSION/lame-$LAME_VERSION.tar.gz" lame.tar.gz
untar lame.tar.gz "lame-$LAME_VERSION"
[[ -f "$prefix/lib/libmp3lame.a" ]] || build_dep "lame-$LAME_VERSION" --disable-frontend --disable-decoder

echo "== ogg $OGG_VERSION"
fetch "https://downloads.xiph.org/releases/ogg/libogg-$OGG_VERSION.tar.gz" ogg.tar.gz
untar ogg.tar.gz "libogg-$OGG_VERSION"
[[ -f "$prefix/lib/libogg.a" ]] || build_dep "libogg-$OGG_VERSION"

echo "== vorbis $VORBIS_VERSION"
fetch "https://downloads.xiph.org/releases/vorbis/libvorbis-$VORBIS_VERSION.tar.gz" vorbis.tar.gz
untar vorbis.tar.gz "libvorbis-$VORBIS_VERSION"
# libvorbis' configure passes -force_cpusubtype_ALL on darwin; Xcode's new
# linker removed that option and hard-errors. Strip it before configuring.
sed -i.bak 's/-force_cpusubtype_ALL//g' "libvorbis-$VORBIS_VERSION/configure"
[[ -f "$prefix/lib/libvorbis.a" ]] || build_dep "libvorbis-$VORBIS_VERSION" --with-ogg="$prefix" --disable-examples --disable-docs

echo "== opus $OPUS_VERSION"
fetch "https://downloads.xiph.org/releases/opus/opus-$OPUS_VERSION.tar.gz" opus.tar.gz
untar opus.tar.gz "opus-$OPUS_VERSION"
[[ -f "$prefix/lib/libopus.a" ]] || build_dep "opus-$OPUS_VERSION" --disable-extra-programs --disable-doc

# mingw-w64 ships no zlib (needed for --enable-zlib: png cover art), so
# cross-build a static one. macOS/Linux use the system zlib.
if [[ "$target" == windows-* ]]; then
  echo "== zlib $ZLIB_VERSION"
  fetch "https://zlib.net/fossils/zlib-$ZLIB_VERSION.tar.gz" zlib.tar.gz
  untar zlib.tar.gz "zlib-$ZLIB_VERSION"
  [[ -f "$prefix/lib/libz.a" ]] || ( cd "zlib-$ZLIB_VERSION"
    make -f win32/Makefile.gcc PREFIX="$cross-" libz.a >/dev/null
    make -f win32/Makefile.gcc PREFIX="$cross-" install \
      INCLUDE_PATH="$prefix/include" LIBRARY_PATH="$prefix/lib" \
      BINARY_PATH="$prefix/bin" >/dev/null )
fi

# ── ffmpeg ──────────────────────────────────────────────────────────────────
echo "== ffmpeg $FFMPEG_VERSION"
fetch "https://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.xz" ffmpeg.tar.xz
untar ffmpeg.tar.xz "ffmpeg-$FFMPEG_VERSION"

cd "ffmpeg-$FFMPEG_VERSION"
# shellcheck disable=SC2086 # cross/platform args are intentionally word-split
PKG_CONFIG_PATH="$prefix/lib/pkgconfig" ./configure \
  --prefix="$prefix" \
  --pkg-config-flags=--static \
  --extra-cflags="$extra_cflags" \
  --extra-ldflags="$extra_ldflags" \
  $cross_args \
  $platform_args \
  --disable-everything \
  --disable-autodetect \
  --disable-network \
  --disable-doc \
  --disable-debug \
  --disable-ffplay \
  --disable-ffprobe \
  --enable-small \
  --disable-x86asm \
  --enable-zlib \
  --enable-swresample \
  --enable-swscale \
  --enable-libmp3lame \
  --enable-libvorbis \
  --enable-libopus \
  --enable-protocol=file,pipe \
  --enable-demuxer=aac,aiff,dsf,iff,flac,image2,image_jpeg_pipe,image_png_pipe,image_bmp_pipe,image_webp_pipe,matroska,mov,mp3,ogg,wav \
  --enable-decoder=aac,alac,flac,mp3,mp3float,vorbis,opus,pcm_s16le,pcm_s16be,pcm_s24le,pcm_s24be,pcm_s32le,pcm_f32le,pcm_u8,dsd_lsbf,dsd_msbf,dsd_lsbf_planar,dsd_msbf_planar,mjpeg,png,bmp,webp,wrapped_avframe \
  --enable-encoder=$aac_encoders,alac,flac,libmp3lame,libvorbis,libopus,pcm_s16le,png,rawvideo \
  --enable-muxer=ipod,mp3,flac,wav,ogg,opus,rawvideo,image2 \
  --enable-parser=aac,flac,mpegaudio,opus,vorbis,mjpeg,png,bmp,webp \
  --enable-bsf=aac_adtstoasc \
  --enable-filter=aresample,scale,format,anull,color,sine \
  --enable-indev=lavfi \
  >/dev/null

make -j"$jobs" >/dev/null

bin="ffmpeg$exe_suffix"
out="$dest_dir/$bin"
cp "$bin" "$out"
case "$target" in
  windows-*) x86_64-w64-mingw32-strip "$out" 2>/dev/null || true ;;
  *) strip "$out" 2>/dev/null || strip -x "$out" 2>/dev/null || true ;;
esac
chmod +x "$out"
echo "$FFMPEG_VERSION" > "$dest_dir/VERSION"

echo "done: $out ($(du -h "$out" | cut -f1)) · ffmpeg $FFMPEG_VERSION audio-only static"
