CC_64 := x86_64-w64-mingw32-gcc
NASM := nasm

SRC_DIR := src
BIN_DIR := bin
STUB_DIR := stub

# Keep release and debug artifacts separated to avoid mixing object files.
RELEASE_DIR := $(BIN_DIR)/release
DEBUG_DIR := $(BIN_DIR)/debug

# Default output path for the stub target. It can be overridden:
# make stub STUB_OUTPUT=/custom/path/stub.exe
STUB_OUTPUT ?= $(BIN_DIR)/stub.exe

# Common compiler flags shared by both release and debug builds.
# -MMD -MP generates dependency files (*.d) automatically so header
# changes trigger recompilation of the affected object files.
COMMON_CFLAGS := -I./$(SRC_DIR) \
                 -masm=intel \
                 -fno-stack-protector \
                 -fno-builtin \
                 -fno-jump-tables \
                 -fno-exceptions \
                 -fno-asynchronous-unwind-tables \
                 -fno-unwind-tables \
                 -fno-zero-initialized-in-bss \
                 -ffunction-sections \
                 -fdata-sections \
                 -fno-ident \
                 -mno-stack-arg-probe \
                 -MMD \
                 -MP

# Release-only optimization flags.
RELEASE_CFLAGS := -O1 \
                  -fomit-frame-pointer \
                  -fno-align-functions \
                  -fno-align-jumps \
                  -fno-align-loops

# Extra warnings to catch suspicious constructs during compilation.
WARN_CFLAGS := -Wall -Wextra -Wshadow -Wundef -Wno-pointer-arith

# Debug-only flags.
DEBUG_CFLAGS := -O0 -g3 -DDEBUG

# Stub build flags.
STUB_CFLAGS := -O1 -fno-stack-protector
STUB_LDFLAGS := -Wl,--subsystem,windows -Wl,--gc-sections -s

NASMFLAGS := -f bin -O3 -w-none

# Source files compiled with GCC.
SRCS := \
	$(SRC_DIR)/core/loader.c \
	$(SRC_DIR)/core/services.c \
	$(SRC_DIR)/evasion/spoof.c \
	$(SRC_DIR)/evasion/hooks.c \
	$(SRC_DIR)/evasion/syscalls.c \
	$(SRC_DIR)/evasion/patch.c \
	$(SRC_DIR)/evasion/mask.c \
	$(SRC_DIR)/utils/utils.c \
	$(SRC_DIR)/pico/pico.c

# Convert source file paths into release/debug object file paths.
# Example:
#   src/core/loader.c -> bin/release/core/loader.x64.o
RELEASE_OBJS := $(patsubst $(SRC_DIR)/%.c,$(RELEASE_DIR)/%.x64.o,$(SRCS))
DEBUG_OBJS := $(patsubst $(SRC_DIR)/%.c,$(DEBUG_DIR)/%.x64.o,$(SRCS))

# Dependency files generated by -MMD -MP.
# Example:
#   bin/release/core/loader.x64.o -> bin/release/core/loader.x64.d
RELEASE_DEPS := $(RELEASE_OBJS:.o=.d)
DEBUG_DEPS := $(DEBUG_OBJS:.o=.d)

RELEASE_ASM := \
	$(RELEASE_DIR)/asm/draugr.x64.bin \
	$(RELEASE_DIR)/asm/dummy.x64.bin

DEBUG_ASM := \
	$(DEBUG_DIR)/asm/draugr.x64.bin \
	$(DEBUG_DIR)/asm/dummy.x64.bin

all: release

release: $(RELEASE_OBJS) $(RELEASE_ASM)

debug: $(DEBUG_OBJS) $(DEBUG_ASM)

# Build the Windows stub executable.
stub:
	mkdir -p "$(dir $(STUB_OUTPUT))"
	$(CC_64) $(STUB_DIR)/stub.c -o "$(STUB_OUTPUT)" $(STUB_CFLAGS) $(STUB_LDFLAGS)

clean:
	rm -rf "$(BIN_DIR)"

print-vars:
	@echo "RELEASE_OBJS=$(RELEASE_OBJS)"
	@echo "DEBUG_OBJS=$(DEBUG_OBJS)"
	@echo "RELEASE_ASM=$(RELEASE_ASM)"
	@echo "DEBUG_ASM=$(DEBUG_ASM)"

# Pattern rules enable incremental builds:
# Each object file depends on exactly one source file.
# Only modified sources are recompiled.

# C Compilation
$(RELEASE_DIR)/%.x64.o: $(SRC_DIR)/%.c
	mkdir -p "$(dir $@)"
	$(CC_64) -DWIN_X64 $(WARN_CFLAGS) $(COMMON_CFLAGS) $(RELEASE_CFLAGS) -c "$<" -o "$@"

$(DEBUG_DIR)/%.x64.o: $(SRC_DIR)/%.c
	mkdir -p "$(dir $@)"
	$(CC_64) -DWIN_X64 $(WARN_CFLAGS) $(COMMON_CFLAGS) $(DEBUG_CFLAGS) -c "$<" -o "$@"

# ASM Compilation
$(RELEASE_DIR)/%.x64.bin: $(SRC_DIR)/%.asm
	mkdir -p "$(dir $@)"
	$(NASM) "$<" -o "$@" $(NASMFLAGS)

$(DEBUG_DIR)/%.x64.bin: $(SRC_DIR)/%.asm
	mkdir -p "$(dir $@)"
	$(NASM) "$<" -o "$@" $(NASMFLAGS)

# Include auto-generated dependency files if they already exist.
# The leading '-' prevents errors on the first build, when .d files have not been generated yet.
-include $(RELEASE_DEPS)
-include $(DEBUG_DEPS)

# These targets are commands, not real files.
.PHONY: all release debug stub clean print-vars