# Force POSIX-compatible shell
SHELL = /bin/sh

# Build configuration
MIX_APP_PATH ?= ..
PROJECT_ROOT := $(abspath $(CURDIR)/..)
PREFIX = $(MIX_APP_PATH)/priv
VIX = $(PREFIX)/vix.so

# Compilation mode configuration
VIX_COMPILATION_MODE ?= PRECOMPILED_NIF_AND_LIBVIPS
VIX_COMPILATION_MODE := $(shell echo "$(VIX_COMPILATION_MODE)" | tr '[:lower:]' '[:upper:]')
VALID_COMPILATION_MODES := PRECOMPILED_NIF_AND_LIBVIPS PRECOMPILED_LIBVIPS PLATFORM_PROVIDED_LIBVIPS
ifeq ($(filter $(VIX_COMPILATION_MODE),$(VALID_COMPILATION_MODES)),)
$(error Invalid VIX_COMPILATION_MODE '$(VIX_COMPILATION_MODE)'. Valid modes: $(VALID_COMPILATION_MODES))
endif

# Build tools
CC ?= gcc
RM ?= rm -f
MKDIR_P ?= mkdir -p
PKG_CONFIG ?= pkg-config

# Optional custom libvips prefix for PLATFORM_PROVIDED_LIBVIPS mode
ifdef VIX_LIBVIPS_PREFIX
	ifneq ($(filter /%,$(VIX_LIBVIPS_PREFIX)),)
		VIX_LIBVIPS_PREFIX_ABS := $(VIX_LIBVIPS_PREFIX)
	else
		VIX_LIBVIPS_PREFIX_ABS := $(abspath $(PROJECT_ROOT)/$(VIX_LIBVIPS_PREFIX))
	endif
	VIPS_PKG_CONFIG_PATH := $(VIX_LIBVIPS_PREFIX_ABS)/lib/pkgconfig$(if $(PKG_CONFIG_PATH),:$(PKG_CONFIG_PATH))
	VIPS_PKG_CONFIG := env PKG_CONFIG_PATH="$(VIPS_PKG_CONFIG_PATH)" $(PKG_CONFIG)
else
	VIPS_PKG_CONFIG := $(PKG_CONFIG)
endif

# System detection and platform-specific configuration
UNAME_SYS := $(shell uname -s 2>/dev/null || echo Unknown)
UNAME_ARCH := $(shell uname -m 2>/dev/null || echo Unknown)

# Base compiler flags
BASE_CFLAGS = -O2 -Wall -Werror -Wextra -Wno-unused-parameter -Wmissing-prototypes -std=c11

# Platform-specific flags
ifeq ($(UNAME_SYS), Darwin)
	CFLAGS += $(BASE_CFLAGS)
	LDFLAGS += -flat_namespace -undefined suppress
else ifeq ($(UNAME_SYS), Linux)
	CFLAGS += $(BASE_CFLAGS)
endif

# flags that are common to all platform
CFLAGS += -D_POSIX_C_SOURCE=200809L -fPIC

# Erlang/OTP includes and libraries
CFLAGS += -I "$(ERTS_INCLUDE_DIR)" -I "$(ERL_INTERFACE_INCLUDE_DIR)"
LDLIBS += -L "$(ERL_INTERFACE_LIB_DIR)"
LDFLAGS += -shared

ifeq ($(VIX_COMPILATION_MODE), PLATFORM_PROVIDED_LIBVIPS)
	VIPS_CFLAGS := $(shell ${VIPS_PKG_CONFIG} --cflags vips 2>/dev/null)
	VIPS_LIBS := $(shell ${VIPS_PKG_CONFIG} --libs vips 2>/dev/null)
	VIPS_VERSION := $(shell ${VIPS_PKG_CONFIG} --modversion vips 2>/dev/null)
	CFLAGS += $(VIPS_CFLAGS)
	LDLIBS += $(VIPS_LIBS)
	PKG_CONFIG_CHECK = check-pkg-config

	ifdef VIX_LIBVIPS_PREFIX
		LDFLAGS += -Wl,-rpath,$(VIX_LIBVIPS_PREFIX_ABS)/lib
	endif
