# MLX Erlang NIF Makefile

# Detect OS
UNAME_S := $(shell uname -s)

# Compiler
CXX = clang++
CC = clang

# C++ Standard
CXXSTD = -std=c++17

# Optimization
OPTIMIZE = -O3 -DNDEBUG

# Warning flags
WARNINGS = -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers

# Debug flags (for debug builds)
DEBUG_FLAGS = -g -DDEBUG

# Erlang/OTP paths
ERL_INTERFACE_DIR = $(shell erl -eval 'io:format("~s~n", [code:lib_dir(erl_interface)])' -s init stop -noshell)
ERTS_DIR = $(shell erl -eval 'io:format("~s~n", [code:root_dir()])' -s init stop -noshell)/erts-$(shell erl -eval 'io:format("~s~n", [erlang:system_info(version)])' -s init stop -noshell)

# Include directories
INCLUDES = -I$(ERL_INTERFACE_DIR)/include \
           -I$(ERTS_DIR)/include

# Platform-specific settings
ifeq ($(UNAME_S),Darwin)
    # macOS settings
    INCLUDES += -I/opt/homebrew/include \
                -I/usr/local/include \
                -I/opt/homebrew/include/mlx
    
    LDFLAGS = -L/opt/homebrew/lib \
              -L/usr/local/lib \
              -lmlx \
              -framework Accelerate \
              -framework Metal \
              -framework MetalKit \
              -framework MetalPerformanceShaders \
              -framework Foundation \
              -undefined dynamic_lookup \
              -shared
    
    # Universal binary support
    ARCH_FLAGS = -arch arm64 -arch x86_64
    
    SO_EXT = .so
    SHARED_FLAGS = -fPIC -shared
endif

ifeq ($(UNAME_S),Linux)
    # Linux settings
    INCLUDES += -I/usr/local/include \
                -I/usr/include/mlx \
                -I/usr/local/cuda/include
    
    LDFLAGS = -L/usr/local/lib \
              -L/usr/local/cuda/lib64 \
              -lmlx \
              -lm \
              -lpthread \
              -lcudart \
              -lcublas \
              -shared
    
    SO_EXT = .so
    SHARED_FLAGS = -fPIC -shared
endif

# Compiler flags
CXXFLAGS = $(CXXSTD) $(OPTIMIZE) $(WARNINGS) $(INCLUDES) $(SHARED_FLAGS) $(ARCH_FLAGS)
CFLAGS = $(OPTIMIZE) $(WARNINGS) $(INCLUDES) $(SHARED_FLAGS) $(ARCH_FLAGS)

# Source files
CPP_SOURCES = mlx_nif.cpp \
              mlx_advanced_nif.cpp \
              mlx_nn_nif.cpp \
              mlx_optimizers_nif.cpp \
              mlx_random_nif.cpp \
              mlx_transforms_nif.cpp \
              mlx_fft_nif.cpp \
              mlx_io_nif.cpp \
              mlx_streaming_nif.cpp \
              mlx_metal_kernels.cpp

# Object files
OBJECTS = $(CPP_SOURCES:.cpp=.o)

# Target library
TARGET = ../priv/mlx_nif$(SO_EXT)

# Default target
all: $(TARGET)

# Create priv directory if it doesn't exist
../priv:
	mkdir -p ../priv

# Build the shared library
$(TARGET): ../priv $(OBJECTS)
	$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
	@echo "Built MLX NIF: $@"

# Compile C++ source files
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

# Debug build
debug: CXXFLAGS += $(DEBUG_FLAGS)
debug: OPTIMIZE = -O0
debug: $(TARGET)

# Clean build artifacts
clean:
	rm -f *.o $(TARGET)
	@echo "Cleaned build artifacts"

# Install MLX dependencies (macOS with Homebrew)
install-deps-macos:
	brew install mlx
	@echo "Installed MLX dependencies for macOS"

# Install MLX dependencies (Linux)
install-deps-linux:
	# This would typically involve building MLX from source
	@echo "Please install MLX from source on Linux"
	@echo "See: https://github.com/ml-explore/mlx"

