# The MIT License (MIT)
#
# Copyright (C) 2026 Scott Duensing
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

# Music library suite: music.lib compiled from its sources (without its
# shell hooks; the test calls musicIdle and musicAppEnded itself) with
# libxmp-lite and audio.lib, against fake sound, FM and MIDI drivers
# (src/test/audio/fakeDrv.c, fakeSynth.c) built as .drv shared objects
# into the work tree the test runs in, with the bundled OP2 bank there.
# The software FM synthesizer is compared with emu8950 (an OPL2
# emulator, MIT; installed pinned into toolchains/emu8950 by
# installMusicRefs.sh, compiled into this test only; lsan.supp covers its
# one known leak).  Module renders are
# checked against golden/render.txt (UPDATE_GOLDENS=1 rewrites it).
#
# playerTest runs the Media Player program (compiled from its project) in
# the shell fixture with the same libraries, the shell, and fileops
# linked in, on the fake sound driver.  recorderTest runs the Sound
# Recorder program the same way; the fake driver records (the test hands
# it the blocks).  The tests write their music and sound files with
# musicFiles.c.

include ../dvxtest/dvxtest.mk

.DEFAULT_GOAL := all

MUSIC_DIR  = $(DVXTEST_ROOT)/src/libs/kpunch/music
AUDIO_DIR  = $(DVXTEST_ROOT)/src/libs/kpunch/audio
FAKE_DIR   = $(DVXTEST_ROOT)/src/test/audio
XMP_DIR    = $(MUSIC_DIR)/thirdparty/libxmp-lite
EMU_DIR    = $(DVXTEST_ROOT)/toolchains/emu8950
BINDIR     = $(DVXTEST_BINDIR)/music
MUSIC_TEST = $(BINDIR)/musicTest
PLAYER_TEST = $(BINDIR)/playerTest
RECORDER_TEST = $(BINDIR)/recorderTest
FILES_OBJ  = $(BINDIR)/musicFiles.o
SHELLDIR   = $(DVXTEST_ROOT)/src/libs/kpunch/dvxshell
SHELLTEST  = $(DVXTEST_ROOT)/src/test/shell
FILEOPS    = $(DVXTEST_ROOT)/src/libs/kpunch/fileops
APPS_SO    = $(DVXTEST_OBJDIR)/apps
SHELL_OBJS = $(patsubst $(SHELLDIR)/%.c,$(BINDIR)/shell/%.o,$(wildcard $(SHELLDIR)/*.c)) $(BINDIR)/shell/shellFixture.o
FOPS_OBJS  = $(patsubst $(FILEOPS)/%.c,$(BINDIR)/fileops/%.o,$(wildcard $(FILEOPS)/*.c))
WORKDIR    = $(BINDIR)/work
DRVDIR     = $(WORKDIR)/libs/kpunch/audio/drivers
BANKFILE   = $(WORKDIR)/libs/kpunch/music/genmidi.op2
GOLDEN     = $(abspath golden/render.txt)
DVXGM      = $(DVXTEST_ROOT)/obj/addons/dvxgm/SHARED/BANKS/KPUNCH/DVXGM.SF2

LIB_SRCS   = $(filter-out $(MUSIC_DIR)/musicShell.c,$(wildcard $(MUSIC_DIR)/*.c)) $(filter-out $(AUDIO_DIR)/audioShell.c,$(wildcard $(AUDIO_DIR)/*.c))
LIB_OBJS   = $(patsubst $(DVXTEST_ROOT)/src/libs/kpunch/%.c,$(BINDIR)/lib/%.o,$(LIB_SRCS))
XMP_SRCS   = $(wildcard $(XMP_DIR)/src/*.c) $(wildcard $(XMP_DIR)/src/loaders/*.c)
XMP_HDRS   = $(wildcard $(XMP_DIR)/include/*.h $(XMP_DIR)/src/*.h $(XMP_DIR)/src/loaders/*.h)
XMP_OBJS   = $(patsubst $(XMP_DIR)/%.c,$(BINDIR)/xmp/%.o,$(XMP_SRCS))
XMP_OPTS   = -DLIBXMP_CORE_PLAYER -DLIBXMP_NO_PROWIZARD -DLIBXMP_NO_DEPACKERS -DBUILDING_STATIC -I$(XMP_DIR)/include -I$(XMP_DIR)/src
EMU_OBJS   = $(BINDIR)/emu8950.o $(BINDIR)/emuadpcm.o

CFLAGS     = $(DVXTEST_CFLAGS) -I$(MUSIC_DIR) -I$(AUDIO_DIR) -I$(XMP_DIR)/include -I$(EMU_DIR) -I$(DVXTEST_ROOT)/src/libs/kpunch -DMUSIC_GOLDEN='"$(GOLDEN)"' -DMUSIC_WORKDIR='"$(WORKDIR)"' -DXMP_TEST_DIR='"$(XMP_DIR)/test"' -DMUSIC_ADDON_BANKS='"$(DVXTEST_ROOT)/src/addons/banks"' -DMUSIC_DVXGM='"$(DVXGM)"'
APP_CFLAGS = $(CFLAGS) -fno-omit-frame-pointer -I$(SHELLDIR) -I$(SHELLTEST) -I$(DVXTEST_BASICDIR) -DDVXTEST_APPS_DIR='"$(APPS_SO)"' -DDVXTEST_FIXTURES_DIR='"$(BINDIR)"' -DSHELLTEST_BASSTUB_SO='"$(DVXTEST_BASSTUB_SO)"' -DSHELLTEST_BASCOMP='"$(DVXTEST_BASCOMP)"' -DPLAYER_DRV_DIR='"$(DRVDIR)"' -DPLAYER_BANK='"$(BANKFILE)"'
XMP_CFLAGS = $(filter-out $(WARNFLAGS) $(DEPFLAGS),$(DVXTEST_CFLAGS)) -w $(XMP_OPTS)

FAKE_DEFS  = -DFAKE_NAME='"pcm"' -DFAKE_PRIORITY=30 -DFAKE_REC_SOURCES=7 -DFAKE_CLOCK=0 -DFAKE_TONE=0 -DFAKE_DAC=1 -DFAKE_RATE=22050 -DFAKE_BITS=16 -DFAKE_CHANNELS=2 -DFAKE_OPEN_OK=1 -DFAKE_PLAY_OK=1
FAKE_DRVS  = $(DRVDIR)/pcm.drv $(DRVDIR)/fmfake.drv $(DRVDIR)/midifake.drv $(DRVDIR)/wtfake.drv

.PHONY: all run clean dvxgm host-tools

all: run

$(EMU_DIR)/emu8950.c:
	@echo "musicTest: emu8950 is missing; run ./installToolchain.sh (src/test/music/installMusicRefs.sh)" >&2
	@false

$(BINDIR)/lib/%.o: $(DVXTEST_ROOT)/src/libs/kpunch/%.c $(DVX_MAKEFILES)
	@mkdir -p $(dir $@)
	$(HOSTCC) $(CFLAGS) -c -o $@ $<

$(FILES_OBJ): musicFiles.c musicFiles.h $(DVX_MAKEFILES)
	@mkdir -p $(BINDIR)
	$(HOSTCC) $(CFLAGS) -c -o $@ $<

$(BINDIR)/shell/shellFixture.o: $(SHELLTEST)/shellFixture.c $(DVX_MAKEFILES)
	@mkdir -p $(dir $@)
	$(HOSTCC) $(APP_CFLAGS) -c -o $@ $<

$(BINDIR)/shell/%.o: $(SHELLDIR)/%.c $(DVX_MAKEFILES)
	@mkdir -p $(dir $@)
	$(HOSTCC) $(APP_CFLAGS) -c -o $@ $<

$(BINDIR)/fileops/%.o: $(FILEOPS)/%.c $(DVX_MAKEFILES)
	@mkdir -p $(dir $@)
	$(HOSTCC) $(APP_CFLAGS) -c -o $@ $<

$(BINDIR)/xmp/%.o: $(XMP_DIR)/%.c $(XMP_HDRS) $(DVX_MAKEFILES)
	@mkdir -p $(dir $@)
	$(HOSTCC) $(XMP_CFLAGS) -c -o $@ $<

$(BINDIR)/emu%.o: $(EMU_DIR)/emu%.c $(DVX_MAKEFILES)
	@mkdir -p $(BINDIR)
	$(HOSTCC) $(XMP_CFLAGS) -fno-sanitize=undefined -c -o $@ $<

$(DRVDIR)/pcm.drv: $(FAKE_DIR)/fakeDrv.c $(AUDIO_DIR)/audioDrv.h $(DVX_MAKEFILES)
	@mkdir -p $(DRVDIR)
	$(HOSTCC) $(CFLAGS) -shared $(FAKE_DEFS) -o $@ $<

$(DRVDIR)/fmfake.drv: $(FAKE_DIR)/fakeSynth.c $(AUDIO_DIR)/audioDrv.h $(DVX_MAKEFILES)
	@mkdir -p $(DRVDIR)
	$(HOSTCC) $(CFLAGS) -shared -DFAKE_SYN_FM -o $@ $<

$(DRVDIR)/midifake.drv: $(FAKE_DIR)/fakeSynth.c $(AUDIO_DIR)/audioDrv.h $(DVX_MAKEFILES)
	@mkdir -p $(DRVDIR)
	$(HOSTCC) $(CFLAGS) -shared -o $@ $<

$(DRVDIR)/wtfake.drv: $(FAKE_DIR)/fakeSynth.c $(AUDIO_DIR)/audioDrv.h $(DVX_MAKEFILES)
	@mkdir -p $(DRVDIR)
	$(HOSTCC) $(CFLAGS) -shared -DFAKE_SYN_WT -o $@ $<

$(BANKFILE): $(MUSIC_DIR)/thirdparty/genmidi/GENMIDI.OP2
	@mkdir -p $(dir $@)
	cp $< $@

$(MUSIC_TEST): musicTest.c $(FILES_OBJ) $(LIB_OBJS) $(XMP_OBJS) $(EMU_OBJS) $(DVX_MAKEFILES) $(DVXTEST_ARCHIVES) | dvxtest-libs
	@mkdir -p $(BINDIR)
	$(HOSTCC) $(CFLAGS) -o $@ $< $(FILES_OBJ) $(LIB_OBJS) $(XMP_OBJS) $(EMU_OBJS) $(DVXTEST_LINK)

$(PLAYER_TEST) $(RECORDER_TEST): $(BINDIR)/%: %.c $(FILES_OBJ) $(LIB_OBJS) $(XMP_OBJS) $(SHELL_OBJS) $(FOPS_OBJS) $(DVX_MAKEFILES) $(DVXTEST_ARCHIVES) $(DVXTEST_TASKS_A) $(DVXTEST_BASRT_A) $(DVXTEST_BASSTUB_SO) | dvxtest-libs dvxtest-apps dvxtest-bascomp
	@mkdir -p $(BINDIR)
	$(HOSTCC) $(APP_CFLAGS) -o $@ $< $(FILES_OBJ) $(LIB_OBJS) $(XMP_OBJS) $(SHELL_OBJS) $(FOPS_OBJS) $(DVXTEST_LDFLAGS) -Wl,--whole-archive $(DVXTEST_BASRT_A) $(DVXTEST_LIBDVX_A) $(DVXTEST_TASKS_A) $(DVXTEST_LIB) -Wl,--no-whole-archive -ldl -lm

host-tools:
	$(MAKE) -C $(DVXTEST_ROOT)/src/tools host COV=0

dvxtest-apps: | host-tools

-include $(LIB_OBJS:.o=.d) $(FILES_OBJ:.o=.d) $(SHELL_OBJS:.o=.d) $(FOPS_OBJS:.o=.d) $(MUSIC_TEST).d $(PLAYER_TEST).d $(RECORDER_TEST).d $(FAKE_DRVS:.drv=.d)

# The General MIDI SoundFont add-on, as the build makes it (src/tools
# makes it again when it is out of date)
dvxgm:
	$(MAKE) -C $(DVXTEST_ROOT)/src/tools ../../obj/addons/dvxgm.stamp

run: $(MUSIC_TEST) $(PLAYER_TEST) $(RECORDER_TEST) $(FAKE_DRVS) $(BANKFILE) dvxgm
	cd $(BINDIR) && ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=suppressions=$(abspath lsan.supp) ./musicTest
	cd $(BINDIR) && ASAN_OPTIONS=detect_leaks=1:detect_odr_violation=0 LSAN_OPTIONS=suppressions=$(abspath lsan.supp) ./playerTest
	cd $(BINDIR) && ASAN_OPTIONS=detect_leaks=1:detect_odr_violation=0 LSAN_OPTIONS=suppressions=$(abspath lsan.supp) ./recorderTest

clean:
	rm -rf $(BINDIR)