else
	PKG_CONFIG_CHECK =
	PRECOMPILED_LIBVIPS_PATH = $(PREFIX)/precompiled_libvips

	ifeq ($(VIX_COMPILATION_MODE), PRECOMPILED_LIBVIPS)
		# force fetching libvips if previously configured to use PRECOMPILED_NIF_AND_LIBVIPS
		# since the precompiled vix does not bundle `include`
		PRECOMPILED_LIBVIPS_PREREQUISITE = $(PRECOMPILED_LIBVIPS_PATH)/include
	else
		PRECOMPILED_LIBVIPS_PREREQUISITE = $(PRECOMPILED_LIBVIPS_PATH)
	endif

	CFLAGS += -pthread
	CFLAGS += -I "$(PRECOMPILED_LIBVIPS_PATH)/include"
	CFLAGS += -I "$(PRECOMPILED_LIBVIPS_PATH)/lib/glib-2.0/include"
	CFLAGS += -I "$(PRECOMPILED_LIBVIPS_PATH)/include/glib-2.0"
	LDLIBS += -L "$(PRECOMPILED_LIBVIPS_PATH)/lib"

	ifeq ($(UNAME_SYS), Darwin)
		LDFLAGS += -Wl,-rpath,@loader_path/precompiled_libvips/lib
		LDLIBS  += $(PRECOMPILED_LIBVIPS_PATH)/lib/libvips-cpp.*.dylib
	else ifeq ($(UNAME_SYS), Linux)
		LDFLAGS += -Wl,-s -Wl,--disable-new-dtags -Wl,-rpath='$$ORIGIN/precompiled_libvips/lib'
		LDLIBS  += $(PRECOMPILED_LIBVIPS_PATH)/lib/libvips-cpp.so.*
	endif
endif

# Verbosity control
V ?= 0
ifeq ($(V),0)
	Q = @
	SAY = @echo
else
	Q =
	SAY = @\#
endif

c_verbose_0 = @echo " CC    " $(<F);
c_verbose_1 =
c_verbose = $(c_verbose_$(V))

link_verbose_0 = @echo " LD    " $(@F);
link_verbose_1 =
link_verbose = $(link_verbose_$(V))

