#!/usr/bin/env bash
# Build libvpx, libopus, and libspeex for Android ABIs → build/native/<abi>/*.a
# CMake in ndk/ autolinks when those archives exist (ANDROIDCAST_HAVE_* = 1).
#
# Prerequisites:
#   bash scripts/init-third-party-submodules.sh
#   Android NDK (auto-detected via scripts/android-ndk.sh)
#
# Usage:
#   ./scripts/build-native-codecs.sh arm64-v8a
#   ANDROIDCAST_LIBVPX_LIB=$PWD/build/native/arm64-v8a/libvpx.a ./gradlew assembleDebug
#
# Produces (when sources present): libvpx.a, libopus.a, libspeex.a under build/native/<abi>/
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ABI="${1:-arm64-v8a}"
VPX_SRC_REAL="$ROOT/third-party/libvpx"
PROJECT_OUT="$ROOT/build/native/$ABI"
OUT="$PROJECT_OUT"
BUILD_DIR="$OUT/libvpx-build"

if [[ ! -d "$VPX_SRC_REAL" ]]; then
  echo "ERROR: $VPX_SRC_REAL missing. Run:"
  echo "  bash scripts/init-third-party-submodules.sh"
  exit 1
fi

# shellcheck source=android-ndk.sh
source "$ROOT/scripts/android-ndk.sh"
if ! NDK="$(androidcast_find_ndk_root "$ROOT")"; then
  exit 1
fi
NDK_HOST_BIN="$(androidcast_ndk_host_bin "$NDK")"
if [[ ! -d "$NDK_HOST_BIN" ]]; then
  echo "ERROR: NDK host toolchain bin not found under $NDK"
  exit 1
fi

case "$ABI" in
  arm64-v8a) VPX_TARGET=arm64-android-gcc ;;
  armeabi-v7a) VPX_TARGET=armv7-android-gcc ;;
  x86_64) VPX_TARGET=x86_64-android-gcc ;;
  x86) VPX_TARGET=x86-android-gcc ;;
  *)
    echo "Unsupported ABI: $ABI"
    exit 1
    ;;
esac

# libvpx Makefiles break on spaces in SRC_PATH_BARE.
path_has_space() {
  case "$1" in
    *" "*) return 0 ;;
    *) return 1 ;;
  esac
}

VPX_SRC="$VPX_SRC_REAL"
STAGING=""
if path_has_space "$ROOT" || path_has_space "$VPX_SRC_REAL"; then
  STAGING="/tmp/androidcast-libvpx-${USER:-build}"
  VPX_SRC="$STAGING/src"
  BUILD_DIR="$STAGING/build/$ABI"
  OUT="$STAGING/out/$ABI"
  mkdir -p "$STAGING/build" "$STAGING/out"
  ln -sfn "$VPX_SRC_REAL" "$VPX_SRC"
  echo "Repo path contains spaces; using space-free staging under $STAGING"
fi

mkdir -p "$BUILD_DIR" "$OUT" "$PROJECT_OUT"
export ANDROID_NDK_HOME="$NDK"
export ANDROID_NDK_ROOT="$NDK"
export PATH="$NDK_HOST_BIN:$PATH"

# shellcheck source=native-build-cache.sh
source "$ROOT/scripts/native-build-cache.sh"
androidcast_apply_native_build_accelerators "$ROOT" "$ABI" "$NDK" "$NDK_HOST_BIN"

VPX_CONFIGURE_EXTRA=()
APP_CFLAGS_EXTRA=""
case "$ABI" in
  x86)
    # NDK does not infer -msse4.1/-mavx from libvpx source names; AVX-512 is unused on Android x86.
    VPX_CONFIGURE_EXTRA+=(--disable-avx512)
    APP_CFLAGS_EXTRA="-msse4.1 -mavx"
    ;;
  x86_64)
    VPX_CONFIGURE_EXTRA+=(--disable-avx512)
    APP_CFLAGS_EXTRA="-msse4.1 -mavx -mavx2"
    ;;
esac

## common native optimization options
APP_CFLAGS_EXTRA+=" -O2 -fdata-sections -ffunction-sections -Wl,--gc-sections -ftree-vectorize -O2 -s -funroll-loops -fomit-frame-pointer "

