.PHONY: help full base os prng testing uuid all clean builddir build-test run-test install build-lib

# External variables
CC             = clang
CXX            = clang++
AR             = ar
BUILD_TYPE     = Debug
BUILD_DIR      = libwapp-build/$(PLATFORM)-$(BUILD_TYPE)
INSTALL_PREFIX = dist
RUNTIME_ASSERT = true

# Internal variables
override CFLAGS              = -Wall -Wextra -Werror -pedantic -Isrc
override LIBFLAGS            = -fPIC
override CSTD               := -std=gnu11
override CXXSTD             := -std=gnu++11
override KERNEL             := $(shell uname -s)
override MACHINE            := $(shell uname -m)
override PLATFORM           := $(KERNEL)_$(MACHINE)
override TEST_INCLUDE       := -Isrc $(shell find tests -type d | xargs -I{} echo -n "-I{} ")
override TEST_SRC           := $(shell find tests -type f -name "*.c" | xargs -I{} echo -n "{} ")
override TEST_C_SRC         := src/wapp.c $(TEST_SRC)
override TEST_CXX_SRC       := $(shell find tests -type f -name "*.cc" | xargs -I{} echo -n "{} ")
override LIB_BASENAME       := wapp
override OBJ_OUT            := $(BUILD_DIR)/$(LIB_BASENAME).o
override LIB_STATIC_NAME    := lib$(LIB_BASENAME).a
override LIB_OUT            := $(BUILD_DIR)/$(LIB_STATIC_NAME)
override TEST_C_OUT         := $(BUILD_DIR)/wapptest
override TEST_CXX_OUT       := $(BUILD_DIR)/wapptestcc
override ABS_INSTALL_PREFIX := $(shell mkdir -p $(INSTALL_PREFIX) && realpath $(INSTALL_PREFIX))
override INCLUDE_INSTALL    := $(ABS_INSTALL_PREFIX)/include/$(LIB_BASENAME)
override LIB_INSTALL        := $(ABS_INSTALL_PREFIX)/lib
override HEADER_INSTALL_CMD := scripts/header_install.sh

ifeq ($(origin BUILD_FLAGS), undefined)
ifeq ($(BUILD_TYPE),Debug)
	BUILD_FLAGS += -g -fsanitize=address,undefined -DWAPP_DEBUG_ASSERT
else ifeq ($(BUILD_TYPE),RelWithDebInfo)
	BUILD_FLAGS += -g -O2 -fsanitize=address,undefined -DWAPP_DEBUG_ASSERT
else ifeq ($(BUILD_TYPE),Release)
	BUILD_FLAGS += -O3
else
	$(error Invalid BUILD type '$(BUILD_TYPE)'. Use 'Debug', 'RelWithDebInfo' or 'Release')
endif
endif

# Disable runtime asserts
ifeq ($(RUNTIME_ASSERT), false)
	override BUILD_FLAGS += -DWAPP_NO_RUNTIME_ASSERT
endif

ifeq ($(CC),gcc)
  # Used to disable the "ASan runtime does not come first in initial library list" error when compiling with gcc
  export ASAN_OPTIONS=verify_asan_link_order=0
endif

