#
# Makefile.windows — кросс-компиляция WDSP для 64-bit Windows на Linux (MinGW-w64)
#
# Использование:
#   make -f Makefile.windows            # DLL + статическая библиотека
#   make -f Makefile.windows dll        # только DLL
#   make -f Makefile.windows static     # только статическая библиотека
#   make -f Makefile.windows clean      # удалить артефакты сборки
#   make -f Makefile.windows distclean  # + удалить собранный и скачанный FFTW
#
# Зависимости (установить до запуска):
#   Arch:    sudo pacman -S mingw-w64-gcc         (тянет mingw-w64-headers автоматически)
#   Debian:  sudo apt install mingw-w64            (мета-пакет: компилятор + заголовки + CRT)
#   Fedora:  sudo dnf install mingw64-gcc          (тянет mingw64-headers автоматически)
#   + wget или curl, tar
#

MINGW_PREFIX ?= x86_64-w64-mingw32

CC     := $(MINGW_PREFIX)-gcc
AR     := $(MINGW_PREFIX)-ar
RANLIB := $(MINGW_PREFIX)-ranlib

CFLAGS  ?= -O3 -Wno-parentheses -Wno-incompatible-pointer-types
LDFLAGS ?=

# ── FFTW: сборка из исходников ────────────────────────────────────────────────

FFTW_VERSION := 3.3.11
FFTW_TAR     := fftw-$(FFTW_VERSION).tar.gz
FFTW_URL     := https://fftw.org/pub/fftw/$(FFTW_TAR)

# Абсолютные пути — нужны для configure --prefix и sub-make -C
FFTW_SRC     := $(CURDIR)/third_party/fftw-$(FFTW_VERSION)
FFTW_INST    := $(CURDIR)/third_party/fftw-win64

# Раздельные build-директории для double и float precision
FFTW_BUILD_D := $(FFTW_INST)/build-double
FFTW_BUILD_F := $(FFTW_INST)/build-float

# FFTW_MARCH задаёт минимальный набор инструкций для Windows-бинарника.
# haswell (2013+) гарантирует AVX2+FMA — оптимальный выбор для SDR на современном ПК.
# Для совместимости со старыми машинами замените на -march=sandybridge (AVX, 2011+)
# или -march=core2 (SSSE3, 2007+).
FFTW_MARCH ?= -march=haswell

FFTW_CONF_COMMON = \
  --host=$(MINGW_PREFIX) \
  CC=$(CC) \
  CFLAGS="$(FFTW_MARCH)" \
  --prefix=$(FFTW_INST) \
  --enable-shared \
  --disable-static \
  --with-our-malloc \
  --enable-sse2 \
  --enable-avx \
  --enable-avx2 \
  --disable-fortran \
  --quiet

# Sentinel-файлы для отслеживания готовности FFTW
FFTW_HEADER := $(FFTW_INST)/include/fftw3.h
FFTW_LIB_D  := $(FFTW_INST)/lib/libfftw3.dll.a
FFTW_LIB_F  := $(FFTW_INST)/lib/libfftw3f.dll.a
FFTW_DLL_D  := $(FFTW_INST)/bin/libfftw3-3.dll
FFTW_DLL_F  := $(FFTW_INST)/bin/libfftw3f-3.dll

# ── Выходные директории и файлы ───────────────────────────────────────────────

OBJDIR := obj_win
OUTDIR := lib_win

DLL        := $(OUTDIR)/libwdsp.dll
IMPLIB     := $(OUTDIR)/libwdsp.dll.a
STATIC_LIB := $(OUTDIR)/libwdsp.a

# ── Исходники (копия из основного Makefile) ───────────────────────────────────

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

OBJS := $(addprefix $(OBJDIR)/, $(SOURCES:.c=.o))

# ── NR3/NR4: исходники third-party ────────────────────────────────────────────
#
# Компилируем rnnoise и libspecbleach напрямую в obj_win/, минуя их sub-make.
# Это исключает конфликт с Linux-сборкой: third_party/*.a содержат ELF-объекты,
# и если они "свежее" исходников, sub-make считает их up-to-date и не пересобирает.

