# Android cross-build for WDSP and JNI bridge
# Usage:
#   make android
#   make -f Makefile.android all
# Optional overrides:
#   ANDROID_NDK=/path/to/ndk ANDROID_API=24 ANDROID_ABIS="arm64-v8a x86_64"
#
# FFTW source is downloaded and extracted automatically.
# Override FFTW_SRC to use an existing directory.

# Auto-detect NDK path (can always be overridden by setting ANDROID_NDK explicitly):
#   1. ANDROID_NDK_HOME environment variable
#   2. Latest NDK under ANDROID_SDK_ROOT or ANDROID_HOME
#   3. Default SDK locations: ~/Android/Sdk (Linux) or ~/Library/Android/sdk (macOS)
ifeq ($(origin ANDROID_NDK),undefined)
  ifdef ANDROID_NDK_HOME
    ANDROID_NDK := $(ANDROID_NDK_HOME)
  else
    _SDK_ROOT := $(or $(ANDROID_SDK_ROOT),$(ANDROID_HOME),\
                   $(wildcard $(HOME)/Android/Sdk),\
                   $(wildcard $(HOME)/Library/Android/sdk))
    ifdef _SDK_ROOT
      ANDROID_NDK := $(lastword $(sort $(wildcard $(_SDK_ROOT)/ndk/*)))
    endif
  endif
endif
ifndef ANDROID_NDK
  $(error Cannot find Android NDK. Set ANDROID_NDK, ANDROID_NDK_HOME, ANDROID_SDK_ROOT, or ANDROID_HOME)
endif
ANDROID_API ?= 24
ANDROID_ABIS ?= arm64-v8a armeabi-v7a x86_64
ANDROID_HOST_TAG ?= $(shell uname -s | tr '[:upper:]' '[:lower:]' | sed 's/darwin/darwin/;s/linux/linux/')-$(shell uname -m | sed 's/aarch64/arm64/;s/x86_64/x86_64/')

# Auto-detect javac: JAVA_HOME > system PATH > Android Studio JBR (Linux/macOS)
_JAVAC_CANDIDATES := \
  $(if $(JAVA_HOME),$(wildcard $(JAVA_HOME)/bin/javac)) \
  $(shell command -v javac 2>/dev/null) \
  $(wildcard /opt/android-studio/jbr/bin/javac) \
  $(wildcard /Applications/Android\ Studio.app/Contents/jbr/Contents/Home/bin/javac)
JAVAC ?= $(firstword $(_JAVAC_CANDIDATES))

TOOLCHAIN := $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(ANDROID_HOST_TAG)

COMMON_CFLAGS   ?= -O3 -D_GNU_SOURCE -Wno-parentheses -fPIC
COMMON_CPPFLAGS ?= -I. -I third_party/rnnoise/include -I third_party/libspecbleach/include
ANDROID_JNI_CFLAGS ?= -std=gnu89 -Wno-implicit-function-declaration -Wno-int-conversion \
  -Wno-incompatible-pointer-types -Wno-incompatible-pointer-types-discards-qualifiers

FFTW_VERSION  := 3.3.11
FFTW_TAR      := fftw-$(FFTW_VERSION).tar.gz
FFTW_URL      := https://fftw.org/pub/fftw/$(FFTW_TAR)
FFTW_SRC      ?= third_party/fftw-$(FFTW_VERSION)
FFTW_MAKEJOBS ?= $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)

WDSP_SOURCES = amd.c \
ammod.c \
amsq.c \
analyzer.c \
anf.c \
anr.c \
apfshadow.c \
bandpass.c \
calcc.c \
calculus.c \
cblock.c \
cfcomp.c \
cfir.c \
channel.c \
cmath.c \
compress.c \
delay.c \
dexp.c \
div.c \
doublepole.c \
eer.c \
emnr.c \
emph.c \
eq.c \
fcurve.c \
FDnoiseIQ.c \
fir.c \
firmin.c \
fmd.c \
fmmod.c \
fmsq.c \
gain.c \
gaussian.c \
gen.c \
icfir.c \
iir.c \
impulse_cache.c \
iobuffs.c \
iqc.c \
linux_port.c \
lmath.c \
main.c \
matchedCW.c \
meter.c \
meterlog10.c \
nbp.c \
nob.c \
nobII.c \
osctrl.c \
patchpanel.c \
resample.c \
rmatch.c \
rnnr.c \
RXA.c \
sbnr.c \
sender.c \
shift.c \
siphon.c \
slew.c \
snb.c \
ssql.c \
syncbuffs.c \
TXA.c \
utilities.c \
varsamp.c \
version.c \
wcpAGC.c \
wisdom.c \
zetaHat.c

RNNOISE_SOURCES = \
third_party/rnnoise/src/denoise.c \
third_party/rnnoise/src/celt_lpc.c \
third_party/rnnoise/src/kiss_fft.c \
third_party/rnnoise/src/nnet.c \
third_party/rnnoise/src/nnet_default.c \
third_party/rnnoise/src/parse_lpcnet_weights.c \
third_party/rnnoise/src/pitch.c \
third_party/rnnoise/src/rnn.c \
third_party/rnnoise/src/rnnoise_data.c \
third_party/rnnoise/src/rnnoise_data_1.c \
third_party/rnnoise/src/rnnoise_data_2.c \
third_party/rnnoise/src/rnnoise_data_3.c \
third_party/rnnoise/src/rnnoise_data_4.c \
third_party/rnnoise/src/rnnoise_data_5.c \
third_party/rnnoise/src/rnnoise_data_6.c \
third_party/rnnoise/src/rnnoise_tables.c

SPECBLEACH_SOURCES = \
third_party/libspecbleach/src/processors/specbleach_adenoiser.c \
third_party/libspecbleach/src/processors/specbleach_denoiser.c \
third_party/libspecbleach/src/processors/adaptivedenoiser/adaptive_denoiser.c \
third_party/libspecbleach/src/shared/gain_estimation/gain_estimators.c \
third_party/libspecbleach/src/shared/noise_estimation/adaptive_noise_estimator.c \
third_party/libspecbleach/src/shared/pre_estimation/absolute_hearing_thresholds.c \
third_party/libspecbleach/src/shared/pre_estimation/critical_bands.c \
third_party/libspecbleach/src/shared/pre_estimation/masking_estimator.c \
third_party/libspecbleach/src/shared/pre_estimation/noise_scaling_criterias.c \
third_party/libspecbleach/src/shared/pre_estimation/spectral_smoother.c \
third_party/libspecbleach/src/shared/pre_estimation/transient_detector.c \
third_party/libspecbleach/src/shared/post_estimation/noise_floor_manager.c \
third_party/libspecbleach/src/shared/post_estimation/postfilter.c \
third_party/libspecbleach/src/shared/post_estimation/spectral_whitening.c \
third_party/libspecbleach/src/shared/utils/denoise_mixer.c \
third_party/libspecbleach/src/shared/utils/general_utils.c \
third_party/libspecbleach/src/shared/utils/spectral_features.c \
third_party/libspecbleach/src/shared/utils/spectral_utils.c \
third_party/libspecbleach/src/shared/stft/stft_processor.c \
third_party/libspecbleach/src/shared/stft/fft_transform.c \
third_party/libspecbleach/src/shared/stft/stft_buffer.c \
third_party/libspecbleach/src/shared/stft/stft_windows.c

ALL_C_SOURCES = $(WDSP_SOURCES) $(RNNOISE_SOURCES) $(SPECBLEACH_SOURCES)

JNI_SOURCE  = org_openhpsdr_dsp_Wdsp.c
JAVA_SOURCE = java/org/openhpsdr/dsp/Wdsp.java

ANDROID_OBJ_ROOT  := obj/android
ANDROID_LIB_ROOT  := lib/android
ANDROID_JAVA_OUT  := lib/android/java

.PHONY: all clean distclean java-classes

all: $(FFTW_SRC)/configure \
  $(foreach abi,$(ANDROID_ABIS),\
    $(ANDROID_LIB_ROOT)/$(abi)/libfftw3.so \
    $(ANDROID_LIB_ROOT)/$(abi)/libfftw3f.so \
    $(ANDROID_LIB_ROOT)/$(abi)/libwdsp.so \
    $(ANDROID_LIB_ROOT)/$(abi)/libwdspj.so) \
  java-classes

# ── Автоматическое получение FFTW ────────────────────────────────────────────

third_party/$(FFTW_TAR):
	mkdir -p third_party
	@echo ">>> Скачиваем FFTW $(FFTW_VERSION)..."
	@if command -v wget >/dev/null 2>&1; then \
	  wget -q --show-progress -O $@ "$(FFTW_URL)"; \
	else \
	  curl -L --progress-bar -o $@ "$(FFTW_URL)"; \
	fi

$(FFTW_SRC)/configure: third_party/$(FFTW_TAR)
	@echo ">>> Распаковываем FFTW..."
	tar xf $< -C third_party/
	@touch $@

# abi_target: compiler triple for NDK clang wrapper
define abi_target
$(if $(filter $(1),arm64-v8a),aarch64-linux-android,\
$(if $(filter $(1),armeabi-v7a),armv7a-linux-androideabi,\
$(if $(filter $(1),x86_64),x86_64-linux-android,unsupported)))
endef

# fftw_host: --host value for FFTW configure (autoconf canonical names)
define fftw_host
$(if $(filter $(1),arm64-v8a),aarch64-linux-android,\
$(if $(filter $(1),armeabi-v7a),arm-linux-androideabi,\
$(if $(filter $(1),x86_64),x86_64-linux-android,$(1))))
endef

# abi_cflags: extra CFLAGS per ABI
define abi_cflags
$(if $(filter $(1),armeabi-v7a),-march=armv7-a -mfloat-abi=softfp -mfpu=neon,)
endef

# fftw3 (double) SIMD flags per ABI
define fftw_simd
$(if $(filter $(1),x86_64),--enable-sse2,)
endef

# fftw3f (float) SIMD flags per ABI
define fftwf_simd
$(if $(filter $(1),arm64-v8a),--enable-neon,\
$(if $(filter $(1),armeabi-v7a),--enable-neon,\
$(if $(filter $(1),x86_64),--enable-sse --enable-sse2,)))
endef

# -----------------------------------------------------------------------
# Per-ABI rules
# -----------------------------------------------------------------------
define make_abi_rules

ABI_$(1)_TARGET  := $(call abi_target,$(1))
ABI_$(1)_CC      := $(TOOLCHAIN)/bin/$$(ABI_$(1)_TARGET)$(ANDROID_API)-clang
ABI_$(1)_OBJDIR  := $(ANDROID_OBJ_ROOT)/$(1)
ABI_$(1)_LIBDIR  := $(ANDROID_LIB_ROOT)/$(1)
ABI_$(1)_CFLAGS  := $(COMMON_CFLAGS) $(call abi_cflags,$(1))

# FFTW install prefixes (inside obj tree)
ABI_$(1)_FFTW_PREFIX  := $$(ABI_$(1)_OBJDIR)/fftw-install
ABI_$(1)_FFTWF_PREFIX := $$(ABI_$(1)_OBJDIR)/fftwf-install

# Stamp files (avoid re-running configure+make on every build)
ABI_$(1)_FFTW_STAMP  := $$(ABI_$(1)_FFTW_PREFIX)/.built
ABI_$(1)_FFTWF_STAMP := $$(ABI_$(1)_FFTWF_PREFIX)/.built

ABI_$(1)_CPPFLAGS := $(COMMON_CPPFLAGS) \
  -I third_party/rnnoise/src -I third_party/libspecbleach/include -I third_party/libspecbleach/src -I third_party/libspecbleach/src/shared \
  -I$$(ABI_$(1)_FFTW_PREFIX)/include

ABI_$(1)_WDSP_OBJS := $$(patsubst %.c,$$(ABI_$(1)_OBJDIR)/%.o,$$(ALL_C_SOURCES))
ABI_$(1)_JNI_OBJ   := $$(ABI_$(1)_OBJDIR)/$(JNI_SOURCE:.c=.o)

# --- Build fftw3 (double) ---
$$(ABI_$(1)_FFTW_STAMP): $(FFTW_SRC)/configure
	@mkdir -p $$(ABI_$(1)_OBJDIR)/fftw-build $$(ABI_$(1)_FFTW_PREFIX)
	cd $$(ABI_$(1)_OBJDIR)/fftw-build && \
	  $(abspath $(FFTW_SRC))/configure \
	    --build=$(shell uname -m)-linux-gnu \
	    --host=$(strip $(call fftw_host,$(1))) \
	    CC="$$(ABI_$(1)_CC)" \
	    CFLAGS="-fPIC" \
	    --prefix=$(abspath $$(ABI_$(1)_FFTW_PREFIX)) \
	    --enable-static --disable-shared --disable-fortran \
	    $(call fftw_simd,$(1)) && \
	  $(MAKE) -j$(FFTW_MAKEJOBS) install
	@touch $$@

# --- Build fftw3f (float/single) ---
$$(ABI_$(1)_FFTWF_STAMP): $(FFTW_SRC)/configure
	@mkdir -p $$(ABI_$(1)_OBJDIR)/fftwf-build $$(ABI_$(1)_FFTWF_PREFIX)
	cd $$(ABI_$(1)_OBJDIR)/fftwf-build && \
	  $(abspath $(FFTW_SRC))/configure \
	    --build=$(shell uname -m)-linux-gnu \
	    --host=$(strip $(call fftw_host,$(1))) \
	    CC="$$(ABI_$(1)_CC)" \
	    CFLAGS="-fPIC" \
	    --prefix=$(abspath $$(ABI_$(1)_FFTWF_PREFIX)) \
	    --enable-static --disable-shared --disable-fortran \
	    --enable-float \
	    $(call fftwf_simd,$(1)) && \
	  $(MAKE) -j$(FFTW_MAKEJOBS) install
	@touch $$@

# --- Shared fftw3.so / fftw3f.so for packaging alongside libwdsp.so ---
$$(ABI_$(1)_LIBDIR)/libfftw3.so: $$(ABI_$(1)_FFTW_STAMP)
	@mkdir -p $$(@D)
	$$(ABI_$(1)_CC) -shared -Wl,-soname,libfftw3.so \
	  -Wl,--whole-archive $$(ABI_$(1)_FFTW_PREFIX)/lib/libfftw3.a -Wl,--no-whole-archive \
	  -lm -o $$@

$$(ABI_$(1)_LIBDIR)/libfftw3f.so: $$(ABI_$(1)_FFTWF_STAMP)
	@mkdir -p $$(@D)
	$$(ABI_$(1)_CC) -shared -Wl,-soname,libfftw3f.so \
	  -Wl,--whole-archive $$(ABI_$(1)_FFTWF_PREFIX)/lib/libfftw3f.a -Wl,--no-whole-archive \
	  -lm -o $$@

# --- Compile WDSP + rnnoise + specbleach sources ---
$$(ABI_$(1)_OBJDIR)/%.o: %.c | $$(ABI_$(1)_FFTW_STAMP) $$(ABI_$(1)_FFTWF_STAMP)
	@mkdir -p $$(@D)
	$$(ABI_$(1)_CC) $$(ABI_$(1)_CPPFLAGS) $$(ABI_$(1)_CFLAGS) -c -o $$@ $$<

# --- Build libwdsp.so (statically links fftw) ---
$$(ABI_$(1)_LIBDIR)/libwdsp.so: $$(ABI_$(1)_WDSP_OBJS) $$(ABI_$(1)_FFTW_STAMP) $$(ABI_$(1)_FFTWF_STAMP)
	@mkdir -p $$(@D)
	$$(ABI_$(1)_CC) -shared -Wl,-soname,libwdsp.so -o $$@ \
	  $$(ABI_$(1)_WDSP_OBJS) \
	  $$(ABI_$(1)_FFTW_PREFIX)/lib/libfftw3.a \
	  $$(ABI_$(1)_FFTWF_PREFIX)/lib/libfftw3f.a \
	  -lm -llog

# --- Compile JNI bridge ---
$$(ABI_$(1)_JNI_OBJ): $(JNI_SOURCE) org_openhpsdr_dsp_Wdsp.h | $$(ABI_$(1)_FFTW_STAMP)
	@mkdir -p $$(@D)
	$$(ABI_$(1)_CC) $$(ABI_$(1)_CPPFLAGS) $$(ABI_$(1)_CFLAGS) $(ANDROID_JNI_CFLAGS) \
	  -I$(TOOLCHAIN)/sysroot/usr/include -c -o $$@ $$<

# --- Build libwdspj.so ---
$$(ABI_$(1)_LIBDIR)/libwdspj.so: $$(ABI_$(1)_JNI_OBJ) $$(ABI_$(1)_LIBDIR)/libwdsp.so
	@mkdir -p $$(@D)
	$$(ABI_$(1)_CC) -shared -Wl,-soname,libwdspj.so -o $$@ \
	  $$(ABI_$(1)_JNI_OBJ) \
	  -L$$(ABI_$(1)_LIBDIR) -lwdsp -llog

endef

$(foreach abi,$(ANDROID_ABIS),$(eval $(call make_abi_rules,$(abi))))

# -----------------------------------------------------------------------

java-classes: $(JAVA_SOURCE)
	@if [ -x "$(JAVAC)" ]; then \
		mkdir -p "$(ANDROID_JAVA_OUT)"; \
		"$(JAVAC)" -d "$(ANDROID_JAVA_OUT)" "$(JAVA_SOURCE)"; \
		echo "Java classes built in $(ANDROID_JAVA_OUT)"; \
	else \
		echo "Skipping Java class build: javac not found at $(JAVAC)"; \
	fi

clean:
	rm -rf $(ANDROID_OBJ_ROOT) $(ANDROID_LIB_ROOT)

distclean: clean
	-rm -rf $(FFTW_SRC) third_party/$(FFTW_TAR)
