# Set UNAME to detect the operating system
UNAME := $(shell uname)

# # Target to check if BLAS and LAPACK are installed
# check_libs:
# 	@echo "Checking for BLAS and LAPACK..."
# 	@echo "int main() { return 0; }" > conftest.c
# 	@$(CXX) conftest.c -o conftest $(BLAS_LIB) $(LAPACK_LIB) $(FLIBS) > /dev/null 2>&1 || \
# 	(echo "Error: BLAS and/or LAPACK and/or FLIBS not found. Please install them or set the correct paths."; exit 1)
# 	@rm -f conftest.c conftest
# 	@echo "BLAS and LAPACK and FLIBS found."

# Make the shared object
$(SHLIB): $(OBJECTS)

# Compile Suitesparse
ssdir = SuiteSparse
sslib = $(sslib1) $(sslib2) $(sslib3) $(sslib4) $(sslib5) $(sslib7)
sslib1 = $(ssdir)/CHOLMOD/CHOLMOD.a
sslib2 = $(ssdir)/CCOLAMD/CCOLAMD.a
sslib3 = $(ssdir)/CAMD/CAMD.a
sslib4 = $(ssdir)/COLAMD/COLAMD.a
sslib5 = $(ssdir)/AMD/AMD.a
sslib7 = $(ssdir)/SuiteSparse_config/SuiteSparse_config.a

ssenv = \
	CC="$(CC)" \
	CPPFLAGS="$(CPPFLAGS) -I\"$(R_INCLUDE_DIR)\" -DNDEBUG -DNPRINT -DNTIMER -DNPARTITION" \
	CFLAGS="$(CFLAGS) $(CPICFLAGS) $(C_VISIBILITY)" \
	AR="$(AR)" \
	RANLIB="$(RANLIB)"


PKG_CPPFLAGS = -I"$(ssdir)/SuiteSparse_config"
PKG_CFLAGS = $(C_VISIBILITY)
PKG_LIBS = $(sslib) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)
PKG_LIBS += $(SHLIB_OPENMP_CXXFLAGS)

PKG_CPPFLAGS += -Iinclude -I../inst/include # -DNDEBUG -DDEBUG
PKG_CXXFLAGS += $(SHLIB_OPENMP_CXXFLAGS)
# Emit the .d files that DEPFILES below -includes. Without this nothing ever
# generates them, so editing a header does not rebuild the sources that include
# it: an incremental build then links objects compiled against two different
# definitions of the same class, which corrupts memory at run time rather than
# failing to build. -MP keeps a deleted header from breaking the next build.
PKG_CXXFLAGS += -MMD -MP

## NB: INSTALL does not clean subdirectories between subarchitectures
$(sslib1):
	cd $(@D) && $(MAKE) clean all $(ssenv)

$(sslib2):
	cd $(@D) && $(MAKE) clean all $(ssenv)

$(sslib3):
	cd $(@D) && $(MAKE) clean all $(ssenv)

$(sslib4):
	cd $(@D) && $(MAKE) clean all $(ssenv)

$(sslib5):
	cd $(@D) && $(MAKE) clean all $(ssenv)

$(sslib7):
	cd $(@D) && $(MAKE) clean all $(ssenv)


# macOS configuration
ifeq ($(UNAME), Darwin)
  # Detect Homebrew prefix
  HOMEBREW_PREFIX := $(shell brew --prefix 2>/dev/null)
  ifeq ($(HOMEBREW_PREFIX),)
    ifneq ($(wildcard /opt/homebrew/bin/brew),)
      HOMEBREW_PREFIX := /opt/homebrew
    else
      HOMEBREW_PREFIX := /usr/local
    endif
  endif

  # Override FLIBS only if Homebrew gfortran is available
  GFORTRAN_LIB := $(HOMEBREW_PREFIX)/lib/gcc/current
  ifneq ($(wildcard $(GFORTRAN_LIB)/libgfortran*),)
    FLIBS = -L$(GFORTRAN_LIB) -lgfortran -lquadmath
  endif

  ARCH := $(shell uname -m)
  ifeq ($(ARCH),x86_64)
    ifdef MKLROOT
      # Intel macOS with MKL: use MKL (includes Intel OpenMP via -liomp5)
      PKG_CPPFLAGS += -DUSEMKL -I$(MKLROOT)/include
      PKG_LIBS += -L$(MKLROOT)/lib -lmkl_rt -liomp5 -lpthread -lm
      $(info MKL detected on Intel macOS, enabling Pardiso.)
    endif
  endif

  # OpenMP on macOS: link Homebrew libomp by full path (if installed).
  # Uses full path to avoid picking up R's potentially outdated bundled libomp.
  # If libomp is not installed, OpenMP is silently disabled (#pragma omp ignored).
  ifndef NGME_SKIP_LIBOMP_LINK
    LIBOMP_DYLIB := $(HOMEBREW_PREFIX)/opt/libomp/lib/libomp.dylib
    ifneq ($(wildcard $(LIBOMP_DYLIB)),)
      ifeq ($(SHLIB_OPENMP_CXXFLAGS),)
        PKG_CXXFLAGS += -Xclang -fopenmp
      endif
      # Apple clang does not ship omp.h; it comes from the Homebrew libomp keg.
      PKG_CPPFLAGS += -I$(HOMEBREW_PREFIX)/opt/libomp/include
      PKG_LIBS += $(LIBOMP_DYLIB)
    endif
  endif