RNNOISE_DIR    := third_party/rnnoise
SPECBLEACH_DIR := third_party/libspecbleach

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

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

RNNOISE_OBJS    := $(addprefix $(OBJDIR)/rnnoise/, $(RNNOISE_SRCS:.c=.o))
SPECBLEACH_OBJS := $(addprefix $(OBJDIR)/specbleach/, $(SPECBLEACH_SRCS:.c=.o))

RNNOISE_LIB    := $(OUTDIR)/librnnoise.a
SPECBLEACH_LIB := $(OUTDIR)/libspecbleach.a

# ── Флаги компиляции ──────────────────────────────────────────────────────────

NR34_INC  := -I$(RNNOISE_DIR)/include -I$(SPECBLEACH_DIR)/include
FFTW_INC  := -I$(FFTW_INST)/include

COMPILE   := $(CC) $(CFLAGS) $(FFTW_INC) $(NR34_INC)

NR34_DEPS := $(RNNOISE_LIB) $(SPECBLEACH_LIB)

# avrt нужен т.к. comm.h включает <avrt.h> под _WIN32
LINK_LIBS := $(NR34_DEPS) \
             -L$(FFTW_INST)/lib -lfftw3 -lfftw3f \
             -lavrt -lm

# ── Цели ──────────────────────────────────────────────────────────────────────

.PHONY: all dll static fftw fftw-double fftw-float third-party check-tools clean distclean

all: dll static

dll:    check-tools fftw third-party $(DLL)
static: check-tools fftw third-party $(STATIC_LIB)

# ── Проверка инструментов ─────────────────────────────────────────────────────

check-tools:
	@command -v $(CC) >/dev/null 2>&1 || { \
	  echo ""; \
	  echo "ОШИБКА: $(CC) не найден. Установите mingw-w64:"; \
	  echo "  Arch:    sudo pacman -S mingw-w64-gcc"; \
	  echo "  Debian:  sudo apt install mingw-w64"; \
	  echo "  Fedora:  sudo dnf install mingw64-gcc"; \
	  echo ""; exit 1; }
	@echo '#include <windows.h>' | $(CC) -x c - -fsyntax-only -Wno-pragma-once-outside-header 2>/dev/null || { \
	  echo ""; \
	  echo "ОШИБКА: Windows API заголовки не найдены."; \
	  echo "Установите пакет с заголовками MinGW-w64:"; \
	  echo "  Arch:    sudo pacman -S mingw-w64-headers"; \
	  echo "  Debian:  sudo apt install mingw-w64          # не gcc-mingw-w64-x86-64!"; \
	  echo "  Fedora:  sudo dnf install mingw64-headers"; \
	  echo ""; exit 1; }
	@(command -v wget >/dev/null 2>&1 || command -v curl >/dev/null 2>&1) || { \
	  echo "ОШИБКА: требуется wget или curl"; exit 1; }

# ── FFTW: скачать исходники → собрать double → собрать float ──────────────────

fftw: fftw-double fftw-float

# Шаг 1: скачать tarball
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

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

# Шаг 3a: configure для double precision
$(FFTW_BUILD_D)/Makefile: $(FFTW_SRC)/configure
	mkdir -p $(FFTW_BUILD_D)
	@echo ">>> Конфигурируем FFTW (double)..."
	cd $(FFTW_BUILD_D) && $(FFTW_SRC)/configure $(FFTW_CONF_COMMON)

# Шаг 4a: сборка и установка double precision
fftw-double: $(FFTW_LIB_D)
$(FFTW_LIB_D): $(FFTW_BUILD_D)/Makefile
	@echo ">>> Собираем FFTW (double)..."
	$(MAKE) -C $(FFTW_BUILD_D) install

