# aetherd Unix Makefile
#
# A simple, minimal build for the Unix port.
# Portable across GNU make (Linux, macOS) and BSD make (OpenBSD, FreeBSD).
#
# Portability rules used here:
#   - `!=` for command substitution (GNU make >= 4.0, BSD make: both support it)
#     NOTE: macOS /usr/bin/make is GNU 3.81 (2006) and silently treats every
#     `!=` line as junk — use Homebrew's `gmake` on macOS.
#   - No `.if` / `ifeq` conditionals at the top level — platform-specific
#     values are computed via shell `case` statements so the same syntax
#     parses in both makes.
#   - Recipe-level conditionals use `if [ ... ]; then ... fi` in the shell.

# Detect OS
UNAME != uname -s

# Default compiler
CC ?= cc

# Build options
STATIC_SODIUM ?= yes

# Local prefix for OpenBSD (where pkg_add and manual builds put things)
LOCAL_PREFIX ?= $(HOME)/local

# Platform-specific compiler/linker flags, computed via shell so the same
# Makefile parses in both GNU make and BSD make.
PLATFORM_CFLAGS != case "`uname -s`" in \
	OpenBSD) echo "-I$(LOCAL_PREFIX)/include -I/usr/local/include" ;; \
	FreeBSD) echo "-I/usr/local/include" ;; \
	Linux)   echo "-D_GNU_SOURCE" ;; \
	*) echo "" ;; \
	esac

PLATFORM_LDFLAGS != case "`uname -s`" in \
	OpenBSD) echo "-L$(LOCAL_PREFIX)/lib -L/usr/local/lib" ;; \
	FreeBSD) echo "-L/usr/local/lib" ;; \
	*) echo "" ;; \
	esac

PKG_CONFIG_PATH != case "`uname -s`" in \
	OpenBSD) echo "$(LOCAL_PREFIX)/lib/pkgconfig:/usr/local/lib/pkgconfig" ;; \
	*) echo "" ;; \
	esac

# Base compiler flags
CFLAGS = -Wall -Wextra -O2 -g \
	-DCONFIG_AETHERD_PERSISTENCE=1 \
	-DCONFIG_AETHERD_GAMES=1 \
	-DCONFIG_AETHERD_CHAT=1 \
	-DCONFIG_AETHERD_GFX=1 \
	-DCONFIG_AETHERD_GFX_BROADCAST=1 \
	-DCONFIG_AETHERD_GFX_TV=1 \
	-DCONFIG_AETHERD_FTN=1 \
	-DCONFIG_AETHERD_FTN_SCHEDULER=1 \
	-DCONFIG_AETHERD_QWK=1 \
	-DCONFIG_AETHERD_POST_RATE_LIMIT_SEC=30 \
	-DGUEST_SESSION_BUDGET_SEC=300

# Allow ad-hoc overrides without editing this file, e.g.
#   make EXTRA_CFLAGS=-DGUEST_SESSION_BUDGET_SEC=900
CFLAGS += $(PLATFORM_CFLAGS) $(EXTRA_CFLAGS)

# OpenSSL for crypto (P-256 ECDSA verify, SHA256) — used for both crypto.c
# and TLS in ws_transport.c (WSS support).
OPENSSL_CFLAGS != pkg-config --cflags openssl 2>/dev/null || echo ""
OPENSSL_LIBS != pkg-config --libs openssl 2>/dev/null || echo "-lssl -lcrypto"

CFLAGS += $(OPENSSL_CFLAGS)
LDFLAGS = $(PLATFORM_LDFLAGS) -lpthread $(OPENSSL_LIBS)

# Directories
SRCDIR = src
AETHERD_SRCDIR = ../src/aetherd
INCDIR = include

# 9p4z location - configurable with environment variable (NINEP_DIR=...).
# When not overridden, probe a few common locations and take the first one
# that actually contains 9p4z sources. Falls back to $HOME/src/9p4z so the
# error message points somewhere sensible.
_NINEP_AUTO != for d in /mnt2/src/9p4z.tmp ../modules/9p4z ../../9p4z "$$HOME/src/9p4z"; do \
		if [ -d "$$d/src" ] && [ -f "$$d/src/union_fs.c" ]; then echo "$$d"; exit 0; fi; \
	done; echo "$$HOME/src/9p4z"
