.DEFAULT_GOAL := all

# Toolchain
CXX_X64 := x86_64-w64-mingw32-g++
CXX_X86 := i686-w64-mingw32-g++

# Paths
BEACON_DIR := beacon
FILES_DIR := files

# Output directories can be overridden by the parent Makefile.
HTTP_DIST_DIR ?= objects_http
SMB_DIST_DIR  ?= objects_smb
TCP_DIST_DIR  ?= objects_tcp
DNS_DIST_DIR  ?= objects_dns

DIST_DIRS := \
	$(HTTP_DIST_DIR) \
	$(SMB_DIST_DIR) \
	$(TCP_DIST_DIR) \
	$(DNS_DIST_DIR)

# Source discovery
COMMON_SOURCES := $(filter-out \
	$(BEACON_DIR)/config.cpp \
	$(BEACON_DIR)/main.cpp \
	$(BEACON_DIR)/ConnectorHTTP.cpp \
	$(BEACON_DIR)/ConnectorSMB.cpp \
	$(BEACON_DIR)/ConnectorTCP.cpp \
	$(BEACON_DIR)/ConnectorDNS.cpp \
	$(BEACON_DIR)/DnsCodec.cpp \
	$(BEACON_DIR)/miniz.cpp, \
	$(wildcard $(BEACON_DIR)/*.cpp))

HTTP_SOURCES := $(COMMON_SOURCES) $(BEACON_DIR)/ConnectorHTTP.cpp
SMB_SOURCES  := $(COMMON_SOURCES) $(BEACON_DIR)/ConnectorSMB.cpp
TCP_SOURCES  := $(COMMON_SOURCES) $(BEACON_DIR)/ConnectorTCP.cpp
DNS_SOURCES  := $(COMMON_SOURCES) $(BEACON_DIR)/ConnectorDNS.cpp $(BEACON_DIR)/DnsCodec.cpp $(BEACON_DIR)/miniz.cpp

# Common compilation flags
ASM_SYNTAX := -masm=intel

SECURITY_FLAGS := \
	-fno-stack-protector \
	-fno-strict-overflow \
	-fno-delete-null-pointer-checks \
	-fno-strict-aliasing \
	-fno-builtin

OPTIMIZATION_FLAGS := \
	-fno-exceptions \
	-fno-unwind-tables \
	-fno-asynchronous-unwind-tables

# Generate dependency files for header tracking.
DEP_FLAGS := -MMD -MP

COMMON_FLAGS := \
	-I$(BEACON_DIR) \
	-fpermissive \
	-w \
	$(ASM_SYNTAX) \
	-fPIC \
	$(SECURITY_FLAGS) \
	$(OPTIMIZATION_FLAGS) \
	$(DEP_FLAGS)

# Miniz is trimmed to reduce CRT-related dependencies while preserving
# compression support required by the DNS codec path.
MINIZ_TRIM_FLAGS := \
	-DMINIZ_NO_STDIO \
	-DMINIZ_NO_ARCHIVE_APIS \
	-DMINIZ_NO_ARCHIVE_WRITING_APIS \
	-DMINIZ_NO_TIME \
	-DMINIZ_NO_ASSERT

# Per-transport object lists
HTTP_OBJECTS_X64 := $(patsubst $(BEACON_DIR)/%.cpp,$(HTTP_DIST_DIR)/%.x64.o,$(HTTP_SOURCES))
HTTP_OBJECTS_X86 := $(patsubst $(BEACON_DIR)/%.cpp,$(HTTP_DIST_DIR)/%.x86.o,$(HTTP_SOURCES))

SMB_OBJECTS_X64 := $(patsubst $(BEACON_DIR)/%.cpp,$(SMB_DIST_DIR)/%.x64.o,$(SMB_SOURCES))
SMB_OBJECTS_X86 := $(patsubst $(BEACON_DIR)/%.cpp,$(SMB_DIST_DIR)/%.x86.o,$(SMB_SOURCES))

TCP_OBJECTS_X64 := $(patsubst $(BEACON_DIR)/%.cpp,$(TCP_DIST_DIR)/%.x64.o,$(TCP_SOURCES))
TCP_OBJECTS_X86 := $(patsubst $(BEACON_DIR)/%.cpp,$(TCP_DIST_DIR)/%.x86.o,$(TCP_SOURCES))

DNS_OBJECTS_X64 := $(patsubst $(BEACON_DIR)/%.cpp,$(DNS_DIST_DIR)/%.x64.o,$(DNS_SOURCES))
DNS_OBJECTS_X86 := $(patsubst $(BEACON_DIR)/%.cpp,$(DNS_DIST_DIR)/%.x86.o,$(DNS_SOURCES))

# Dependency files generated from object compilation.
DEPS := \
	$(HTTP_OBJECTS_X64:.o=.d) $(HTTP_OBJECTS_X86:.o=.d) \
	$(SMB_OBJECTS_X64:.o=.d)  $(SMB_OBJECTS_X86:.o=.d) \
	$(TCP_OBJECTS_X64:.o=.d)  $(TCP_OBJECTS_X86:.o=.d) \
	$(DNS_OBJECTS_X64:.o=.d)  $(DNS_OBJECTS_X86:.o=.d)

# Parallel build settings
NPROC := $(shell nproc)
ifeq ($(MAKELEVEL),0)
MAKEFLAGS += -j$(NPROC) --no-print-directory
endif

.PHONY: all pre dirs assets clean x64 x86

all: pre x64 x86

dirs: $(DIST_DIRS)

$(DIST_DIRS):
	@mkdir -p $@

# Prepare output directories and copy runtime assets only when needed.
pre: dirs assets

assets: \
	$(HTTP_DIST_DIR)/config.cpp \
	$(SMB_DIST_DIR)/config.cpp \
	$(TCP_DIST_DIR)/config.cpp \
	$(DNS_DIST_DIR)/config.cpp

$(HTTP_DIST_DIR)/config.cpp: $(FILES_DIR)/config.tpl | $(HTTP_DIST_DIR)
	@cp -u $< $@

$(SMB_DIST_DIR)/config.cpp: $(FILES_DIR)/config.tpl | $(SMB_DIST_DIR)
	@cp -u $< $@

$(TCP_DIST_DIR)/config.cpp: $(FILES_DIR)/config.tpl | $(TCP_DIST_DIR)
	@cp -u $< $@

$(DNS_DIST_DIR)/config.cpp: $(FILES_DIR)/config.tpl | $(DNS_DIST_DIR)
	@cp -u $< $@

# Incremental transport object builds.
x64: $(HTTP_OBJECTS_X64) $(SMB_OBJECTS_X64) $(TCP_OBJECTS_X64) $(DNS_OBJECTS_X64)
	@echo "    * Refreshing x64 main objects"
	@# HTTP
	@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_HTTP -D BUILD_SVC -o $(HTTP_DIST_DIR)/main_service.x64.o
	@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_HTTP -D BUILD_DLL -o $(HTTP_DIST_DIR)/main_dll.x64.o

	@# SMB
	@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_SMB -D BUILD_SVC -o $(SMB_DIST_DIR)/main_service.x64.o
	@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_SMB -D BUILD_DLL -o $(SMB_DIST_DIR)/main_dll.x64.o

	@# TCP
	@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_TCP -D BUILD_SVC -o $(TCP_DIST_DIR)/main_service.x64.o
	@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_TCP -D BUILD_DLL -o $(TCP_DIST_DIR)/main_dll.x64.o

	@# DNS
	@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_DNS -D BUILD_SVC -o $(DNS_DIST_DIR)/main_service.x64.o
	@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_DNS -D BUILD_DLL -o $(DNS_DIST_DIR)/main_dll.x64.o

x86: $(HTTP_OBJECTS_X86) $(SMB_OBJECTS_X86) $(TCP_OBJECTS_X86) $(DNS_OBJECTS_X86)
	@echo "    * Refreshing x86 main objects"
	@# HTTP
	@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_HTTP -D BUILD_SVC -o $(HTTP_DIST_DIR)/main_service.x86.o
	@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_HTTP -D BUILD_DLL -o $(HTTP_DIST_DIR)/main_dll.x86.o

	@# SMB
	@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_SMB -D BUILD_SVC -o $(SMB_DIST_DIR)/main_service.x86.o
	@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_SMB -D BUILD_DLL -o $(SMB_DIST_DIR)/main_dll.x86.o

	@# TCP
	@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_TCP -D BUILD_SVC -o $(TCP_DIST_DIR)/main_service.x86.o
	@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_TCP -D BUILD_DLL -o $(TCP_DIST_DIR)/main_dll.x86.o

	@# DNS
	@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_DNS -D BUILD_SVC -o $(DNS_DIST_DIR)/main_service.x86.o
	@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_DNS -D BUILD_DLL -o $(DNS_DIST_DIR)/main_dll.x86.o

# Generic transport object compilation rules.
$(HTTP_DIST_DIR)/%.x64.o: $(BEACON_DIR)/%.cpp | $(HTTP_DIST_DIR)
	@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_HTTP $< -o $@

$(HTTP_DIST_DIR)/%.x86.o: $(BEACON_DIR)/%.cpp | $(HTTP_DIST_DIR)
	@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_HTTP $< -o $@

$(SMB_DIST_DIR)/%.x64.o: $(BEACON_DIR)/%.cpp | $(SMB_DIST_DIR)
	@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_SMB $< -o $@

$(SMB_DIST_DIR)/%.x86.o: $(BEACON_DIR)/%.cpp | $(SMB_DIST_DIR)
	@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_SMB $< -o $@

$(TCP_DIST_DIR)/%.x64.o: $(BEACON_DIR)/%.cpp | $(TCP_DIST_DIR)
	@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_TCP $< -o $@

$(TCP_DIST_DIR)/%.x86.o: $(BEACON_DIR)/%.cpp | $(TCP_DIST_DIR)
	@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_TCP $< -o $@

$(DNS_DIST_DIR)/%.x64.o: $(BEACON_DIR)/%.cpp | $(DNS_DIST_DIR)
	@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_DNS $< -o $@

$(DNS_DIST_DIR)/%.x86.o: $(BEACON_DIR)/%.cpp | $(DNS_DIST_DIR)
	@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_DNS $< -o $@

# Miniz requires dedicated flags and must override the generic rules.
$(HTTP_DIST_DIR)/miniz.x64.o: $(BEACON_DIR)/miniz.cpp | $(HTTP_DIST_DIR)
	@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_HTTP $(MINIZ_TRIM_FLAGS) $< -o $@

$(HTTP_DIST_DIR)/miniz.x86.o: $(BEACON_DIR)/miniz.cpp | $(HTTP_DIST_DIR)
	@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_HTTP $(MINIZ_TRIM_FLAGS) $< -o $@

$(SMB_DIST_DIR)/miniz.x64.o: $(BEACON_DIR)/miniz.cpp | $(SMB_DIST_DIR)
	@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_SMB $(MINIZ_TRIM_FLAGS) $< -o $@

$(SMB_DIST_DIR)/miniz.x86.o: $(BEACON_DIR)/miniz.cpp | $(SMB_DIST_DIR)
	@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_SMB $(MINIZ_TRIM_FLAGS) $< -o $@

$(TCP_DIST_DIR)/miniz.x64.o: $(BEACON_DIR)/miniz.cpp | $(TCP_DIST_DIR)
	@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_TCP $(MINIZ_TRIM_FLAGS) $< -o $@

$(TCP_DIST_DIR)/miniz.x86.o: $(BEACON_DIR)/miniz.cpp | $(TCP_DIST_DIR)
	@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_TCP $(MINIZ_TRIM_FLAGS) $< -o $@

$(DNS_DIST_DIR)/miniz.x64.o: $(BEACON_DIR)/miniz.cpp | $(DNS_DIST_DIR)
	@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_DNS $(MINIZ_TRIM_FLAGS) $< -o $@

$(DNS_DIST_DIR)/miniz.x86.o: $(BEACON_DIR)/miniz.cpp | $(DNS_DIST_DIR)
	@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_DNS $(MINIZ_TRIM_FLAGS) $< -o $@

clean:
	@rm -rf $(DIST_DIRS)

-include $(DEPS)