# Escape sequences
BOLD              = \033[1m
BLACK             = \033[30m
BG_BLACK          = \033[40m
RED               = \033[31m
BG_RED            = \033[41m
GREEN             = \033[32m
BG_GREEN          = \033[42m
YELLOW            = \033[33m
BG_YELLOW         = \033[43m
BLUE              = \033[34m
BG_BLUE           = \033[44m
MAGENTA           = \033[35m
BG_MAGENTA        = \033[45m
CYAN              = \033[36m
BG_CYAN           = \033[46m
WHITE             = \033[37m
BG_WHITE          = \033[47m
GRAY              = \033[90m
BG_GRAY           = \033[100m
BRIGHT_RED        = \033[91m
BG_BRIGHT_RED     = \033[101m
BRIGHT_GREEN      = \033[92m
BG_BRIGHT_GREEN   = \033[102m
BRIGHT_YELLOW     = \033[93m
BG_BRIGHT_YELLOW  = \033[103m
BRIGHT_BLUE       = \033[94m
BG_BRIGHT_BLUE    = \033[104m
BRIGHT_MAGENTA    = \033[95m
BG_BRIGHT_MAGENTA = \033[105m
BRIGHT_CYAN       = \033[96m
BG_BRIGHT_CYAN    = \033[106m
BRIGHT_WHITE      = \033[97m
BG_BRIGHT_WHITE   = \033[107m
RESET             = \033[0m

ECHO_E = echo -e
ifeq ($(KERNEL), Darwin)
	ECHO_E = echo
endif

all: clean builddir run-c-test full run-cc-test

help:
	@$(ECHO_E) "$(BOLD)$(BLUE)Available build variables:$(RESET)"
	@$(ECHO_E) "  $(GREEN)CC$(RESET)                C compiler to use $(YELLOW)(Default: clang)$(RESET)."
	@$(ECHO_E) "  $(GREEN)CXX$(RESET)               C++ compiler to use $(YELLOW)(Default: clang++)$(RESET)."
	@$(ECHO_E) "  $(GREEN)AR$(RESET)                Archiving utility to use for building static libraries $(YELLOW)(Default: ar)$(RESET)."
	@$(ECHO_E) "  $(GREEN)BUILD_TYPE$(RESET)        Build type $(MAGENTA)[Debug | RelWithDebInfo | Release] $(YELLOW)(Default: Debug)$(RESET)."
	@$(ECHO_E) "  $(GREEN)BUILD_DIR$(RESET)         Directory where build files will be written."
	@$(ECHO_E) "  $(GREEN)INSTALL_PREFIX$(RESET)    Prefix where library and include files will be installed."
	@$(ECHO_E) "  $(GREEN)RUNTIME_ASSERT$(RESET)    Whether runtime asserts are enabled $(MAGENTA)[true | false] $(YELLOW)(Default: true)$(RESET)."
	@$(ECHO_E) "  $(GREEN)$(RESET)                  $(BOLD)$(BG_RED)DISCLAIMER:$(RESET) Using this flag is not recommended as it disables safety checks"
	@$(ECHO_E) "  $(GREEN)$(RESET)                  potentially leading to Undefined Behaviour."
	@$(ECHO_E)
	@$(ECHO_E) "$(BOLD)$(BLUE)Available targets:$(RESET)"
	@$(ECHO_E) "  $(GREEN)make$(RESET)              Build, install and test the full wapp library."
	@$(ECHO_E) "  $(GREEN)make full$(RESET)         Build and install the full wapp library."
	@$(ECHO_E) "  $(GREEN)make base$(RESET)         Build and install only the $(CYAN)base$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make os$(RESET)           Build and install only the $(CYAN)os$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make prng$(RESET)         Build and install only the $(CYAN)prng$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make uuid$(RESET)         Build and install only the $(CYAN)uuid$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make testing$(RESET)      Build and install only the $(CYAN)testing$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make clean$(RESET)        Clean the build directory."
	@$(ECHO_E) "  $(GREEN)make help$(RESET)         Print this help message and exit."

full: LIB_SRC = src/wapp.c
full: INCLUDES = common os base prng testing uuid
full: install

base: LIB_SRC = src/base/wapp_base.c
base: INCLUDES = common base
base: install

os: LIB_SRC = src/os/wapp_os.c
os: INCLUDES = common os base
os: install

prng: LIB_SRC = src/prng/wapp_prng.c
prng: INCLUDES = common prng
prng: install

testing: LIB_SRC = src/testing/wapp_testing.c
testing: INCLUDES = common os testing
testing: install

uuid: LIB_SRC = src/uuid/wapp_uuid.c
uuid: INCLUDES = common base prng
uuid: install

clean:
	@rm -rf "$(BUILD_DIR)"
	@rm -rf "$(INCLUDE_INSTALL)"
	@rm -f "$(LIB_INSTALL)/$(LIB_STATIC_NAME)"

builddir:
	@mkdir -p "$(BUILD_DIR)"

build-c-test:
	$(CC) $(CSTD) $(CFLAGS) $(BUILD_FLAGS) $(TEST_INCLUDE) $(TEST_C_SRC) -o "$(TEST_C_OUT)"

run-c-test: build-c-test
	@echo -e "\n\033[34;1mRUNNING C TESTS\033[0m"
	@"$(TEST_C_OUT)"
	@rm "$(TEST_C_OUT)"

build-cc-test:
	$(CXX) $(CXXSTD) $(CFLAGS) $(BUILD_FLAGS) $(TEST_INCLUDE) $(TEST_CXX_SRC) "$(LIB_OUT)" -o "$(TEST_CXX_OUT)"

run-cc-test: build-cc-test
	@echo -e "\n\033[34;1mRUNNING C++ TESTS\033[0m"
	@export LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:"$(BUILD_DIR)" && "$(TEST_CXX_OUT)"
	@rm "$(TEST_CXX_OUT)"

install: build-lib
	@mkdir -p "$(LIB_INSTALL)"
	@cp -v "$(LIB_OUT)" "$(LIB_INSTALL)"
	@mkdir -p "$(INCLUDE_INSTALL)"
	@bash $(HEADER_INSTALL_CMD) $(LIB_SRC) "$(INCLUDE_INSTALL)" $(INCLUDES)

build-lib: builddir
	$(CC) -c $(CSTD) $(CFLAGS) $(BUILD_FLAGS) $(LIBFLAGS) $(LIB_SRC) -o "$(OBJ_OUT)"
	$(AR) r "$(LIB_OUT)" "$(OBJ_OUT)"
	@rm "$(OBJ_OUT)"
