# Compiler Flags
CC      := gcc
CFLAGS  := -g -Wall -Wextra -Werror -pedantic -fno-omit-frame-pointer
DEPFLAGS = -MMD -MP
CI2_GUI ?= 0 

# Directory Variables
LIBDIR  := lib
OBJ     := obj
INC     := include
SRC     := src
TEST    := tests

# Install Paths (override with: make install PREFIX=/usr/local)
PREFIX      ?= /usr/local
INSTALL_LIB  = $(PREFIX)/lib
INSTALL_INC  = $(PREFIX)/include

# Platform Detection and Library Linking
ifeq ($(OS), Windows_NT)
	PLATFORM      := win32
	PLATFORM_SRCS  := $(wildcard $(SRC)/win32_*.c)
	PLATFORM_OBJS  := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(PLATFORM_SRCS))
	PLATFORM_FLAGS := -DWIN32 -DUNICODE -D_UNICODE
	PLATFORM_LIBS := -municode -mconsole 
	
ifeq ($(CI2_GUI),1)
	PLATFORM_LIBS := $(filter-out -mconsole, $(PLATFORM_LIBS))
	PLATFORM_LIBS += -mwindows
	PLATFORM_FLAGS += -DCI2_GUI
endif
	
else
	UNAME_S := $(shell uname -s)
	ifeq ($(UNAME_S), Linux)
	PLATFORM       := linux
	PLATFORM_SRCS   := $(wildcard $(SRC)/linux_*.c)
	PLATFORM_OBJS   := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(PLATFORM_SRCS))
	PLATFORM_FLAGS := -D LINUX
	PLATFORM_LIBS  := 
ifeq ($(CI2_GUI),1)
	PLATFORM_FLAGS += -DCI2_GUI
	PLATFORM_LIBS += -lX11
endif

else ifeq ($(UNAME_S), Darwin)
	PLATFORM       := mac
	PLATFORM_SRCS   := $(wildcard $(SRC)/mac_*.c)
	PLATFORM_OBJS   := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(PLATFORM_SRCS))
	PLATFORM_FLAGS := -D MACOS
	PLATFORM_LIBS  :=
else
	$(error Unsupported platform: $(UNAME_S))
endif
endif

# Add platform flags 
CFLAGS += $(PLATFORM_FLAGS)

# Filepath Pattern Matching
LIB := $(LIBDIR)/lib.a

# Filter out ALL platform-prefixed files, then add back the correct platform's
SRCS := $(filter-out \
            $(wildcard $(SRC)/win32_*.c) \
            $(wildcard $(SRC)/linux_*.c) \
            $(wildcard $(SRC)/macos_*.c), \
        $(wildcard $(SRC)/*.c)) \
        $(PLATFORM_SRCS)

OBJS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS))
TESTS    := $(wildcard $(TEST)/*.c)
TESTBINS := $(patsubst $(TEST)/%.c, $(TEST)/bin/%, $(TESTS))

# Commands must be labeled PHONY
.PHONY: all release clean test install uninstall

# Compiler Release Flags (no sanitizers — those belong in CI/debug builds)
release: CFLAGS := -Wall -Wextra -Werror -pedantic -fno-omit-frame-pointer -O2 -DNDEBUG $(PLATFORM_FLAGS)
release: clean $(LIB)
	@echo "\nRelease build complete for platform: $(PLATFORM)\n"

# Target for compilation
all: $(LIB)
	@echo "\nBuilt for platform: $(PLATFORM)\n"

# Target / Dependencies
$(LIB): $(OBJS) | $(LIBDIR)
	ar -cvrs $@ $^

# Compile with automatic dependency tracking
$(OBJ)/%.o: $(SRC)/%.c $(SRC)/%.h | $(OBJ)
	$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@

$(OBJ)/%.o: $(SRC)/%.c | $(OBJ)
	$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@

$(TEST)/bin/%: $(TEST)/%.c $(LIB) | $(TEST)/bin
	$(CC) $(CFLAGS) $< $(LIB) $(PLATFORM_LIBS) -o $@

# Make directories if none
$(LIBDIR):
	mkdir $@
$(OBJ):
	mkdir $@
$(TEST)/bin:
	mkdir -p $@

# Run the tests in the bin folder and track results
# Uses perl for nanosecond timing — portable across Linux and macOS
test: $(LIB) $(TESTBINS)
	@SUCCESS=0; FAILURE=0; \
		RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'; \
		for t in $(TESTBINS); do \
		NAME=$$(basename $$t); \
		START=$$(perl -MTime::HiRes=time -e 'printf "%d\n", time()*1000'); \
		if $$t; then \
		RET=0; \
		else \
		RET=$$?; \
		fi; \
		END=$$(perl -MTime::HiRes=time -e 'printf "%d\n", time()*1000'); \
		ELAPSED_MS=$$((END - START)); \
		if [ $$RET -eq 0 ]; then \
		printf "%-20s %bPASS%b (%b%4d ms%b)\n" "$$NAME" "$$GREEN" "$$NC" "$$YELLOW" "$$ELAPSED_MS" "$$NC"; \
		SUCCESS=$$((SUCCESS + 1)); \
		else \
		printf "%-20s %bFAIL%b (%b%4d ms%b)\n" "$$NAME" "$$RED" "$$NC" "$$YELLOW" "$$ELAPSED_MS" "$$NC"; \
		FAILURE=$$((FAILURE + 1)); \
		fi; \
		done; \
		printf "\nTests completed\n"; \
		printf "SUCCESS: %b%d%b\n" "$$GREEN" "$$SUCCESS" "$$NC"; \
		printf "FAILURE: %b%d%b\n" "$$RED" "$$FAILURE" "$$NC"; \
		test $$FAILURE -eq 0

# Install library and public headers
install: $(LIB)
	@echo "Installing to $(PREFIX)..."
	install -d $(INSTALL_LIB)
	install -d $(INSTALL_INC)
	install -m 644 $(LIB) $(INSTALL_LIB)/
	@if [ -d $(INC) ]; then \
		install -m 644 $(INC)/*.h $(INSTALL_INC)/; \
		echo "Installed headers from $(INC)/"; \
		else \
		echo "Warning: no $(INC)/ directory found — skipping header install"; \
		fi
	@echo "Install complete: lib -> $(INSTALL_LIB)  headers -> $(INSTALL_INC)"

uninstall:
	@echo "Removing installed files from $(PREFIX)..."
	$(RM) $(INSTALL_LIB)/lib.a
	@if [ -d $(INC) ]; then \
		for h in $(INC)/*.h; do \
		$(RM) $(INSTALL_INC)/$$(basename $$h); \
		done; \
		fi
	@echo "Uninstall complete"

clean:
	$(RM) -r $(LIBDIR) $(OBJ) $(TEST)/bin/

# Pull in generated dependency files (silently ignored if absent)
-include $(DEPS)