# Шаг 3b: configure для float precision (--enable-float)
$(FFTW_BUILD_F)/Makefile: $(FFTW_SRC)/configure
	mkdir -p $(FFTW_BUILD_F)
	@echo ">>> Конфигурируем FFTW (float)..."
	cd $(FFTW_BUILD_F) && $(FFTW_SRC)/configure $(FFTW_CONF_COMMON) --enable-float

# Шаг 4b: сборка и установка float precision
# Зависит от fftw-double чтобы install-шаги не конкурировали за prefix
fftw-float: $(FFTW_LIB_F)
$(FFTW_LIB_F): $(FFTW_BUILD_F)/Makefile $(FFTW_LIB_D)
	@echo ">>> Собираем FFTW (float)..."
	$(MAKE) -C $(FFTW_BUILD_F) install

# ── Third-party библиотеки (кросс-компиляция) ─────────────────────────────────

third-party: $(NR34_DEPS)

$(RNNOISE_LIB): $(RNNOISE_OBJS) | $(OUTDIR)
	$(AR) rv $@ $^
	$(RANLIB) $@

$(SPECBLEACH_LIB): $(SPECBLEACH_OBJS) | $(OUTDIR)
	$(AR) rv $@ $^
	$(RANLIB) $@

# % в GNU make совпадает через '/', поэтому паттерн покрывает вложенные пути
$(OBJDIR)/rnnoise/%.o: $(RNNOISE_DIR)/%.c
	@mkdir -p $(@D)
	$(CC) $(CFLAGS) -I$(RNNOISE_DIR)/include -I$(RNNOISE_DIR)/src -c -o $@ $<

$(OBJDIR)/specbleach/%.o: $(SPECBLEACH_DIR)/%.c | $(FFTW_LIB_D)
	@mkdir -p $(@D)
	$(CC) $(CFLAGS) -I$(FFTW_INST)/include \
	  -I$(SPECBLEACH_DIR)/include -I$(SPECBLEACH_DIR)/src -I$(SPECBLEACH_DIR)/src/shared \
	  -c -o $@ $<

# ── Основная библиотека ───────────────────────────────────────────────────────

$(OUTDIR) $(OBJDIR):
	mkdir -p $@

$(DLL): $(OBJS) $(NR34_DEPS) $(FFTW_LIB_D) $(FFTW_LIB_F) | $(OUTDIR)
	@echo ">>> Линкуем $@..."
	$(CC) -shared \
	  -Wl,--out-implib,$(IMPLIB) \
	  $(LDFLAGS) \
	  -o $@ \
	  $(OBJS) $(LINK_LIBS)
	@cp $(FFTW_DLL_D) $(FFTW_DLL_F) $(OUTDIR)/
	@echo ""
	@echo "=== Готово ==="
	@echo "  DLL:        $(DLL)"
	@echo "  Import lib: $(IMPLIB)"
	@echo "  Заголовок:  wdsp.h"
	@echo ""
	@echo "  FFTW DLL скопированы в $(OUTDIR)/ — включите их в дистрибутив"
	@echo "  рядом с libwdsp.dll."

$(STATIC_LIB): $(OBJS) | $(OUTDIR)
	$(AR) rv $@ $(OBJS)
	$(RANLIB) $@
	@echo ""
	@echo "=== Готово ==="
	@echo "  Статическая: $(STATIC_LIB)"
	@echo "  Линкуйте приложение с:"
	@echo "    $(STATIC_LIB) $(NR34_DEPS)"
	@echo "    -L$(FFTW_INST)/lib -lfftw3 -lfftw3f -lavrt -lm"

$(OBJDIR)/%.o: %.c | $(OBJDIR) $(FFTW_LIB_D)
	$(COMPILE) -c -o $@ $<

# ── Очистка ───────────────────────────────────────────────────────────────────

clean:
	-rm -rf $(OBJDIR) $(OUTDIR)

# distclean удаляет собранный и скачанный FFTW — при следующем запуске пересоберётся
distclean: clean
	-rm -rf $(FFTW_INST) $(FFTW_SRC) third_party/$(FFTW_TAR)