echo "Building libvpx for $ABI (target=$VPX_TARGET) ..."
echo "  source: $VPX_SRC"
echo "  build:  $BUILD_DIR"

cd "$BUILD_DIR"

# Out-of-tree build fails if someone previously ran configure inside third-party/libvpx.
if [[ -f "$VPX_SRC/vpx_config.h" ]]; then
  src_real="$(cd "$VPX_SRC" && pwd)"
  build_real="$(pwd)"
  if [[ "$src_real" != "$build_real" ]]; then
    echo "Cleaning in-tree configure artifacts from libvpx source (needed for out-of-tree build)..."
    make -C "$VPX_SRC" distclean >/dev/null 2>&1 || true
    rm -f "$VPX_SRC"/vpx_config.h "$VPX_SRC"/vpx_config.c "$VPX_SRC"/vpx_version.h \
      "$VPX_SRC"/config.mk "$VPX_SRC"/Makefile
    rm -f "$VPX_SRC"/libs-*.mk "$VPX_SRC"/vp*_rtcd.h "$VPX_SRC"/libvpx*.a 2>/dev/null || true
  fi
fi

LIBS_MK="$BUILD_DIR/libs-${VPX_TARGET}.mk"
need_configure=false
if [[ ! -f Makefile ]]; then
  need_configure=true
elif [[ ! -f "$LIBS_MK" ]] || ! grep -q "^TOOLCHAIN := ${VPX_TARGET}\$" "$BUILD_DIR/config.mk" 2>/dev/null; then
  echo "Re-configuring (stale or wrong ABI in $BUILD_DIR)..."
  make distclean >/dev/null 2>&1 || rm -f Makefile config.mk "$LIBS_MK" vpx_config.h vpx_config.c vpx_config.asm \
    vpx_version.h vp8_rtcd.h vp9_rtcd.h vpx_scale_rtcd.h vpx_dsp_rtcd.h
  need_configure=true
elif [[ "$ABI" == x86 || "$ABI" == x86_64 ]] && [[ -f "$BUILD_DIR/vpx_config.h" ]] \
    && grep -q '#define HAVE_AVX512 1' "$BUILD_DIR/vpx_config.h"; then
  echo "Re-configuring (AVX-512 disabled for Android NDK on $ABI)..."
  make distclean >/dev/null 2>&1 || rm -f Makefile config.mk "$LIBS_MK" vpx_config.h vpx_config.c vpx_config.asm \
    vpx_version.h vp8_rtcd.h vp9_rtcd.h vpx_scale_rtcd.h vpx_dsp_rtcd.h
  need_configure=true
fi

if $need_configure; then
  # CC/CXX may be ccache-wrapped NDK clang (see native-build-cache.sh).
  "$VPX_SRC/configure" \
    --target="$VPX_TARGET" \
    --disable-examples \
    --disable-tools \
    --disable-unit-tests \
    --disable-webm-io \
    --enable-vp8 \
    --enable-vp9 \
    --enable-static \
    --disable-shared \
    --extra-cflags="-fPIC -O2" \
    --enable-multi-res-encoding \
    --enable-vp9-temporal-denoising \
    --enable-onthefly-bitpacking \
    --enable-error-concealment \
    --enable-realtime-only \
    --enable-postproc \
    --enable-vp9-postproc \
    --enable-multithread \
    --enable-spatial-resampling \
    --disable-debug-libs \
    --disable-docs \
    --enable-external-build \
    "${VPX_CONFIGURE_EXTRA[@]}"
fi

# armeabi-v7a ndk-build runs make in ndk-app/ and needs vpx_config.asm (not required for arm64).
make vpx_config.asm 2>/dev/null || true
if [[ -f "$BUILD_DIR/vpx_config.asm" ]] && [[ ! -s "$BUILD_DIR/vpx_config.asm" ]]; then
  rm -f "$BUILD_DIR/vpx_config.asm"
  make vpx_config.asm
fi

# RTCD headers (configure creates them; refresh if missing).
make -j1 vp8_rtcd.h vp9_rtcd.h vpx_scale_rtcd.h vpx_dsp_rtcd.h

