# ==============================================================================
# FORTRESS - High-Security Encryption System
# ==============================================================================
# Makefile for building the secure encryption library and tools
# ==============================================================================

CC := gcc
AR := ar

# Project directories
SRC_DIR := src
CORE_DIR := $(SRC_DIR)/core
INCLUDE_DIR := include
BUILD_DIR := build
OBJ_DIR := $(BUILD_DIR)/obj
LIB_DIR := $(BUILD_DIR)/lib
BIN_DIR := $(BUILD_DIR)/bin
TEST_DIR := tests

# Output files
LIBRARY := $(LIB_DIR)/libfortress.a
SHARED_LIB := $(LIB_DIR)/libfortress.so

# ==============================================================================
# Security-Hardened Compiler Flags
# ==============================================================================

# Base flags
CFLAGS := -std=c11 -I$(INCLUDE_DIR)

# Warning flags (treat all warnings as errors)
CFLAGS += -Wall -Wextra -Werror -Wpedantic
CFLAGS += -Wformat=2 -Wformat-security -Wformat-overflow=2
CFLAGS += -Wnull-dereference -Wstack-protector
CFLAGS += -Wstrict-overflow=5 -Wstringop-overflow=4
CFLAGS += -Wconversion -Wsign-conversion
CFLAGS += -Wcast-align -Wcast-qual
CFLAGS += -Wwrite-strings -Wpointer-arith
CFLAGS += -Wshadow -Wredundant-decls
CFLAGS += -Wmissing-prototypes -Wstrict-prototypes
CFLAGS += -Wundef -Wswitch-default -Wswitch-enum

# Security hardening flags
CFLAGS += -fstack-protector-strong
CFLAGS += -fstack-clash-protection
CFLAGS += -fcf-protection=full
CFLAGS += -D_FORTIFY_SOURCE=2
CFLAGS += -fPIE
CFLAGS += -fPIC
CFLAGS += -fno-strict-aliasing
CFLAGS += -fno-delete-null-pointer-checks
CFLAGS += -fno-strict-overflow

# Optimization (use -O2 minimum for _FORTIFY_SOURCE)
CFLAGS += -O2

# Debug build flags
DEBUG_CFLAGS := $(CFLAGS) -g3 -O0 -DDEBUG -fsanitize=address,undefined

# Linker flags for executables
LDFLAGS := -pie -Wl,-z,relro,-z,now -Wl,-z,noexecstack

# Linker flags for shared library
LDFLAGS_SHARED := -Wl,-z,relro,-z,now -Wl,-z,noexecstack

# Required libraries
LIBS := -lsodium -lpthread -lcrypto -lm -lz

# Optional extra flags for non-system dependency locations
# (e.g. make EXTRA_CFLAGS="-I$$HOME/.local/include" EXTRA_LDFLAGS="-L$$HOME/.local/lib")
CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)
LDFLAGS_SHARED += $(EXTRA_LDFLAGS)

# An optional local SDK keeps workstation builds independent of system packages.
LOCAL_SDK := $(CURDIR)/.sdk/root
ifneq ($(wildcard $(LOCAL_SDK)/usr/include/sodium.h),)
CFLAGS += -I$(LOCAL_SDK)/usr/include -I$(LOCAL_SDK)/usr/include/$(shell $(CC) -dumpmachine)
LDFLAGS += -L$(LOCAL_SDK)/usr/lib/$(shell $(CC) -dumpmachine)
LDFLAGS_SHARED += -L$(LOCAL_SDK)/usr/lib/$(shell $(CC) -dumpmachine)
endif

# ==============================================================================
# Source Files
# ==============================================================================