NINEP_DIR ?= $(_NINEP_AUTO)
NINEP_SRCDIR = $(NINEP_DIR)/src

# Include paths - unix/include first for platform shims, then 9p4z headers
INCLUDES = -I$(INCDIR) \
	-I$(INCDIR)/zephyr \
	-I$(NINEP_DIR)/include \
	-I../include \
	-I../include/zephyr

# Add includes to CFLAGS for BSD make compatibility (BSD make doesn't apply
# $(INCLUDES) inside per-target rules unless we fold it into CFLAGS).
CFLAGS += $(INCLUDES)

# CONFIG_NINEP_* values (Zephyr builds get these from Kconfig)
CFLAGS += -include $(INCDIR)/ninep_unix_config.h

# Source files - use 9p4z sources directly
# NOTE: WSS (TLS WebSocket) lives inside ws_transport.c, not a separate file.
UNIX_SRCS = \
	$(SRCDIR)/main.c \
	$(SRCDIR)/fs.c \
	$(SRCDIR)/tcp_transport.c \
	$(SRCDIR)/ws_transport.c \
	$(SRCDIR)/portal_fs.c \
	$(SRCDIR)/crypto.c \
	$(SRCDIR)/log.c \
	$(SRCDIR)/gopher_server.c \
	$(SRCDIR)/finger_server.c \
	$(SRCDIR)/http_server.c

# 9p4z source files (shared with Zephyr build)
NINEP_SRCS = \
	$(NINEP_SRCDIR)/server.c \
	$(NINEP_SRCDIR)/client.c \
	$(NINEP_SRCDIR)/fid.c \
	$(NINEP_SRCDIR)/message.c \
	$(NINEP_SRCDIR)/proto.c \
	$(NINEP_SRCDIR)/union_fs.c

# aetherd source files (from parent directory)
AETHERD_SRCS = \
	$(AETHERD_SRCDIR)/aetherd.c \
	$(AETHERD_SRCDIR)/aetherd_gopher.c \
	$(AETHERD_SRCDIR)/aetherd_finger.c \
	$(AETHERD_SRCDIR)/aetherd_usr.c \
	$(AETHERD_SRCDIR)/aetherd_usr_fs.c \
	$(AETHERD_SRCDIR)/aetherd_chat.c \
	$(AETHERD_SRCDIR)/aetherd_gfx.c \
	$(AETHERD_SRCDIR)/aetherd_games.c \
	$(AETHERD_SRCDIR)/aetherd_nehemiah.c \
	$(AETHERD_SRCDIR)/aetherd_ftn.c \
	$(AETHERD_SRCDIR)/aetherd_groups.c \
	$(AETHERD_SRCDIR)/aetherd_qwk.c \
	$(AETHERD_SRCDIR)/aetherd_qwk_glue.c \
	$(AETHERD_SRCDIR)/aetherd_net.c \
	$(AETHERD_SRCDIR)/gfx_world_data.c

SRCS = $(UNIX_SRCS) $(NINEP_SRCS) $(AETHERD_SRCS)

# BSD make puts all objects in current directory; we mirror that for GNU make
# by giving each .o an explicit per-file rule below.
OBJS = main.o fs.o tcp_transport.o ws_transport.o portal_fs.o crypto.o log.o \
       gopher_server.o finger_server.o http_server.o \
       server.o client.o fid.o message.o proto.o union_fs.o \
       aetherd.o aetherd_gopher.o aetherd_finger.o aetherd_usr.o aetherd_usr_fs.o \
       aetherd_chat.o aetherd_gfx.o aetherd_games.o \
       aetherd_nehemiah.o aetherd_ftn.o aetherd_groups.o aetherd_qwk.o aetherd_qwk_glue.o \
       aetherd_net.o gfx_tv.o gfx_world_data.o