# NDK project root must not contain libvpx's top-level Makefile (ndk-build would invoke it).
NDK_ROOT="$BUILD_DIR/ndk-app"
JNI_DIR="$NDK_ROOT/jni"
rm -rf "$NDK_ROOT"
mkdir -p "$JNI_DIR"
ln -sfn "$VPX_SRC" "$JNI_DIR/libvpx"

# NDK Android.mk prepends jni/ to ASM_CONVERSION; keep the configure-generated absolute path
# in BUILD_DIR for `make vpx_config.asm`, and use a jni-only copy for ndk-build.
LIBS_MK_NDK="$BUILD_DIR/libs-ndk-${VPX_TARGET}.mk"
if [[ -f "$LIBS_MK" ]]; then
  cp -f "$LIBS_MK" "$LIBS_MK_NDK"
  sed -i 's|^ASM_CONVERSION=.*|ASM_CONVERSION=libvpx/build/make/ads2gas.pl|' "$LIBS_MK_NDK"
fi

for f in config.mk vpx_config.c vpx_config.h vpx_config.asm vpx_version.h \
    vp8_rtcd.h vp9_rtcd.h vpx_scale_rtcd.h vpx_dsp_rtcd.h; do
  if [[ -f "$BUILD_DIR/$f" ]]; then
    ln -sfn "$BUILD_DIR/$f" "$JNI_DIR/$f"
    # ndk-build may invoke make in ndk-app/ (not jni/) for RTCD / vpx_config.asm on armeabi-v7a.
    ln -sfn "$BUILD_DIR/$f" "$NDK_ROOT/$f"
  fi
done
if [[ -f "$LIBS_MK_NDK" ]]; then
  ln -sfn "$LIBS_MK_NDK" "$JNI_DIR/libs-${VPX_TARGET}.mk"
fi

{
  echo "APP_ABI := $ABI"
  echo "APP_PLATFORM := android-29"
  echo "APP_STL := c++_static"
  if [[ -n "$APP_CFLAGS_EXTRA" ]]; then
    echo "APP_CFLAGS += $APP_CFLAGS_EXTRA"
  fi
} > "$JNI_DIR/Application.mk"

cat > "$JNI_DIR/Android.mk" <<'EOF'
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# configure outputs (RTCD headers, vpx_config.h) live in jni/ — not inside libvpx sources
LOCAL_CFLAGS += -I$(LOCAL_PATH)
include $(LOCAL_PATH)/libvpx/build/make/Android.mk
EOF

"$NDK/ndk-build" -C "$NDK_ROOT" -j"$(nproc)"

LIBVPX_A="$NDK_ROOT/obj/local/$ABI/libvpx.a"
if [[ ! -f "$LIBVPX_A" ]]; then
  echo "ERROR: expected $LIBVPX_A after ndk-build"
  exit 1
fi

cp -f "$LIBVPX_A" "$OUT/libvpx.a"
if [[ "$OUT" != "$PROJECT_OUT" ]]; then
  cp -f "$OUT/libvpx.a" "$PROJECT_OUT/libvpx.a"
fi

androidcast_ndk_api() { echo 29; }

androidcast_ndk_clang() {
  local abi="$1"
  local api
  api="$(androidcast_ndk_api)"
  case "$abi" in
    arm64-v8a) echo "$NDK_HOST_BIN/aarch64-linux-android${api}-clang" ;;
    armeabi-v7a) echo "$NDK_HOST_BIN/armv7a-linux-androideabi${api}-clang" ;;
    x86_64) echo "$NDK_HOST_BIN/x86_64-linux-android${api}-clang" ;;
    x86) echo "$NDK_HOST_BIN/i686-linux-android${api}-clang" ;;
    *) echo "ERROR: unsupported ABI $abi" >&2; return 1 ;;
  esac
}

androidcast_ndk_configure_host() {
  case "$1" in
    arm64-v8a) echo aarch64-linux-android ;;
    armeabi-v7a) echo armv7a-linux-androideabi ;;
    x86_64) echo x86_64-linux-android ;;
    x86) echo i686-linux-android ;;
    *) echo "$1" ;;
  esac
}