# Check if MLX is available
check-mlx:
	@echo "Checking for MLX installation..."
ifeq ($(UNAME_S),Darwin)
	@if [ -f /opt/homebrew/lib/libmlx.dylib ] || [ -f /usr/local/lib/libmlx.dylib ]; then \
		echo "✓ MLX found"; \
	else \
		echo "✗ MLX not found. Run 'make install-deps-macos'"; \
		exit 1; \
	fi
endif
ifeq ($(UNAME_S),Linux)
	@if [ -f /usr/local/lib/libmlx.so ]; then \
		echo "✓ MLX found"; \
	else \
		echo "✗ MLX not found. Please install MLX from source"; \
		exit 1; \
	fi
endif

# Test the NIF
test: $(TARGET)
	cd .. && rebar3 eunit

# Format code
format:
	clang-format -i *.cpp *.h

# Static analysis
analyze:
	clang-static-analyzer $(CXXFLAGS) $(CPP_SOURCES)

# Memory check (requires valgrind on Linux)
memcheck: $(TARGET)
ifeq ($(UNAME_S),Linux)
	cd .. && valgrind --tool=memcheck --leak-check=full erl -pa _build/default/lib/*/ebin -eval "mlx_nif:test_basic()" -s init stop
else
	@echo "Memory checking with valgrind is only available on Linux"
endif

# Benchmark build (optimized for performance)
benchmark: OPTIMIZE = -O3 -march=native -mtune=native -DNDEBUG
benchmark: $(TARGET)

# Profile build (with profiling symbols)
profile: CXXFLAGS += -pg
profile: LDFLAGS += -pg
profile: $(TARGET)

# Print build configuration
config:
	@echo "Build Configuration:"
	@echo "  OS: $(UNAME_S)"
	@echo "  Compiler: $(CXX)"
	@echo "  C++ Standard: $(CXXSTD)"
	@echo "  Optimization: $(OPTIMIZE)"
	@echo "  Includes: $(INCLUDES)"
	@echo "  LDFLAGS: $(LDFLAGS)"
	@echo "  Target: $(TARGET)"

# Help
help:
	@echo "MLX Erlang NIF Build System"
	@echo ""
	@echo "Targets:"
	@echo "  all          - Build the NIF library (default)"
	@echo "  debug        - Build with debug symbols"
	@echo "  clean        - Remove build artifacts"
	@echo "  check-mlx    - Check if MLX is installed"
	@echo "  install-deps-macos - Install MLX via Homebrew (macOS)"
	@echo "  install-deps-linux - Instructions for Linux"
	@echo "  test         - Build and run tests"
	@echo "  format       - Format source code"
	@echo "  analyze      - Run static analysis"
	@echo "  memcheck     - Run memory leak detection (Linux only)"
	@echo "  benchmark    - Build optimized for benchmarking"
	@echo "  profile      - Build with profiling enabled"
	@echo "  config       - Show build configuration"
	@echo "  help         - Show this help"

# Dependencies
mlx_nif.o: mlx_nif.cpp mlx_nif.h
mlx_advanced_nif.o: mlx_advanced_nif.cpp mlx_nif.h
mlx_nn_nif.o: mlx_nn_nif.cpp mlx_nif.h
mlx_optimizers_nif.o: mlx_optimizers_nif.cpp mlx_nif.h
mlx_random_nif.o: mlx_random_nif.cpp mlx_nif.h
mlx_transforms_nif.o: mlx_transforms_nif.cpp mlx_nif.h
mlx_fft_nif.o: mlx_fft_nif.cpp mlx_nif.h
mlx_io_nif.o: mlx_io_nif.cpp mlx_nif.h
mlx_streaming_nif.o: mlx_streaming_nif.cpp mlx_nif.h
mlx_metal_kernels.o: mlx_metal_kernels.cpp mlx_nif.h

.PHONY: all debug clean install-deps-macos install-deps-linux check-mlx test format analyze memcheck benchmark profile config help