# Source files discovery
SRC := $(wildcard *.c g_object/*.c)
ifeq ($(SRC),)
	$(error No C source files found)
endif

# Object files (placed in source directory to avoid env conflicts)
OBJ := $(SRC:.c=.o)

# Dependency files for better incremental builds
DEP := $(SRC:.c=.d)

# Build configuration stamps to force rebuilds when relevant env changes
COMPILE_CONFIG = $(PREFIX)/compile.config
LINK_CONFIG = $(PREFIX)/link.config

$(COMPILE_CONFIG): FORCE | $(PREFIX)
	$(Q){ \
		printf '%s\n' \
			"CC=$(CC)" \
			"VIX_COMPILATION_MODE=$(VIX_COMPILATION_MODE)" \
			"VIX_LIBVIPS_PREFIX_ABS=$(VIX_LIBVIPS_PREFIX_ABS)" \
			"VIPS_VERSION=$(VIPS_VERSION)" \
			"CFLAGS=$(CFLAGS)"; \
	} > "$@.tmp"
	$(Q)if [ ! -f "$@" ] || ! cmp -s "$@.tmp" "$@"; then \
		mv "$@.tmp" "$@"; \
	else \
		$(RM) "$@.tmp"; \
	fi

$(LINK_CONFIG): FORCE | $(PREFIX)
	$(Q){ \
		printf '%s\n' \
			"CC=$(CC)" \
			"VIX_COMPILATION_MODE=$(VIX_COMPILATION_MODE)" \
			"VIX_LIBVIPS_PREFIX_ABS=$(VIX_LIBVIPS_PREFIX_ABS)" \
			"VIPS_VERSION=$(VIPS_VERSION)" \
			"LDFLAGS=$(LDFLAGS)" \
			"LDLIBS=$(LDLIBS)"; \
	} > "$@.tmp"
	$(Q)if [ ! -f "$@" ] || ! cmp -s "$@.tmp" "$@"; then \
		mv "$@.tmp" "$@"; \
	else \
		$(RM) "$@.tmp"; \
	fi

# Main targets
all: check-env $(PKG_CONFIG_CHECK) $(VIX)
	$(SAY) "Build complete: $(VIX)"

install: all

calling_from_make:
	$(Q)cd .. && mix compile

# Environment validation
check-env:
	@if [ -z "$(ERTS_INCLUDE_DIR)" ]; then \
		echo "Error: ERTS_INCLUDE_DIR not set"; \
		exit 1; \
	fi
	@if [ -z "$(ERL_INTERFACE_INCLUDE_DIR)" ]; then \
		echo "Error: ERL_INTERFACE_INCLUDE_DIR not set"; \
		exit 1; \
	fi
	@if [ -z "$(ERL_INTERFACE_LIB_DIR)" ]; then \
		echo "Error: ERL_INTERFACE_LIB_DIR not set"; \
		exit 1; \
	fi

check-pkg-config:
	@if ! command -v ${PKG_CONFIG} >/dev/null 2>&1; then \
		echo "Error: ${PKG_CONFIG} (PKG_CONFIG) not found but required for PLATFORM_PROVIDED_LIBVIPS"; \
		exit 1; \
	fi
	@if ! ${VIPS_PKG_CONFIG} --exists vips; then \
		echo "Error: vips not found via pkg-config"; \
		if [ -n "$(VIX_LIBVIPS_PREFIX_ABS)" ]; then \
			echo "Checked custom libvips prefix: $(VIX_LIBVIPS_PREFIX_ABS)"; \
		fi; \
		echo "Please install libvips development headers"; \
		exit 1; \
	fi
	@if [ -n "$(VIX_LIBVIPS_PREFIX_ABS)" ]; then \
		resolved_prefix=$$(${VIPS_PKG_CONFIG} --variable=prefix vips 2>/dev/null || true); \
		if [ -z "$$resolved_prefix" ]; then \
			echo "Error: failed to resolve the libvips prefix via pkg-config"; \
			exit 1; \
		fi; \
		if [ "$$resolved_prefix" != "$(VIX_LIBVIPS_PREFIX_ABS)" ]; then \
			if [ -d "$$resolved_prefix" ] && [ -d "$(VIX_LIBVIPS_PREFIX_ABS)" ] && [ "$$resolved_prefix" -ef "$(VIX_LIBVIPS_PREFIX_ABS)" ]; then \
				:; \
			else \
				echo "Error: libvips resolved to '$$resolved_prefix' instead of '$(VIX_LIBVIPS_PREFIX_ABS)'"; \
				echo "VIX_LIBVIPS_PREFIX must point to the libvips installation that should be used"; \
				exit 1; \
			fi; \
		fi; \
	fi

# Create output directory
$(PREFIX):
	$(Q)$(MKDIR_P) "$@"

$(OBJ): Makefile $(COMPILE_CONFIG) $(PRECOMPILED_LIBVIPS_PREREQUISITE)

# Dependency generation
%.d: %.c
	$(Q)$(CC) $(CFLAGS) -MM -MT $(@:.d=.o) $< > $@

# Include dependency files only for build goals. Regenerating them needs the
# Erlang include paths, which are not available for direct clean/help targets.
NO_DEP_GOALS := clean clean_precompiled_libvips help debug
ifneq ($(filter-out $(NO_DEP_GOALS),$(or $(MAKECMDGOALS),all)),)
-include $(DEP)
endif

# Object file compilation with dependency tracking
%.o: %.c
	$(c_verbose)$(CC) -c $(CFLAGS) -MMD -MP -o $@ $<

# Final linking
$(VIX): $(PREFIX) $(LINK_CONFIG) $(OBJ)
	$(link_verbose)$(CC) $(OBJ) -o $@ $(LDFLAGS) $(LDLIBS)

# Precompiled libvips setup
$(PRECOMPILED_LIBVIPS_PREREQUISITE):
	$(SAY) "Setting up precompiled libvips..."
	$(Q) elixir ../build_scripts/precompiler.exs "$(PREFIX)"

# Clean targets
clean:
	$(SAY) "Cleaning build artifacts..."
	$(Q)$(RM) $(VIX) $(OBJ) $(DEP) $(COMPILE_CONFIG) $(LINK_CONFIG)

clean_precompiled_libvips: clean
	$(SAY) "Cleaning precompiled libvips..."
	$(Q)$(RM) "$(PREFIX)"/libvips-*.tar.gz
	$(Q)$(RM) -r "$(PREFIX)/precompiled_libvips"

# Help target
help:
	@echo "Available targets:"
	@echo "  all                        - Build the NIF library"
	@echo "  clean                      - Clean build artifacts"
	@echo "  clean_precompiled_libvips  - Clean precompiled libvips"
	@echo "  check-env                  - Validate build environment"
	@echo "  help                       - Show this help"
	@echo ""
	@echo "Build configuration:"
	@echo "  VIX_COMPILATION_MODE: $(VIX_COMPILATION_MODE)"
	@echo "  VIX_LIBVIPS_PREFIX: $(VIX_LIBVIPS_PREFIX_ABS)"
	@echo "  System: $(UNAME_SYS) $(UNAME_ARCH)"
	@echo "  CC: $(CC)"
	@echo ""
	@echo "Set V=1 for verbose output"

# Debug target
debug:
	@echo "=== Build Configuration ==="
	@echo "VIX_COMPILATION_MODE: $(VIX_COMPILATION_MODE)"
	@echo "UNAME_SYS: $(UNAME_SYS)"
	@echo "UNAME_ARCH: $(UNAME_ARCH)"
	@echo "CC: $(CC)"
	@echo "CFLAGS: $(CFLAGS)"
	@echo "LDFLAGS: $(LDFLAGS)"
	@echo "LDLIBS: $(LDLIBS)"
	@echo "PKG_CONFIG: $(PKG_CONFIG)"
	@echo "VIPS_PKG_CONFIG: $(VIPS_PKG_CONFIG)"
	@echo "VIPS_VERSION: $(VIPS_VERSION)"
	@echo "SRC: $(SRC)"
	@echo "OBJ: $(OBJ)"
	@echo "PREFIX: $(PREFIX)"
	@echo "VIX: $(VIX)"

FORCE:

.PHONY: FORCE all clean clean_precompiled_libvips calling_from_make install check-env check-pkg-config help debug