# --- Optional Doom door -------------------------------------------------
# Auto-enabled when doomgeneric is vendored in (not committed; fetch with
#   git clone --depth 1 https://github.com/ozkl/doomgeneric third_party/doomgeneric
# and drop a freedoom1.wad/doom1.wad next to the aetherd binary).
# Shell-computed vars + $(X_$(Y)) indirection keep this BSD-make friendly.
DOOM_SRC_DIR = third_party/doomgeneric/doomgeneric
DOOM_ENABLED != test -f $(DOOM_SRC_DIR)/doomgeneric.c && echo 1 || echo 0
DOOM_CFLAGS_1 = -DCONFIG_AETHERD_GFX_DOOM=1
DOOM_CFLAGS_0 =
CFLAGS += $(DOOM_CFLAGS_$(DOOM_ENABLED))
DOOM_EXTRA_1 = gfx_doom.o
DOOM_EXTRA_0 =
DOOM_EXTRA = $(DOOM_EXTRA_$(DOOM_ENABLED))
DOOM_SLAVE_1 = aetherd-doom
DOOM_SLAVE_0 =
DOOM_SLAVE = $(DOOM_SLAVE_$(DOOM_ENABLED))
DOOM_NOTE_1 = @true
DOOM_NOTE_0 = @echo ""; \
	echo "NOTE: doom door DISABLED ($(DOOM_SRC_DIR) not found)."; \
	echo "      Run 'make fetch-doom' once, then rebuild."
DOOM_NOTE = $(DOOM_NOTE_$(DOOM_ENABLED))
# Render at classic internal resolution; /gfx scales per session
DOOM_RES = -DDOOMGENERIC_RESX=320 -DDOOMGENERIC_RESY=200
# Upstream's source list minus the platform ports (doomgeneric_*.c)
DOOM_CORE = dummy am_map doomdef doomstat dstrings d_event d_items d_iwad \
	d_loop d_main d_mode d_net f_finale f_wipe g_game hu_lib hu_stuff \
	info i_cdmus i_endoom i_joystick i_scale i_sound i_system i_timer \
	memio m_argv m_bbox m_cheat m_config m_controls m_fixed m_menu \
	m_misc m_random p_ceilng p_doors p_enemy p_floor p_inter p_lights \
	p_map p_maputl p_mobj p_plats p_pspr p_saveg p_setup p_sight p_spec \
	p_switch p_telept p_tick p_user r_bsp r_data r_draw r_main r_plane \
	r_segs r_sky r_things sha1 sounds statdump st_lib st_stuff s_sound \
	tables v_video wi_stuff w_checksum w_file w_main w_wad z_zone \
	w_file_stdc i_input i_video doomgeneric

# --- Optional ScummVM door -----------------------------------------------
# Auto-enabled when a ScummVM tree is vendored in (not committed; fetch
# with 'make fetch-scummvm'). Unlike doomgeneric, ScummVM is a full
# application rather than an embeddable core, so scummvm_backend/ holds
# an OSystem implementation that install.sh grafts into the tree before
# building. The result is a normal ScummVM binary whose "platform" is a
# pipe to aetherd.
SCUMMVM_SRC_DIR = third_party/scummvm
# The engines FRST stocks: the ScummVM freeware titles (sky, queen,
# drascula), the Mac adventure engines (macventure = Shadowgate & co,
# wage), and mohawk for a bring-your-own copy of Myst/Riven.
#
# Engines are COMPILE-TIME, and ScummVM builds its detection tables
# separately from its engines — so a game outside this list is detected
# and named but cannot be run. The door checks the detected engine
# against `--list-engines` and refuses such a game up front with a
# message naming the engine, rather than letting it fail inside ScummVM.
# Set to empty for every engine (much longer build, ~15MB more binary).
SCUMMVM_ENGINES ?= macventure,mohawk,queen,sky,wage,drascula
# Omit the CJK font blob (38MB) and the imgui debugger fonts (4MB) from
# the staged data. Safe unless you host Japanese/Chinese titles.
SCUMMVM_SLIM_DATA ?= no
SCUMMVM_ENABLED != test -f $(SCUMMVM_SRC_DIR)/configure && echo 1 || echo 0
SCUMMVM_CFLAGS_1 = -DCONFIG_AETHERD_GFX_SCUMMVM=1
SCUMMVM_CFLAGS_0 =
CFLAGS += $(SCUMMVM_CFLAGS_$(SCUMMVM_ENABLED))
SCUMMVM_EXTRA_1 = gfx_scummvm.o
SCUMMVM_EXTRA_0 =
SCUMMVM_EXTRA = $(SCUMMVM_EXTRA_$(SCUMMVM_ENABLED))
SCUMMVM_SLAVE_1 = aetherd-scummvm scummvm-data/.staged
SCUMMVM_SLAVE_0 =
SCUMMVM_SLAVE = $(SCUMMVM_SLAVE_$(SCUMMVM_ENABLED))
SCUMMVM_NOTE_1 = @true
SCUMMVM_NOTE_0 = @echo ""; \
	echo "NOTE: scummvm door DISABLED ($(SCUMMVM_SRC_DIR) not found)."; \
	echo "      Run 'make fetch-scummvm' once, then rebuild."