endif

# Enable MKL/Pardiso when MKLROOT is provided (e.g., oneAPI) on non-macOS
ifneq ($(UNAME), Darwin)
  ifdef MKLROOT
    PKG_CPPFLAGS += -DUSEMKL -I$(MKLROOT)/include
    PKG_LIBS += -L$(MKLROOT)/lib/intel64 \
               -lmkl_rt -lmkl_gnu_thread -lmkl_core \
               -lgomp -lpthread -lm -ldl
    $(info MKL detected, enabling Pardiso.)
  endif
endif

# TESTS = test/test-algebra.o  test/test-opt.o
UTILS = util/GIG.o util/rgig.o util/MatrixAlgebra.o util/solver.o util/probing.o
# $(wildcard util/*.o)

LATENTS = latents/ou.o latents/matern.o latents/tensorprod.o latents/randeff.o latents/bivar.o latents/generic.o latents/generic_ns.o latents/arma.o latents/rcallback.o latents/fractional/fractional_operators.o
# $(wildcard latents/*.o)

OBJECTS = $(sslib) RcppExports.o prior.o noise.o sample_rGIG.o estimate.o optimizer.o block.o group_cv.o latent.o ngme.o loglikelihood.o operator.o \
   $(UTILS) $(TESTS) $(LATENTS)


DEPFILES = $(filter %.d,$(OBJECTS:.o=.d))
-include $(DEPFILES)

# The .d files rebuild an object when a header it includes changes, but nothing
# rebuilds it when the FLAGS change. That gap is not theoretical here: pkgload
# -- devtools::test(), load_all(), testthat::test_local() -- compiles THIS
# directory with its own flags, leaving objects built without -DNDEBUG behind,
# and a later R CMD INSTALL reuses them. The library then links
# assertion-enabled and assertion-disabled objects together, behaves like
# neither build, and presents as memory corruption rather than as a stale
# object. Stamping the flags into a file every object depends on turns that
# silent mix into an ordinary rebuild.
#
# ALL_CPPFLAGS / ALL_CXXFLAGS are R's fully assembled compile flags and are
# where -DNDEBUG actually lives; the bare CPPFLAGS/CXXFLAGS are empty here and
# would stamp nothing that matters. They must be expanded in a RECIPE, since R
# assembles them only after reading this file -- at parse time they stamp an
# empty string and catch nothing. The stamp is rewritten only when the flags
# really differ, so an unchanged build still recompiles nothing.
NGME_FLAGSTAMP = .ngme-build-flags
NGME_FLAGS = $(subst ",,$(CXX)|$(ALL_CPPFLAGS)|$(ALL_CXXFLAGS))
.PHONY: ngme-flagcheck
$(NGME_FLAGSTAMP): ngme-flagcheck
	@printf '%s' '$(NGME_FLAGS)' | cmp -s - $@ 2>/dev/null || \
	  printf '%s' '$(NGME_FLAGS)' > $@

# Only this package's own objects; the SuiteSparse archives are built by their
# own recipes with their own flags and must not be dragged into this.
$(filter-out $(sslib),$(OBJECTS)): $(NGME_FLAGSTAMP)

# Provide recipe to remove all objects
clean:
	@rm -f *.o util/*.o latents/*.o latents/fractional/*.o
	@rm -f *.d util/*.d latents/*.d latents/fractional/*.d
	@rm -f $(NGME_FLAGSTAMP)

.PHONY: clean