CORE_SOURCES := $(wildcard $(CORE_DIR)/*.c)
CORE_OBJECTS := $(patsubst $(CORE_DIR)/%.c,$(OBJ_DIR)/%.o,$(CORE_SOURCES))

TEST_SOURCES := $(wildcard $(TEST_DIR)/*.c)
TEST_OBJECTS := $(patsubst $(TEST_DIR)/%.c,$(OBJ_DIR)/test_%.o,$(TEST_SOURCES))
TEST_BINS := $(patsubst $(TEST_DIR)/%.c,$(BIN_DIR)/test_%,$(TEST_SOURCES))

# ==============================================================================
# Targets
# ==============================================================================

.PHONY: all clean debug test install dirs

all: dirs $(LIBRARY) $(SHARED_LIB)

debug: CFLAGS := $(DEBUG_CFLAGS)
debug: all

dirs:
	@mkdir -p $(OBJ_DIR) $(LIB_DIR) $(BIN_DIR)

# Static library
$(LIBRARY): $(CORE_OBJECTS) | dirs
	@echo "[AR] Creating static library: $@"
	@$(AR) rcs $@ $^

# Shared library
$(SHARED_LIB): $(CORE_OBJECTS) | dirs
	@echo "[LD] Creating shared library: $@"
	@$(CC) -shared $(LDFLAGS_SHARED) -o $@ $^ $(LIBS)

# Core object files
$(OBJ_DIR)/%.o: $(CORE_DIR)/%.c | dirs
	@echo "[CC] Compiling: $<"
	@$(CC) $(CFLAGS) -c -o $@ $<

# Test executables - link statically
$(BIN_DIR)/test_%: $(TEST_DIR)/%.c $(LIBRARY) | dirs
	@echo "[CC] Building test: $@"
	@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBRARY) $(LIBS)

test: dirs $(TEST_BINS)
	@echo "Running tests..."
	@# Tests keep keystore state under fixed /tmp fixtures; stale state from an
	@# interrupted or older-build run breaks fortress_init, so start clean.
	@rm -rf /tmp/fortress_test /tmp/fortress_test_dec /tmp/fortress_*_test \
	        /tmp/fortress_ks_backup /tmp/fortress_test_* \
	        /tmp/fortress_*.wncryv2 /tmp/fortress_*.dec
	@for test in $(TEST_BINS); do \
		echo "=== Running $$test ===" ; \
		$$test || exit 1; \
	done
	@echo "All tests passed!"

clean:
	@echo "Cleaning build artifacts..."
	@rm -rf $(BUILD_DIR)

# Install to system (requires root)
install: all
	@echo "Installing to /usr/local..."
	@install -d /usr/local/include/fortress
	@install -m 644 $(INCLUDE_DIR)/*.h /usr/local/include/fortress/
	@install -m 644 $(LIBRARY) /usr/local/lib/
	@install -m 755 $(SHARED_LIB) /usr/local/lib/
	@ldconfig

# ==============================================================================
# Chat applications (apps/)
# ==============================================================================
# `make apps` builds libfortress (injecting the user-local dependency paths so
# a plain `make apps` works) then recurses into every apps/*/Makefile that
# exists, so app subdirectories added later (server, client, tests, ...) are
# picked up automatically. `make apps-test` runs each subdir's `test` target.

.PHONY: apps apps-test

APP_LOCAL_CFLAGS  := -I$(HOME)/.local/include $(EXTRA_CFLAGS)
APP_LOCAL_LDFLAGS := -L$(HOME)/.local/lib $(EXTRA_LDFLAGS)
APP_MAKEFILES     := $(wildcard apps/*/Makefile)
APP_DIRS          := $(patsubst %/Makefile,%,$(APP_MAKEFILES))

apps:
	@$(MAKE) all EXTRA_CFLAGS="$(APP_LOCAL_CFLAGS)" EXTRA_LDFLAGS="$(APP_LOCAL_LDFLAGS)"
	@for d in $(APP_DIRS); do \
		echo "[APPS] build $$d"; \
		$(MAKE) -C $$d EXTRA_CFLAGS="$(EXTRA_CFLAGS)" EXTRA_LDFLAGS="$(EXTRA_LDFLAGS)" || exit 1; \
	done

apps-test: apps
	@for d in $(APP_DIRS); do \
		echo "[APPS] test $$d"; \
		$(MAKE) -C $$d test EXTRA_CFLAGS="$(EXTRA_CFLAGS)" EXTRA_LDFLAGS="$(EXTRA_LDFLAGS)" || exit 1; \
	done

# ==============================================================================
# Development helpers
# ==============================================================================

.PHONY: format lint analyze

format:
	@find $(SRC_DIR) $(INCLUDE_DIR) $(TEST_DIR) -name '*.c' -o -name '*.h' | xargs clang-format -i

lint:
	@cppcheck --enable=all --std=c11 -I$(INCLUDE_DIR) $(SRC_DIR)

analyze:
	@scan-build make clean all

# Print configuration
.PHONY: info
info:
	@echo "Compiler: $(CC)"
	@echo "CFLAGS: $(CFLAGS)"
	@echo "LDFLAGS: $(LDFLAGS)"
	@echo "LIBS: $(LIBS)"

# A temporary keystore keeps the demonstration separate from user data.
.PHONY: demo
demo: $(BIN_DIR)/fortress-demo
	@task_tmp=$$(mktemp -d); trap 'rm -rf "$$task_tmp"' EXIT; ./$(BIN_DIR)/fortress-demo "$$task_tmp/keystore"

$(BIN_DIR)/fortress-demo: examples/roundtrip.c $(LIBRARY) | dirs
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBRARY) $(LIBS)
