CXX := g++
FC := gfortran
LIBNAME := libSQUIC

# If you want to install the interface packages globally (may require admin access), set this to true
R_PYTHON_INTERFACE_GLOBAL_INSTALL?=false

# If R_PYTHON_INTERFACE_GLOBAL_INSTALL is false, this path is used of local installatino of R and the defualt --user based installation for python is used
R_LIBS_USER :=~/R/$(shell Rscript --vanilla -e 'cat(paste0(R.Version()$$platform, "-library/", R.Version()$$major))')

CXXFLAGS = -w -Wall -fPIC -O3 -D_LONG_INTEGER_ -DMKL_ILP64 -m64 -I extern/include 
FFLAGS   = -w -Wall -fPIC -O3 -fimplicit-none -fdefault-integer-8 -I extern/include
LDFLAGS  = 

# Detect the operating system
UNAME_S := $(shell uname -s)

# Compiler flags & library name
ifeq ($(UNAME_S), Darwin) # Apple

	OMP_INCL = -I/usr/local/opt/libomp/include
	OMP_LIB  = -L/usr/local/opt/libomp/lib -lomp

    # Check for libomp with Macports and Homebrew directories and update flags
    ifeq ($(shell [ -d "/opt/local/include/libomp" ] && echo exists), exists)
        OMP_INCL = -I/opt/local/include/libomp
        OMP_LIB  = -L/opt/local/lib/libomp -lomp
    else ifeq ($(shell [ -d "/opt/homebrew/opt/libomp/include" ] && echo exists), exists)
        OMP_INCL = -I/opt/homebrew/opt/libomp/include
        OMP_LIB  = -L/opt/homebrew/opt/libomp/lib -lomp
    endif

    CXXFLAGS += -static-libstdc++ $(OMP_INCL)
    FFLAGS   += -static-libgfortran  $(OMP_INCL)
    LDFLAGS  += -framework Accelerate -Xpreprocessor -fopenmp $(OMP_LIB)
    LIBSUFFIX = dylib

else # Linux
    CXXFLAGS += -static-libstdc++ -static-libgcc
    FFLAGS   += -static-libgfortran
    LDFLAGS  += -lblas -llapack -fopenmp # Change "-lblas -llapack" if you are using a different type of BLAS or LAPACK.  
    LIBSUFFIX = so
endif

# Base directory
BASE_DIR := $(CURDIR)

# Source directory
SRC_DIR = src