SCUMMVM_NOTE = $(SCUMMVM_NOTE_$(SCUMMVM_ENABLED))

# --- Rebuild everything when the compiler flags change ----------------
# CFLAGS is computed at parse time from what happens to be vendored in:
# the doom and scummvm doors switch -DCONFIG_* defines on and off based
# on whether third_party/ contains their sources. So the same `make` in
# the same tree can legitimately need different flags than the objects
# were built with, while no *file* has changed — and make, seeing every
# object newer than its source, does nothing at all.
#
# That is not hypothetical: build once before `make fetch-scummvm`, then
# again after, and you get aetherd-scummvm and scummvm-data built, an
# aetherd binary silently missing the door, and no error anywhere. The
# board comes up looking fine and the Playhouse just isn't there.
#
# Recording the flags in a file and depending on it turns that into an
# ordinary rebuild. Rewritten only when the flags actually differ, so a
# no-op make stays a no-op.
CFLAGS_STAMP = .cflags
_CFLAGS_SYNC != printf '%s\n' "$(CFLAGS)" | cmp -s - $(CFLAGS_STAMP) 2>/dev/null \
	|| printf '%s\n' "$(CFLAGS)" > $(CFLAGS_STAMP)

# Target
TARGET = aetherd

.PHONY: all clean test coverage coverage-report run debug dynamic static info check-deps doom-note fetch-doom scummvm-note fetch-scummvm clean-scummvm

all: $(TARGET) $(DOOM_SLAVE) $(SCUMMVM_SLAVE) aetherd-tv doom-note scummvm-note

doom-note:
	$(DOOM_NOTE)

scummvm-note:
	$(SCUMMVM_NOTE)

# One-time vendor of the doom engine; auto-detected on the next make
fetch-doom:
	git clone --depth 1 https://github.com/ozkl/doomgeneric third_party/doomgeneric

# One-time vendor of ScummVM; auto-detected on the next make. Shallow
# because we never need its history, only its engines.
fetch-scummvm:
	git clone --depth 1 https://github.com/scummvm/scummvm $(SCUMMVM_SRC_DIR)

$(TARGET): $(OBJS) $(DOOM_EXTRA) $(SCUMMVM_EXTRA)
	$(CC) $(OBJS) $(DOOM_EXTRA) $(SCUMMVM_EXTRA) -o $@ $(LDFLAGS)