build_opus_for_abi() {
  local opus_src="$ROOT/third-party/opus"
  if [[ ! -f "$opus_src/CMakeLists.txt" ]]; then
    echo "Skip libopus: $opus_src missing (bash scripts/init-third-party-submodules.sh)"
    return 0
  fi
  local opus_build="$BUILD_DIR/opus-cmake"
  rm -rf "$opus_build"
  echo "Building libopus for $ABI (CMake) ..."
  cmake -DCMAKE_TOOLCHAIN_FILE="$NDK/build/cmake/android.toolchain.cmake" \
    -DANDROID_ABI="$ABI" \
    -DANDROID_PLATFORM="android-$(androidcast_ndk_api)" \
    -DBUILD_SHARED_LIBS=OFF \
    -DOPUS_BUILD_SHARED_LIBRARY=OFF \
    -DOPUS_BUILD_TESTING=OFF \
    -DOPUS_BUILD_PROGRAMS=OFF \
    -DCMAKE_BUILD_TYPE=Release \
    -B "$opus_build" -S "$opus_src"
  cmake --build "$opus_build" --target opus -j"$(nproc)"
  local opus_a="$opus_build/libopus.a"
  if [[ ! -f "$opus_a" ]]; then
    opus_a="$(find "$opus_build" -name 'libopus.a' | head -1)"
  fi
  if [[ ! -f "$opus_a" ]]; then
    echo "ERROR: libopus.a not found after Opus build"
    exit 1
  fi
  cp -f "$opus_a" "$PROJECT_OUT/libopus.a"
  echo "Built: $PROJECT_OUT/libopus.a (size $(ls -lah "${PROJECT_OUT}/libopus.a" | awk '{print $5}'))"
}

build_speex_for_abi() {
  local speex_src_real="$ROOT/third-party/speex"
  if [[ ! -d "$speex_src_real" ]]; then
    echo "Skip libspeex: $speex_src_real missing (bash scripts/init-third-party-submodules.sh)"
    return 0
  fi
  local speex_src="$speex_src_real"
  local speex_build="$BUILD_DIR/speex-autotools"
  if path_has_space "$speex_src_real"; then
    speex_src="$STAGING/speex-src"
    ln -sfn "$speex_src_real" "$speex_src"
  fi
  if [[ ! -f "$speex_src/configure" ]]; then
    echo "Running speex autogen.sh ..."
    (cd "$speex_src" && ./autogen.sh)
  fi
  rm -rf "$speex_build"
  mkdir -p "$speex_build"
  local cc host
  cc="$(androidcast_ndk_clang "$ABI")"
  host="$(androidcast_ndk_configure_host "$ABI")"
  echo "Building libspeex for $ABI (autotools) ..."
  (
    cd "$speex_build"
    "$speex_src/configure" \
      --host="$host" \
      --prefix="$speex_build/install" \
      --disable-shared \
      --enable-static \
      --disable-binaries \
      --with-pic \
      CC="$cc" \
      CFLAGS="-fPIC -O2"
    make -j"$(nproc)"
  )
  local speex_a="$speex_build/libspeex/.libs/libspeex.a"
  if [[ ! -f "$speex_a" ]]; then
    speex_a="$(find "$speex_build" -name 'libspeex.a' | head -1)"
  fi
  if [[ ! -f "$speex_a" ]]; then
    echo "ERROR: libspeex.a not found after Speex build"
    exit 1
  fi
  cp -f "$speex_a" "$PROJECT_OUT/libspeex.a"
  echo "Built: $PROJECT_OUT/libspeex.a (size $(ls -lah "${PROJECT_OUT}/libspeex.a" | awk '{print $5}'))"
}

build_opus_for_abi
build_speex_for_abi

echo ""
echo "Built: $PROJECT_OUT/libvpx.a (size $(ls -lah "${PROJECT_OUT}/libvpx.a" | awk '{print $5}'))"
if [[ -n "$STAGING" ]]; then
  echo "(staged under $STAGING because of spaces in repo path)"
fi
echo "Build other ABIs as needed, e.g.:"
echo "  ./scripts/build-native-codecs.sh armeabi-v7a"
echo "Then rebuild the APK (CMake autolinks build/native/<abi>/*.a per ABI):"
echo "  ./gradlew assembleDebug"