# Source files - Automatically detect all .cpp and .f90 files in the source directory
CPP_SRCS = $(wildcard $(SRC_DIR)/*.cpp)
F_SRCS = $(wildcard $(SRC_DIR)/*.f)

# Object files
CPP_OBJS = $(CPP_SRCS:.cpp=.o)
F_OBJS = $(F_SRCS:.f=.o)

# SuiteSparse Static Libraries
EXTERN_DIR = extern
STATIC_LIBS = $(addprefix $(EXTERN_DIR)/lib/, \
    libcholmod.a libsuitesparseconfig.a libcolamd.a libamd.a \
    libcamd.a libccolamd.a libmetis.a)

# Makefile targets
all: dependencies squic

squic: $(CPP_OBJS) $(F_OBJS)
	@echo ">> Linking libSQUIC"
	$(CXX) -shared -o $(LIBNAME).$(LIBSUFFIX) $(CPP_OBJS) $(F_OBJS) $(STATIC_LIBS) $(LDFLAGS)

# C++ source to object file
%.o: %.cpp
	@echo ">> Compiling libSQUIC $@"
	$(CXX) $(CXXFLAGS) -c $< -o $@

# Fortran source to object file
%.o: %.f
	@echo ">> Compiling libSQUIC $@"
	$(FC) $(FFLAGS) -c $< -o $@

dependencies:
	mkdir -p $(EXTERN_DIR)/lib $(EXTERN_DIR)/bin $(EXTERN_DIR)/include

	@echo ">> Compiling Metis prefix=$(BASE_DIR)/$(EXTERN_DIR)"
	$(MAKE) -C $(EXTERN_DIR)/src/metis-5.1.0 config prefix=$(BASE_DIR)/$(EXTERN_DIR) MAKEFLAGS=$(MAKEFLAGS)
	$(MAKE) -C $(EXTERN_DIR)/src/metis-5.1.0 install MAKEFLAGS=$(MAKEFLAGS)

	@echo ">> Compiling AMD"
	$(MAKE) -C $(EXTERN_DIR)/src/AMD static MAKEFLAGS=$(MAKEFLAGS)
	cp $(EXTERN_DIR)/src/AMD/Lib/*.a $(EXTERN_DIR)/lib 
	cp $(EXTERN_DIR)/src/AMD/Include/*.h $(EXTERN_DIR)/include

	@echo ">> Compiling CAMD"
	$(MAKE) -C $(EXTERN_DIR)/src/CAMD static MAKEFLAGS=$(MAKEFLAGS)
	cp $(EXTERN_DIR)/src/CAMD/Lib/*.a $(EXTERN_DIR)/lib
	cp $(EXTERN_DIR)/src/CAMD/Include/*.h $(EXTERN_DIR)/include

	@echo ">> Compiling CCOLAMD"
	$(MAKE) -C $(EXTERN_DIR)/src/CCOLAMD static MAKEFLAGS=$(MAKEFLAGS)
	cp $(EXTERN_DIR)/src/CCOLAMD/Lib/*.a $(EXTERN_DIR)/lib
	cp $(EXTERN_DIR)/src/CCOLAMD/Include/*.h $(EXTERN_DIR)/include

	@echo ">> Compiling CHOLMOD"
	$(MAKE) -C $(EXTERN_DIR)/src/CHOLMOD static MAKEFLAGS=$(MAKEFLAGS)
	cp $(EXTERN_DIR)/src/CHOLMOD/Lib/*.a $(EXTERN_DIR)/lib
	cp $(EXTERN_DIR)/src/CHOLMOD/Include/*.h $(EXTERN_DIR)/include

	@echo ">> Compiling COLAMD"
	$(MAKE) -C $(EXTERN_DIR)/src/COLAMD static MAKEFLAGS=$(MAKEFLAGS)
	cp $(EXTERN_DIR)/src/COLAMD/Lib/*.a $(EXTERN_DIR)/lib
	cp $(EXTERN_DIR)/src/COLAMD/Include/*.h $(EXTERN_DIR)/include

	@echo ">> Compiling SuiteSparse_config"
	$(MAKE) -C $(EXTERN_DIR)/src/SuiteSparse_config static MAKEFLAGS=$(MAKEFLAGS)
	cp $(EXTERN_DIR)/src/SuiteSparse_config/*.a $(EXTERN_DIR)/lib
	cp $(EXTERN_DIR)/src/SuiteSparse_config/*.h $(EXTERN_DIR)/include

install:
	@echo ">> Install path: $(HOME)"
	@echo ">> Architecture: $(shell uname -m)_$(shell uname -s)"
	cp $(LIBNAME).$(LIBSUFFIX) $(HOME)

install_r:
	@echo ">> Install R Interface"
	@if [ "$(R_PYTHON_INTERFACE_GLOBAL_INSTALL)" = "true" ]; then \
		echo ">> Global R interface library installation, using standard installation folders"; \
		Rscript -e 'options(repos = c(CRAN = "http://cran.rstudio.com")); install.packages(c("Matrix","Rcpp", "RcppArmadillo"))'; \
		Rscript -e 'install.packages("../r", repos = NULL, type = "source")'; \
	else \
		echo ">> Local R interface library installation"; \
		echo ">> R library installation at \"$(R_LIBS_USER)\"; see \"$(HOME)/.Renviron\" for details"; \
		echo "R_LIBS_USER=$(R_LIBS_USER)" >> "$(HOME)/.Renviron"; \
		mkdir -p $(R_LIBS_USER); \
		Rscript -e 'options(repos = c(CRAN = "http://cran.rstudio.com")); install.packages(c("Matrix","Rcpp", "RcppArmadillo"))'; \
		Rscript -e 'install.packages("../r", repos = NULL, type = "source")'; \
	fi

	@echo ">> Running basic R demo"
	Rscript demo.R

install_py:
	@echo ">> Install Python Interface"
	@if [ "$(R_PYTHON_INTERFACE_GLOBAL_INSTALL)" = "true" ]; then \
		echo ">> Global Python interface library installation, using standard installation folders"; \
		pip3 install scipy numpy; \
		pip3 install ../python; \
	else \
		echo ">> Local Python interface library installation"; \
		echo ">> Python library installation using \"--user\""; \
		pip3  install scipy numpy --user; \
		pip3 install ../python --user; \
	fi

	@echo ">> Running basic Python demo"
	python3 demo.py

clean:
	rm -f $(CPP_OBJS) $(F_OBJS) $(LIBNAME).$(LIBSUFFIX)
	rm ../r/src/*.o 
	rm ../r/src/*.so 

purge:
	rm -f $(CPP_OBJS) $(F_OBJS) $(LIBNAME).$(LIBSUFFIX)
	rm -rf $(EXTERN_DIR)/lib $(EXTERN_DIR)/bin $(EXTERN_DIR)/include
	$(MAKE) -C $(EXTERN_DIR)/src/metis-5.1.0 distclean
	$(MAKE) -C $(EXTERN_DIR)/src/AMD clean
	$(MAKE) -C $(EXTERN_DIR)/src/CAMD clean
	$(MAKE) -C $(EXTERN_DIR)/src/CCOLAMD clean
	$(MAKE) -C $(EXTERN_DIR)/src/CHOLMOD clean
	$(MAKE) -C $(EXTERN_DIR)/src/COLAMD clean
	$(MAKE) -C $(EXTERN_DIR)/src/SuiteSparse_config clean

.PHONY: all squic dependencies install install_r install_py clean purge