doomgeneric.a:
	@mkdir -p doom_objs
	@set -e; for f in $(DOOM_CORE); do \
	  $(CC) -O2 -g -w -DNORMALUNIX -DLINUX -DSNDSERV -D_DEFAULT_SOURCE \
	    $(DOOM_RES) \
	    -c $(DOOM_SRC_DIR)/$$f.c -o doom_objs/$$f.o; \
	done
	ar rcs $@ doom_objs/*.o
	@echo "doomgeneric.a built ($(DOOM_SRC_DIR))"

# The doom door manager inside aetherd (no doom code linked here)
gfx_doom.o: $(SRCDIR)/gfx_doom.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/gfx_doom.c -o $@

# The ScummVM door manager inside aetherd (no ScummVM code linked here)
gfx_scummvm.o: $(SRCDIR)/gfx_scummvm.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/gfx_scummvm.c -o $@

# One aetherd-scummvm process is spawned per gfx session playing a game.
# Rebuilt when the backend changes; the ScummVM tree itself is configured
# once and then incrementally rebuilt, since a full build is minutes.
aetherd-scummvm: scummvm_backend/aetherd.cpp scummvm_backend/aetherd-graphics.cpp \
                 scummvm_backend/aetherd-graphics.h scummvm_backend/module.mk
	@./scummvm_backend/install.sh $(SCUMMVM_SRC_DIR)
	@if [ ! -f $(SCUMMVM_SRC_DIR)/config.mk ]; then \
		if [ -n "$(SCUMMVM_ENGINES)" ]; then \
			ENG="--disable-all-engines --enable-engine=$(SCUMMVM_ENGINES)"; \
			echo "configuring ScummVM (engines: $(SCUMMVM_ENGINES))..."; \
		else \
			ENG=""; \
			echo "configuring ScummVM (all engines; this build takes a while)..."; \
		fi; \
		cd $(SCUMMVM_SRC_DIR) && ./configure --backend=aetherd $$ENG \
			--disable-nasm --disable-taskbar --disable-tts; \
	fi
	@$(MAKE) -C $(SCUMMVM_SRC_DIR)
	cp $(SCUMMVM_SRC_DIR)/scummvm $@
	@echo aetherd-scummvm slave built

# ScummVM's themes and per-engine data files, staged where the door can
# hand them to each child (the sky engine will not start without sky.cpt).
scummvm-data/.staged: $(SCUMMVM_SRC_DIR)/configure
	@mkdir -p scummvm-data/engine-data scummvm-data/themes
	@cp $(SCUMMVM_SRC_DIR)/dists/engine-data/* scummvm-data/engine-data/ 2>/dev/null || true
	@cp $(SCUMMVM_SRC_DIR)/gui/themes/*.zip scummvm-data/themes/ 2>/dev/null || true
	@# translations.dat and gui-icons.dat sit beside the theme zips, not
	@# in engine-data; without them every session logs a warning.
	@cp $(SCUMMVM_SRC_DIR)/gui/themes/*.dat scummvm-data/themes/ 2>/dev/null || true
	@if [ "$(SCUMMVM_SLIM_DATA)" = "yes" ]; then \
		rm -f scummvm-data/engine-data/fonts-cjk.dat \
		      scummvm-data/engine-data/fonts-imgui.dat; \
		echo "scummvm-data: slim (no CJK fonts, no imgui fonts)"; \
	fi
	@touch scummvm-data/.staged
	@echo "scummvm-data staged: `du -sh scummvm-data | cut -f1`"

# The TV channel manager inside aetherd + its decoder slave (ffmpeg is
# a runtime dependency of the slave only)
gfx_tv.o: $(SRCDIR)/gfx_tv.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/gfx_tv.c -o $@

aetherd-tv: $(SRCDIR)/tv_slave.c
	$(CC) -O2 -g -Wall -o $@ $(SRCDIR)/tv_slave.c
	@echo aetherd-tv slave built

# One aetherd-doom process is spawned per gfx session playing doom
doom_slave.o: $(SRCDIR)/doom_slave.c
	$(CC) -O2 -g -Wall $(DOOM_RES) -I$(DOOM_SRC_DIR) -c $(SRCDIR)/doom_slave.c -o $@

aetherd-doom: doom_slave.o doomgeneric.a
	$(CC) doom_slave.o doomgeneric.a -o $@ -lm
	@echo aetherd-doom slave built
	@echo ""
	@echo "Build complete: $(TARGET)"
	@echo "Run: ./$(TARGET) -h for usage"

# Blanket header dependency: any header change rebuilds every object.
# Coarse but correct — struct layouts live in these headers, and a stale
# object linked against a changed layout crashes at runtime (it has bitten
# repeatedly: "make after pulling" -> aetherd crashes on first /gfx open).
# BSD-make-portable; a real depfile scheme can replace it if it ever hurts.
AETHERD_HDRS != ls ../include/zephyr/aetherd/*.h $(INCDIR)/*.h \
	$(INCDIR)/zephyr/*.h $(NINEP_DIR)/include/zephyr/9p/*.h 2>/dev/null
$(OBJS) doom_slave.o gfx_doom.o gfx_tv.o gfx_scummvm.o: $(AETHERD_HDRS) $(CFLAGS_STAMP)

# Explicit per-file rules — required for BSD make portability and to keep
# all objects in the current directory (instead of next to their sources).
main.o: $(SRCDIR)/main.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/main.c -o $@

fs.o: $(SRCDIR)/fs.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/fs.c -o $@

tcp_transport.o: $(SRCDIR)/tcp_transport.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/tcp_transport.c -o $@

ws_transport.o: $(SRCDIR)/ws_transport.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/ws_transport.c -o $@

portal_fs.o: $(SRCDIR)/portal_fs.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/portal_fs.c -o $@

crypto.o: $(SRCDIR)/crypto.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/crypto.c -o $@

log.o: $(SRCDIR)/log.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/log.c -o $@

gopher_server.o: $(SRCDIR)/gopher_server.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/gopher_server.c -o $@

finger_server.o: $(SRCDIR)/finger_server.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/finger_server.c -o $@

http_server.o: $(SRCDIR)/http_server.c
	$(CC) $(CFLAGS) -c $(SRCDIR)/http_server.c -o $@

server.o: $(NINEP_SRCDIR)/server.c
	$(CC) $(CFLAGS) -c $(NINEP_SRCDIR)/server.c -o $@

client.o: $(NINEP_SRCDIR)/client.c
	$(CC) $(CFLAGS) -c $(NINEP_SRCDIR)/client.c -o $@

fid.o: $(NINEP_SRCDIR)/fid.c
	$(CC) $(CFLAGS) -c $(NINEP_SRCDIR)/fid.c -o $@

message.o: $(NINEP_SRCDIR)/message.c
	$(CC) $(CFLAGS) -c $(NINEP_SRCDIR)/message.c -o $@

proto.o: $(NINEP_SRCDIR)/proto.c
	$(CC) $(CFLAGS) -c $(NINEP_SRCDIR)/proto.c -o $@

union_fs.o: $(NINEP_SRCDIR)/union_fs.c
	$(CC) $(CFLAGS) -c $(NINEP_SRCDIR)/union_fs.c -o $@

aetherd.o: $(AETHERD_SRCDIR)/aetherd.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd.c -o $@

aetherd_gopher.o: $(AETHERD_SRCDIR)/aetherd_gopher.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_gopher.c -o $@

aetherd_finger.o: $(AETHERD_SRCDIR)/aetherd_finger.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_finger.c -o $@

aetherd_usr.o: $(AETHERD_SRCDIR)/aetherd_usr.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_usr.c -o $@

aetherd_usr_fs.o: $(AETHERD_SRCDIR)/aetherd_usr_fs.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_usr_fs.c -o $@

aetherd_chat.o: $(AETHERD_SRCDIR)/aetherd_chat.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_chat.c -o $@

aetherd_gfx.o: $(AETHERD_SRCDIR)/aetherd_gfx.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_gfx.c -o $@

aetherd_games.o: $(AETHERD_SRCDIR)/aetherd_games.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_games.c -o $@

aetherd_nehemiah.o: $(AETHERD_SRCDIR)/aetherd_nehemiah.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_nehemiah.c -o $@

aetherd_ftn.o: $(AETHERD_SRCDIR)/aetherd_ftn.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_ftn.c -o $@

aetherd_groups.o: $(AETHERD_SRCDIR)/aetherd_groups.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_groups.c -o $@

aetherd_qwk.o: $(AETHERD_SRCDIR)/aetherd_qwk.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_qwk.c -o $@

aetherd_qwk_glue.o: $(AETHERD_SRCDIR)/aetherd_qwk_glue.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_qwk_glue.c -o $@

aetherd_net.o: $(AETHERD_SRCDIR)/aetherd_net.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/aetherd_net.c -o $@

clean:
	rm -f $(OBJS) $(TARGET) gfx_doom.o doomgeneric.a doom_slave.o aetherd-doom
	rm -f gfx_scummvm.o aetherd-scummvm $(CFLAGS_STAMP)
	rm -rf doom_objs
	@echo "Clean complete"

# Kept out of `clean`: rebuilding ScummVM from scratch is minutes, and
# `clean` is run casually.
clean-scummvm:
	rm -f aetherd-scummvm
	rm -rf scummvm-data
	-$(MAKE) -C $(SCUMMVM_SRC_DIR) clean
	@echo "ScummVM clean complete"

# Convenience targets. Platform-specific behavior moved into the recipe shell
# so the same Makefile parses in GNU make and BSD make.
run: $(TARGET)
	@if [ "`uname -s`" = "OpenBSD" ] && [ "$(STATIC_SODIUM)" != "yes" ]; then \
		LD_LIBRARY_PATH="$(LOCAL_PREFIX)/lib:$$LD_LIBRARY_PATH" ./$(TARGET); \
	else \
		./$(TARGET); \
	fi

debug: $(TARGET)
	@if [ "`uname -s`" = "OpenBSD" ] && [ "$(STATIC_SODIUM)" != "yes" ]; then \
		LD_LIBRARY_PATH="$(LOCAL_PREFIX)/lib:$$LD_LIBRARY_PATH" ./$(TARGET) -p 9564; \
	else \
		./$(TARGET) -p 9564; \
	fi

# Build with dynamic linking (for development/debugging)
dynamic:
	$(MAKE) STATIC_SODIUM=no

# Build with static linking (for distribution)
static:
	$(MAKE) STATIC_SODIUM=yes

# Run integration tests
test: $(TARGET)
	@echo "Running integration tests..."
	@../tests/integration/cga_crypto_test.sh

# Coverage build - instrument aetherd with gcov, run tests, generate report
# GNU-make-only (uses pattern rules and SRCS-derived names). OpenBSD users
# who want coverage can run this under `gmake coverage`.
COV_OBJS = $(SRCS:.c=.cov.o)

%.cov.o: %.c
	$(CC) $(CFLAGS) --coverage -O0 -c $< -o $@

aetherd-cov: $(COV_OBJS)
	$(CC) $(COV_OBJS) -o $@ $(LDFLAGS) --coverage

coverage: aetherd-cov
	@echo "Instrumented binary built: aetherd-cov"
	@echo "Run tests against it, then 'make coverage-report'"

coverage-report:
	lcov --capture --directory . --directory ../src --output-file coverage.info \
		--ignore-errors inconsistent,source,gcov
	lcov --remove coverage.info '*/9p4z/*' '/usr/*' '/opt/*' --output-file coverage.info \
		--ignore-errors unused,source
	genhtml coverage.info --output-directory coverage-html \
		--ignore-errors source,source --synthesize-missing
	@echo ""
	@echo "Coverage report: coverage-html/index.html"
	@lcov --summary coverage.info --ignore-errors source 2>&1 | tail -4

coverage-clean:
	find . ../src -name '*.gcda' -o -name '*.gcno' -o -name '*.cov.o' | xargs rm -f 2>/dev/null
	rm -rf coverage.info coverage-html aetherd-cov

# Show info
info:
	@echo "aetherd Unix build"
	@echo "Platform: $(UNAME)"
	@echo ""
	@echo "Configuration:"
	@echo "  CC: $(CC)"
	@echo "  NINEP_DIR: $(NINEP_DIR)"
	@echo "  STATIC_SODIUM: $(STATIC_SODIUM)"
	@if [ "`uname -s`" = "OpenBSD" ]; then \
		echo "  LOCAL_PREFIX: $(LOCAL_PREFIX)"; \
		echo "  PKG_CONFIG_PATH: $(PKG_CONFIG_PATH)"; \
	fi
	@echo ""
	@echo "Sources:"
	@for s in $(SRCS); do echo "  $$s"; done
	@echo ""
	@echo "Include paths:"
	@echo "  $(INCLUDES)"
	@echo ""
	@echo "Libraries:"
	@echo "  $(LDFLAGS)"

# Check dependencies
check-deps:
	@echo "Checking dependencies..."
	@echo -n "openssl: "
	@if [ -n "$(PKG_CONFIG_PATH)" ]; then \
		if PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" pkg-config --exists openssl 2>/dev/null; then \
			echo "found (`PKG_CONFIG_PATH=\"$(PKG_CONFIG_PATH)\" pkg-config --modversion openssl 2>/dev/null`)"; \
		else \
			echo "NOT FOUND - install with: pkg_add openssl (OpenBSD) or equivalent"; \
			exit 1; \
		fi; \
	else \
		if pkg-config --exists openssl 2>/dev/null; then \
			echo "found (`pkg-config --modversion openssl 2>/dev/null`)"; \
		else \
			echo "NOT FOUND - install with appropriate package manager"; \
			exit 1; \
		fi; \
	fi
	@echo -n "9p4z: "
	@if [ -d "$(NINEP_DIR)/src" ]; then \
		echo "found at $(NINEP_DIR)"; \
	else \
		echo "NOT FOUND - clone from https://github.com/jrsharp/9p4z to $(NINEP_DIR)"; \
		exit 1; \
	fi
	@echo "All dependencies satisfied."

gfx_world_data.o: $(AETHERD_SRCDIR)/gfx_world_data.c
	$(CC) $(CFLAGS) -c $(AETHERD_SRCDIR)/gfx_world_data.c -o $@
