#  Copyright (C) 2017 The authors of Py-ChemShell
#
#  This file is part of Py-ChemShell.
#
#  Py-ChemShell is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation, either version 3 of the
#  License, or (at your option) any later version.
#
#  Py-ChemShell is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with Py-ChemShell.  If not, see
#  <http://www.gnu.org/licenses/>.

# YL 26/09/2020: latest repository versions
#                CMake 3.10 on Ubuntu 18.04
#                CMake 3.16 on Ubuntu 20.04
cmake_minimum_required(VERSION 3.16)

# YL 26/09/2020: enable VERSION variables in project()
cmake_policy(SET CMP0048 NEW)

# set project name and enable languages
project(ChemShell
        VERSION       25.0
        DESCRIPTION  "Py-ChemShell: an open source, python-based computational chemistry environment."
        HOMEPAGE_URL "www.chemshell.org"
        LANGUAGES     C Fortran)

# YL 23/02/2021: save a timestamp
string(TIMESTAMP CHEMSH_COMPILE_TIME "%H:%M:%S\\ %d-%m-%Y")

# find_package(MPI) of CMake 3.19 fails if CXX is not enabled
if(NOT ${CMAKE_CXX_COMPILER} STREQUAL "")
    enable_language(CXX OPTIONAL)
endif()

# path to additional CMake modules
set(CMAKE_MODULE_PATH ${CHEMSH_DIR}/chemsh/utils/cmake)

find_package(Threads)

# ChemShell root dir
get_filename_component(CHEMSH_DIR ${CMAKE_CURRENT_LIST_DIR}/.. ABSOLUTE)
# this variable will be written into the embedded Python interpreter
set(CHEMSH_PATH ${CHEMSH_DIR})
set(CHEMSH_TEST ${CHEMSH_DIR}/tests/test.py)

# utils
include(${CHEMSH_DIR}/chemsh/utils/cmake/colours.cmake)
include(${CHEMSH_DIR}/chemsh/utils/cmake/static_libs.cmake)
include(${CHEMSH_DIR}/chemsh/utils/cmake/get_module.cmake)


# 1st step: change the compilers and quit
# read in the switch from a file generated by setup script, as when CMAKE_Fortran_COMPILER or CMAKE_C_COMPILER is changed (e.g., when switching between GNU and Intel compilers for different builds), a re-run of CMake configuring is imposed and all command-line variables, even including ${INIT_MODE} if given by subprocess.run() in setup, are lost (a known CMake issue)!

file(STRINGS "init.txt" INIT_MODE)
if(INIT_MODE)
    # delete saved compiler variables
    if(EXISTS ${CHEMSH_DIR}/chemsh/CMakeFiles/${CMAKE_VERSION}/CMakeCCompiler.cmake)
        file(REMOVE ${CHEMSH_DIR}/chemsh/CMakeFiles/${CMAKE_VERSION}/CMakeCCompiler.cmake)
    endif()
    if(EXISTS ${CHEMSH_DIR}/chemsh/CMakeFiles/${CMAKE_VERSION}/CMakeFortranCompiler.cmake)
        file(REMOVE ${CHEMSH_DIR}/chemsh/CMakeFiles/${CMAKE_VERSION}/CMakeFortranCompiler.cmake)
    endif()
    return()
endif()

# 2nd step: reconfigure with full specifications. as changed/unchanged compilers have already been written in CMakeCache.txt, a restart will not happen.

# YL FIXME: multiple MPI in system

# do not use system runtime LD_LIBRARY_PATH
#set(CMAKE_SKIP_RPATH TRUE)
#set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
#set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
#set(CMAKE_SKIP_BUILD_RPATH TRUE)

#set(CMAKE_Fortran_COMPILER ${FC} CACHE FILEPATH ${FC})
#set(CMAKE_C_COMPILER       ${CC} CACHE FILEPATH ${CC})

# CMake may think some platforms (e.g., ARCHER) do not support dynamic linking
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)

# YL 03/03/2021: allow overriding the architecture by setup command (useful for fake compiler such as CPE-GNU ftn)
if("${CHEMSH_ARCH}" STREQUAL "")
    if(${CMAKE_Fortran_COMPILER_ID} STREQUAL "Intel" OR ${CMAKE_Fortran_COMPILER_ID} STREQUAL "IntelLLVM")
        set(CHEMSH_ARCH "intel")
    elseif(${CMAKE_Fortran_COMPILER_ID} STREQUAL "GNU")
        set(CHEMSH_ARCH "gnu")
    elseif(${CMAKE_Fortran_COMPILER_ID} STREQUAL "Flang")
        set(CHEMSH_ARCH "llvm")
    elseif(${CMAKE_Fortran_COMPILER_ID} STREQUAL "Cray")
        set(CHEMSH_ARCH "cray")
    else()
        message(FATAL_ERROR " Fortran compiler not supported.")
    endif()
endif()

# does not work: check if Fortran and C compilers are the same
#if(NOT ${CMAKE_C_COMPILER_ID} STREQUAL ${CMAKE_Fortran_COMPILER_ID})
#    message(FATAL_ERROR " Fortran compiler (--fc) and C compiler (--cc) not compatible.")
#endif()


# Intel
# 3/3/2021 Settings revised to match Intel Link Line Advisor (thanks to HMS for revisions)
if(${CHEMSH_ARCH} STREQUAL "intel")
    # debug flags
    if(DEBUG)
        # YL 19/10/2021: there might be an Intel bug that compiling with -check pointer causes a runtime error in DL_PY2F (see getChar())
#        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -O0 -g -check all")
#        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -O0 -g -check arg_temp_created,assume,contiguous,format,output_conversion,stack,uninit")
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -O0 -g")
        set(CMAKE_C_FLAGS        "${CHEMSH_C_FLAGS} -O0 -g")
    else()
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -O${CHEMSH_COMPILE_OPT_LEVEL} -diag-disable=6178,8291,10434 -multiple-processes=${CHEMSH_MAKE_NJOBS}")
        if(${CMAKE_C_COMPILER_VERSION} VERSION_GREATER_EQUAL 2021)
            set(CHEMSH_C_FLAGS   "${CHEMSH_C_FLAGS} -O${CHEMSH_COMPILE_OPT_LEVEL} -diag-disable=10441")
            set(CHEMSH_CXX_FLAGS "${CHEMSH_CXX_FLAGS} -O${CHEMSH_COMPILE_OPT_LEVEL} -diag-disable=10441 -Wno-all")
        else()
            set(CHEMSH_C_FLAGS "${CHEMSH_C_FLAGS} -O${CHEMSH_COMPILE_OPT_LEVEL} -multiple-processes=${CHEMSH_MAKE_NJOBS}")
        endif()
    endif()
    # 64-bit integers
    if(CHEMSH_64_BIT)
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -i8")
        set(MKL_SUFFIX "ilp64")
    else()
        set(MKL_SUFFIX "lp64")
    endif()
    # MKL: use the following if it is not defined by utils/platforms/xxxx.py
    # YL FIXME 17/11/2021: in theory OpenMP should still be usable when NOT WITH_MPI
    if("${MKL_FLAGS}" STREQUAL "")
        # WITH_MPI has not yet been defined by now!
        if(MPI)
            # HMS -mkl=parallel (see below) switches on OpenMP. Therefore need -liomp5
            # HMS Also set blacs here 
            set(CHEMSH_MKL_FLAGS "-L\"${MKL_LIB}\" -Wl,--start-group -lmkl_core -lmkl_intel_${MKL_SUFFIX} -lmkl_intel_thread -lmkl_blacs_intelmpi_${MKL_SUFFIX} -liomp5 -Wl,--end-group -lpthread -lm -ldl")
        else()
            # YL 14/11/2022: use -liomp5 even no MPI
            set(CHEMSH_MKL_FLAGS "-L\"${MKL_LIB}\"  -Wl,--start-group -lmkl_core -lmkl_intel_${MKL_SUFFIX} -lmkl_intel_thread -liomp5 -Wl,--end-group -lpthread -lm -ldl")
        endif()
    else()
        set(CHEMSH_MKL_FLAGS ${MKL_FLAGS})
    endif()
    # default ScaLAPACK flags if not defined by the user 
    if("${ScaLAPACK_FLAGS}" STREQUAL "")
        if(MPI)
            # YL 17/11/2021: some versions of MKL (18) won't work without -lmkl_core -lmkl_intel_lp64 -lmkl_intel_thread
            #Rajany:Since MKL and Scalapack flags are the same, linker can accept only one when both are used:,-L is added at the last step.
            set(ScaLAPACK_FLAGS  "-Wl,--start-group  -lmkl_core -lmkl_intel_${MKL_SUFFIX} -lmkl_intel_thread -lmkl_blacs_intelmpi_${MKL_SUFFIX} -lmkl_scalapack_${MKL_SUFFIX} -liomp5 -Wl,--end-group -lpthread -lm -ldl")
        endif()
    endif()

# GNU
elseif(${CHEMSH_ARCH} STREQUAL "gnu")
    # YL 19/03/2021: since gcc/gfortran v10 type mismatch is not allowed by default
    if(${CMAKE_Fortran_COMPILER_VERSION} VERSION_GREATER_EQUAL 10.0)
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -fallow-argument-mismatch")
    endif()
    # debug flags
    if(DEBUG)
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -O0 -Wall -g -fcheck=all -fbacktrace -ffree-line-length-none")
        set(CHEMSH_C_FLAGS       "${CHEMSH_C_FLAGS} -O0 -g -ggdb -w")
    else()
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -O${CHEMSH_COMPILE_OPT_LEVEL} -ffree-line-length-none -fbacktrace -Wno-argument-mismatch")
        set(CHEMSH_C_FLAGS       "${CHEMSH_C_FLAGS} -O${CHEMSH_COMPILE_OPT_LEVEL}")
    endif()
    # 64-bit integers
    if(CHEMSH_64_BIT)
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} -fdefault-integer-8")
    endif()

    # if user has not set the blas library
    if (${BLAS_DIR} STREQUAL "default")
        find_package(BLAS REQUIRED)
    else()
        SET(BLAS_LIBRARIES ${BLAS_DIR} CACHE INTERNAL "")
    endif()

    # if the user has not defined a lapack library
    if (${LAPACK_DIR} STREQUAL "default")
        find_package(LAPACK REQUIRED)
    else()
        SET(LAPACK_LIBRARIES ${LAPACK_DIR} CACHE INTERNAL "")
    endif()

    SET(ENV{LAPACK_LIBRARIES} ${LAPACK_LIBRARIES})
    SET(ENV{BLAS_LIBRARIES} ${BLAS_LIBRARIES})

elseif(${CHEMSH_ARCH} STREQUAL "cpe-gnu")
    if(NOT "${MKL_FLAGS}" STREQUAL "")
        set(CHEMSH_MKL_FLAGS ${MKL_FLAGS})
    endif()
endif()

# TD 21/08/2024: RPATHS are set for ChemShell components here
#                This is needed so that CHEMSH_ROOT can be moved post build without anything breaking
#                $ORIGIN is a special path that wil get expanded at run time as the so/exe location
#                --disable-new-dtags Will set RPATH rather than RUNPATH, which is higher priority
#
# This is intended for .so files that end up in $CHEMSH_ROOT/build/$CHEMSH_ARCH/lib/
set(CHEMSH_LINK_FLAGS "-Wl,-rpath=$ORIGIN -Wl,--disable-new-dtags")
# This is intended for chemsh.x
set(CHEMSHX_LINK_FLAGS "-Wl,-rpath=$ORIGIN/../../build/${CHEMSH_ARCH}/lib/ -Wl,--disable-new-dtags")

# check ScaLAPACK flags (only tested for Intel)
if(NOT("${ScaLAPACK_DIR}" STREQUAL "") AND NOT("${ScaLAPACK_FLAGS}" STREQUAL ""))
    set(CHEMSH_ScaLAPACK_FLAGS "-L${ScaLAPACK_DIR} ${ScaLAPACK_FLAGS}")
elseif("${ScaLAPACK_DIR}" STREQUAL "" AND NOT("${ScaLAPACK_FLAGS}" STREQUAL ""))
    if(NOT("${MKL_LIB}" STREQUAL ""))
        set(CHEMSH_ScaLAPACK_FLAGS "-L${MKL_LIB} ${ScaLAPACK_FLAGS}")
    else()
        set(CHEMSH_ScaLAPACK_FLAGS "${ScaLAPACK_FLAGS}")
    endif()
endif()
# YL 06/03/2021: added a ScaLAPACK switch
#string(TOLOWER "${ScaLAPACK_DIR}" ScaLAPACK_TMP)
#if("${ScaLAPACK_TMP}" STREQUAL "no" OR "${ScaLAPACK_TMP}" STREQUAL "off" OR "${ScaLAPACK_TMP}" STREQUAL "false" OR "${ScaLAPACK_TMP}" STREQUAL "")
#RKV: if scalapack_flags are not defined at this point(from user or platform), scalapack cannot be linked evenif scalapakdir is non empty.issue a warning?
  if("${ScaLAPACK_FLAGS}" STREQUAL "")
    set(WITH_ScaLAPACK OFF)
  else()
    set(WITH_ScaLAPACK ON)
  endif()
  message(STATUS "CHEMSH_SCALAPACK_FLAGS ${CHEMSH_ScaLAPACK_FLAGS}")

# FFTW Flags
# YL 23/02/2021: added a FFTW switch so that the user can switch off FFTW when it's not available
string(TOLOWER "${FFTW_DIR}" FFTW_TMP)

if(${CHEMSH_ARCH} STREQUAL "gnu" OR ${CHEMSH_ARCH} STREQUAL "cpe-gnu")
    if("${FFTW_TMP}" STREQUAL "no" OR "${FFTW_TMP}" STREQUAL "off" OR "${FFTW_TMP}" STREQUAL "false" OR "${FFTW_TMP}" STREQUAL "")
        set(WITH_FFTW OFF)
    else()
        set(WITH_FFTW ON)
        get_filename_component(FFTW_DIR "${FFTW_DIR}" ABSOLUTE)
        message(STATUS "FFTW_LIB_DIR set as ${FFTW_DIR}")
        message(STATUS "FFTW_LINK_FLAGS set as ${FFTW_LINK_FLAGS}")
        if(NOT("${FFTW_INCLUDE_DIR}" STREQUAL ""))
          set(FFTW_COMPILE_FLAGS "-I${FFTW_INCLUDE_DIR}")
        endif()
        message(STATUS "FFTW_COMPILE_FLAGS set as ${FFTW_COMPILE_FLAGS}")
    endif()
#
elseif(${CHEMSH_ARCH} STREQUAL "intel")
    if("${FFTW_TMP}" STREQUAL "no" OR "${FFTW_TMP}" STREQUAL "off" OR "${FFTW_TMP}" STREQUAL "false" OR "${FFTW_TMP}" STREQUAL "")
        set(FFTW_LIB_DIR "")
        set(WITH_FFTW OFF)
    else()
        if("${FFTW_TMP}" STREQUAL "intel" OR ("${FFTW_TMP}" STREQUAL "mkl"))
        set(WITH_FFTW ON)
        set(WITH_FFTW_MKL ON)
        message(STATUS "FFTW_LIB_DIR set as ${FFTW_LIB_DIR}")

        #Rajany:set MKL_ROOT;
        #${MKL} and MKL_LIB are  MKLroot/lib/intel64 
        # use env MKLROOT first
        set(MKL_ROOT1 $ENV{MKLROOT})
        get_filename_component(MKL_ROOT1 ${MKL_ROOT1} ABSOLUTE) 
        get_filename_component(MKL_ROOT2 ${MKL_LIB}./../ ABSOLUTE) # last name is taken as name, not dir, since it has no /. so only one ../
        if(NOT $ENV{MKLROOT} STREQUAL "")
          message(STATUS " MKL_ROOT found from ENV: ${MKL_ROOT1}")
          set(FFTW_INCLUDE_DIR "${MKL_ROOT1}/include/fftw")
          get_filename_component(FFTW_INCLUDE_DIR "${FFTW_INCLUDE_DIR}" ABSOLUTE)
          message(STATUS "FFTW_INCLUDE_DIR set as ${FFTW_INCLUDE_DIR}")
          #Raj:hardcoding for now
          set(MKL_INCLUDE_FLAGS "-I${MKL_ROOT1}/include -I${MKL_ROOT1}/include/intel64/${MKL_SUFFIX}")
          set(MKL_COMPILE_FLAGS "${MKL_COMPILE_FLAGS} ${MKL_INCLUDE_FLAGS}")

        else()
            if(NOT ${MKL_ROOT2} STREQUAL "")
                message(STATUS " MKL_ROOT is set from MKL_LIB path as: ${MKL_ROOT2}")
                set(FFTW_INCLUDE_DIR "${MKL_ROOT2}/include/fftw")
                get_filename_component(FFTW_INCLUDE_DIR "${FFTW_INCLUDE_DIR}" ABSOLUTE)
                message(STATUS "FFTW_INCLUDE_DIR set as ${FFTW_INCLUDE_DIR}")
                set(MKL_INCLUDE_FLAGS "-I${MKL_ROOT2}/include -I${MKL_ROOT2}/include/intel64/${MKL_SUFFIX}")
                get_filename_component(MKL_INCLUDE_FLAGS "${MKL_INCLUDE_FLAGS}" ABSOLUTE)
                set(MKL_COMPILE_FLAGS "${MKL_COMPILE_FLAGS} ${MKL_INCLUDE_FLAGS}")
            else()
               message(STATUS " Cannot set MKL_ROOT! This might cause problems!:")
            endif()
        endif()
    # Set the fftw_interface directory
        #if(${CMAKE_Fortran_COMPILER_VERSION} VERSION_GREATER_EQUAL 2021.0.0)
        set(INTERFACE_DIR "${MKL_ROOT1}/share/mkl/interfaces/fftw3xf")
        get_filename_component(INTERFACE_DIR "${INTERFACE_DIR}" ABSOLUTE)
        if(EXISTS ${INTERFACE_DIR})
            set(FFTW_INTERFACE_DIR ${INTERFACE_DIR})
            message(STATUS "FFTW_INTERFACE_DIR set as ${FFTW_INTERFACE_DIR}")
        else()
            set(INTERFACE_DIR2 "${MKL_ROOT1}/interfaces/fftw3xf")
            get_filename_component(INTERFACE_DIR2 "${INTERFACE_DIR2}" ABSOLUTE)
            if(EXISTS ${INTERFACE_DIR2})
                  set(FFTW_INTERFACE_DIR ${INTERFACE_DIR2})
                  message(STATUS "FFTW_INTERFACE_DIR set as ${FFTW_INTERFACE_DIR}")

            else()
                set(INTERFACE_DIR3 "${MKL_ROOT2}/share/mkl/interfaces/fftw3xf")
                get_filename_component(INTERFACE_DIR3 "${INTERFACE_DIR3}" ABSOLUTE)
                if(EXISTS ${INTERFACE_DIR3})
                    set(FFTW_INTERFACE_DIR ${INTERFACE_DIR3})
                else()
                    set(INTERFACE_DIR4 "${MKL_ROOT2}/interfaces/fftw3xf")
                    get_filename_component(INTERFACE_DIR4 "${INTERFACE_DIR4}" ABSOLUTE)
                    if(EXISTS ${INTERFACE_DIR4})
                        set(FFTW_INTERFACE_DIR ${INTERFACE_DIR4})
                        message(STATUS "FFTW_INTERFACE_DIR set as ${FFTW_INTERFACE_DIR}")
                    else()
                        message(ERROR "\n Could not find a valid fft-interface directory. ${INTERFACE_DIR4} does not exist")
                    endif()
                endif()
            endif()
        endif()

        else()
          set(WITH_FFTW ON)
          set(WITH_FFTW_MKL OFF)
          set(FFTW_INTERFACE_DIR "")
          get_filename_component(FFTW_DIR "${FFTW_DIR}" ABSOLUTE)
          message(STATUS "FFTW_LIB_DIR set as ${FFTW_DIR}")
          message(STATUS "FFTW_LINK_FLAGS set as ${FFTW_LINK_FLAGS}")
          if(NOT("${FFTW_INCLUDE_DIR}" STREQUAL ""))
            set(FFTW_COMPILE_FLAGS "-I${FFTW_INCLUDE_DIR}")
          endif()
          message(STATUS "FFTW_COMPILE_FLAGS set as ${FFTW_COMPILE_FLAGS}")
        endif()
  endif()
endif()

string(REPLACE "." ";" Fortran_COMPILER_VERSION_LIST ${CMAKE_Fortran_COMPILER_VERSION})
list(LENGTH Fortran_COMPILER_VERSION_LIST LENGTH_Fortran_COMPILER_VERSION_LIST)
list(GET Fortran_COMPILER_VERSION_LIST 0 Fortran_COMPILER_VERSION_MAJOR)
list(GET Fortran_COMPILER_VERSION_LIST 1 Fortran_COMPILER_VERSION_MINOR)
if(${LENGTH_Fortran_COMPILER_VERSION_LIST} GREATER_EQUAL 3)
    list(GET Fortran_COMPILER_VERSION_LIST 2 Fortran_COMPILER_VERSION_PATCH)
endif()
if(${LENGTH_Fortran_COMPILER_VERSION_LIST} GREATER_EQUAL 4)
    list(GET Fortran_COMPILER_VERSION_LIST 3 Fortran_COMPILER_VERSION_TWEAK)
    set(Fortran_COMPILER_VERSION_TWEAK ${Fortran_COMPILER_VERSION_TWEAK} CACHE STRING "internal Intel version number" FORCE)
# YL 03/07/2022: it can't be ""!
else()
    set(Fortran_COMPILER_VERSION_TWEAK "unknown" CACHE STRING "internal Intel version number" FORCE)
endif()
# User should choose whether build is serial or parallel by specifying mpi flag to setup script
if(NOT MPI)
    set(WITH_MPI OFF)
    if(${CHEMSH_ARCH} STREQUAL "intel")
        # YL 12/04/2022: reported by Hans, CMake has a serious bug that it identifies the Intel compiler version 1X.Y.Z right but 202X.Y.Z completely wrong as 20.2.X
        if(${Fortran_COMPILER_VERSION_TWEAK} VERSION_GREATER_EQUAL 20210000 OR ${CMAKE_Fortran_COMPILER_VERSION} VERSION_GREATER_EQUAL 2022)
            set(INTEL_OMP_FLAGS "-qmkl=sequential")
        else()
            set(INTEL_OMP_FLAGS "-mkl=sequential")
        endif()
        # Compiler options for serial MKL (using implicit linking)
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} ${INTEL_OMP_FLAGS}")
    endif()
    # cannot use ScaLAPACK
    set(CHEMSH_ScaLAPACK_FLAGS "")
    set(WITH_ScaLAPACK OFF)
# parallel (MPI compiler wrapper)
else()  
    # check if compiler is an MPI wrapper: if it compiles mpi.f90, return 0; if it fails, return 1
    execute_process(COMMAND ${CMAKE_Fortran_COMPILER} mpi.f90
                    WORKING_DIRECTORY ${CHEMSH_DIR}/chemsh/utils/cmake
                    RESULT_VARIABLE TEST_RESULT
                    OUTPUT_QUIET
                    ERROR_QUIET
                   )
    # compiler does not compile in parallel!
    if(TEST_RESULT)
        # YL 02/08/2023: to meet Intel's recent transition from ifort to ifx, just in case
        if(${CHEMSH_ARCH} STREQUAL "intel")
            execute_process(COMMAND mpiifort -fc=ifx mpi.f90
                            WORKING_DIRECTORY ${CHEMSH_DIR}/chemsh/utils/cmake
                            RESULT_VARIABLE TEST_RESULT
                            OUTPUT_QUIET
                            ERROR_QUIET
                           )
            if(TEST_RESULT)
                message(FATAL_ERROR "Compiler does not support parallel compilation!")
            endif()
        else()
            message(FATAL_ERROR "Compiler does not support parallel compilation!")
        endif()
    endif()
#    option(WITH_MPI "Enable MPI parallelisation" ON)
    set(WITH_MPI ON)

    # Intel MPI
    if(${CHEMSH_ARCH} STREQUAL "intel")
        # since CMake 3.12 MPI_ROOT will be used for searching
        # see details by: cmake --help-policy CMP0074
# YL 10/03/2020: commented out for now to allow CMake 3.10
#        cmake_policy(SET CMP0074 NEW)
        find_package(MPI REQUIRED)
        # YL 16/06/2023: find_mpi() doesn't always set the MPI variables
        execute_process(COMMAND ${MPI_C_COMPILER} -show
                        OUTPUT_VARIABLE MPI_COMPILER_SHOW
                        ERROR_QUIET)
        separate_arguments(MPI_COMPILER_SHOW)
        foreach(FOO ${MPI_COMPILER_SHOW})
            if(FOO MATCHES "^-I")
                if("${MPI_C_INCLUDE_DIRS}" STREQUAL "" AND ${MPI_C_FOUND})
                string(REPLACE "-I" "" FOOBAR ${FOO})
            string(REPLACE "\"" "" MPI_C_INCLUDE_DIRS ${FOOBAR})
            set(MPI_C_INCLUDE_DIRS "${MPI_C_INCLUDE_DIRS}" CACHE PATH "Directory of MPI C headers")
                endif()
                if("${MPI_Fortran_INCLUDE_DIRS}" STREQUAL "" AND ${MPI_Fortran_FOUND})
                string(REPLACE "-I" "" FOOBAR ${FOO})
            string(REPLACE "\"" "" MPI_Fortran_INCLUDE_DIRS ${FOOBAR})
            set(MPI_Fortran_INCLUDE_DIRS "${MPI_Fortran_INCLUDE_DIRS}" CACHE PATH "Directory of MPI Fortran headers")
                endif()
            endif()
            if(FOO MATCHES "^-L")
        string(REPLACE "-L" "" FOOBAR ${FOO})
        string(REPLACE "\"" "" FOOBAR ${FOOBAR})
        list(APPEND MPI_LIB_DIRS ${FOOBAR})
            endif()
        endforeach(FOO)
        # YL 23/06/2023: set MPI_INCLUDE_PATH and MPI_LIB_PATH only if they are not defined by the user
        if("${MPI_INCLUDE_PATH}" STREQUAL "")
            set(MPI_INCLUDE_PATH ${MPI_Fortran_INCLUDE_DIRS} CACHE PATH "Directory of MPI headers")
        endif()
        if("${MPI_LIB_PATH}" STREQUAL "")
            set(MPI_LIB_PATH "${MPI_LIB_DIRS}" CACHE PATH "Directory of MPI shared libraries")
        endif()
        if(${Fortran_COMPILER_VERSION_TWEAK} VERSION_GREATER_EQUAL 20210000 OR ${CMAKE_Fortran_COMPILER_VERSION} VERSION_GREATER_EQUAL 2022)
            set(INTEL_OMP_FLAGS "-qmkl=parallel")
        else()
            set(INTEL_OMP_FLAGS "-mkl=parallel")
        endif()
        # Compiler options for parallel MKL (using implicit linking)
        set(CHEMSH_Fortran_FLAGS "${CHEMSH_Fortran_FLAGS} ${INTEL_OMP_FLAGS}")
    
    # OpenMPI
    elseif(${CHEMSH_ARCH} STREQUAL "gnu")
        set(MPI_COMPILER          ${CMAKE_Fortran_COMPILER})
        set(MPI_Fortran_LIBRARIES ${MPI_LIBRARIES})
        set(MPI_Fortran_INCLUDE   ${MPI_INCLUDE_PATH})
        set(MPI_C_LIBRARIES       ${MPI_LIB_PATH}/libmpi.so)
        set(MPI_C_INCLUDE         ${MPI_INCLUDE_PATH})
        set(MPI_INCLUDE_PATH      ${MPI_INCLUDE_PATH})
        find_package(MPI REQUIRED)
        # YL 13/09/2025: override iff they are still blank
        if("${MPI_Fortran_COMPILER}" STREQUAL "")
            set(MPI_Fortran_COMPILER  ${CMAKE_Fortran_COMPILER})
        endif()
        if("${MPI_C_COMPILER}" STREQUAL "")
            set(MPI_C_COMPILER        ${CMAKE_C_COMPILER})
        endif()
        if("${MPI_CXX_COMPILER}" STREQUAL "")
            set(MPI_CXX_COMPILER      ${CMAKE_CXX_COMPILER})
        endif()
        # TWK TODO Scalapck with GNU?
    elseif(${CHEMSH_ARCH} STREQUAL "cpe-gnu")
        find_package(MPI REQUIRED)
    endif()

    # YL 04/03/2021: we now always use the absolute path to inscribe as args.mpiexec (see chemsh.py.in)
    #                then in some special cicumstance where different MPI can be embedded, for example:
    #                a Intel MPI compiled ChemShell allows parallel ORCA which is emplicited OpenMPI-driven
    #                we also removed the check for CMake v3.10 which is our minimum now
    set(MPIEXEC ${MPIEXEC_EXECUTABLE})

endif()

# RKV 09/08/24: CUDA linker flags should be set at parent scope, otherwise final linking fails.
if(GPU OR GPU_COMPILER OR GPU_ARCH)
    string(TOLOWER "${GPU}" GPU_TMP)
    if("${GPU_TMP}" STREQUAL "cuda")
       set(CMAKE_CUDA_COMPILER  "nvcc")
       #enable_language(CUDA OPTIONAL)
    endif()
    if(NOT("${GPU_COMPILER}" STREQUAL ""))
      get_filename_component(GPU_PATH ${GPU_COMPILER} DIRECTORY)
      get_filename_component(GPU_NAME ${GPU_COMPILER} NAME)
      if(${GPU_NAME} MATCHES "nvcc")
        set(CMAKE_CUDA_COMPILER  "nvcc")
        #enable_language(CUDA OPTIONAL)
      endif()
    endif()
    #RKV gpu arch can be hip etc. as of now, not supported. here assumes gpu-arch is cuda-arch
    # enable cuda if the gpu architecture is given, even if nvcc is not specified.
    if( NOT("${GPU_ARCH}" STREQUAL "") )
       set(CMAKE_CUDA_COMPILER  "nvcc")
       #enable_language(CUDA OPTIONAL)
    endif()

    if( NOT(${GPU_ARCH} STREQUAL "") AND (NOT(${CMAKE_CUDA_COMPILER} STREQUAL "")) )
        set(CUDA_ARCH ${GPU_ARCH})
    endif()
      
  if(NOT (${CMAKE_CUDA_COMPILER} STREQUAL "") )

      #if nvfortran to be used nvhpc is required;ignore cudatoolkitpath
      if(DEFINED ENV{CUDA_HOME} AND NOT("$CHEMSH_ARCH" STREQUAL "nvfort"))
        set(CUDA_PATH $ENV{CUDA_HOME})
        message(STATUS "Cuda root path=env CUDA_PATH: ${CUDA_PATH}")
        set(NVCC_PATH "${CUDA_PATH}/bin")
        set(CUDA_ENV TRUE)
      elseif(DEFINED ENV{CUDA_ROOT} AND NOT("$CHEMSH_ARCH" STREQUAL "nvfort"))
        set(CUDA_PATH $ENV{CUDA_ROOT})
        message(STATUS "Cuda root path=env CUDA_ROOT: ${CUDA_PATH}")
        set(NVCC_PATH "${CUDA_PATH}/bin")
        set(CUDA_ENV TRUE)
      endif()
       
      #if nvhpc is defined give it priority
      if(DEFINED ENV{NVHPC_ROOT})
        set(NVHPC TRUE)
        set(NVHPC_ROOT $ENV{NVHPC_ROOT})
        message(STATUS "NVHPC_ROOT: ${NVHPC_ROOT}")
      elseif(DEFINED ENV{NVHPC_HOME})
        set(NVHPC TRUE)
        set(NVHPC_ROOT $ENV{NVHPC_HOME})
        message(STATUS "NVHPC_HOME: ${NVHPC_ROOT}")
      else()
        execute_process(COMMAND which ${CMAKE_CUDA_COMPILER} OUTPUT_VARIABLE CUDA_FULLPATH)
        if("${CUDA_FULLPATH}" STREQUAL "")
            message(FATAL_ERROR "CUDA NOT FOUND...")
        else()
          get_filename_component(NVCC_PATH ${CUDA_FULLPATH} DIRECTORY)
          get_filename_component(CUDA_path "${NVCC_PATH}../" DIRECTORY)
          get_filename_component(CUDA_NAME "${CUDA_path}" NAME)
          if(${CUDA_NAME} STRGREATER_EQUAL "cuda")
             set(CUDA_PATH ${CUDA_path})
             message(STATUS "Cuda root path: ${CUDA_PATH}")
             set(CUDA_ENV TRUE)
          elseif(${CUDA_NAME} STRGREATER_EQUAL "compiler")
              set(NVHPC TRUE)
              get_filename_component(NVHPC_ROOT "${CUDA_path}/.." ABSOLUTE)
              message(STATUS "NVHPC ROOT ${NVHPC_ROOT}")
          else()
            if(CUDA_ENV)
              message(STATUS "SET BY CUDAENV: CUDAPATH ${CUDA_PATH}")
            else()
              set(CUDA_PATH  ${CUDA_path})
              message(STATUS "CUDAPATH set as ${CUDA_PATH}")
            endif()
          endif()
        endif()
      endif()

        
      if(NVHPC)
        file(GLOB cudaver LIST_DIRECTORIES TRUE "${NVHPC_ROOT}/cuda/*.*")
        #More than one versions of cuda may be found in nvhpc
        set(CUDA_PATH ${cudaver})
        list(LENGTH cudaver versions)
        if(${versions} GREATER 1)
          #allow user argument cuda_version        
          if( NOT ( "" STREQUAL "${CUDA_VERSION}"))
            set(cudaversion "${NVHPC_ROOT}/cuda/${CUDA_VERSION}")
          else()
            message(STATUS "More than one cuda versions found .. Using the latest version..")
            list(SORT cudaver COMPARE NATURAL ORDER DESCENDING)
            list(POP_FRONT cudaver cudaversion)
          endif()
          set(CUDA_PATH ${cudaversion})
        endif()

        message(STATUS "Cuda root path from env NVHPC: ${CUDA_PATH}")
        set(NVCC_PATH "${CUDA_PATH}/bin")

        get_filename_component(CUDA_VERSION_NUM "${CUDA_PATH}" NAME)
        message(STATUS "CUDA VERSION: ${CUDA_VERSION_NUM}")

      endif()

      #if cudapath has not been set yet  
      if("${CUDA_PATH}" STREQUAL "")
          message(FATAL_ERROR "COULD NOT FIND CUDA PATH...")
      endif()
      #set full path of nvcc
      get_filename_component(NVFULLPATH nvcc DIRECTORY)
      if("${NVFULLPATH}" STREQUAL "")
        set(CMAKE_CUDA_COMPILER "${NVCC_PATH}/nvcc")
      else()
        get_filename_component(CMAKE_CUDA_COMPILER nvcc ABSOLUTE)
      endif()
      message(STATUS "CUDA COMPILER ${CMAKE_CUDA_COMPILER}")

      execute_process(COMMAND dir ${CUDA_PATH}/targets OUTPUT_VARIABLE CUDA_ARCHPATH)
      string(STRIP ${CUDA_ARCHPATH} CUDA_ARCHPATH)
      message(STATUS "CUDA Arch: ${CUDA_ARCHPATH}")

      # Set final linker flags. Tested only for gnu
      #if(${CHEMSH_ARCH} STREQUAL "gnu")
      set(linkpaths " -L${CUDA_PATH}/lib -L${CUDA_PATH}/lib64 -L${CUDA_PATH}/compat/lib -L${CUDA_PATH}/targets/${CUDA_ARCHPATH}/lib")
      set(includepaths " -I${CUDA_PATH}/include -I${CUDA_PATH}/targets/${CUDA_ARCHPATH}/include")
      if(NVHPC)
          set(linkpaths "${linkpaths} -L${NVHPC_ROOT}/math_libs/lib64 -L${NVHPC_ROOT}/math_libs/lib")
          set(includepaths "${includepaths} -I${NVHPC_ROOT}/math_libs/include")
      endif()

        #ld_library_path will be necessary when using hpc kit.somehow chemshell doesn't look in it if not explicitly asked
        #set(LD_LIBRARY_PATH $ENV{LD_LIBRARY_PATH})
        #string(REPLACE ":" " -L" LD_LIBRARY_PATH $ENV{LD_LIBRARY_PATH})
        #set(linkpaths "${linkpaths} -L${LD_LIBRARY_PATH}")

      set(CUDA_LINK_FLAGS "-Wl,--enable-new-dtags ${linkpaths} -lstdc++ -lcudart -lnvrtc -lcuda -lcudadevrt")
      set(CUDA_MATH_LINK_FLAGS " ${linkpaths} -lcufft -lcublasLt -lcublas")
      set(CUDA_INCLUDE_FLAGS "${includepaths}")
      #Raj:TODO use mathflags only if needed by the external package. 
      set(CHEMSH_LD_FLAGS "${CHEMSH_LD_FLAGS} ${CUDA_LINK_FLAGS} ${CUDA_MATH_LINK_FLAGS}")
      set(CHEMSH_C_FLAGS "${CHEMSH_C_FLAGS} ${CUDA_INCLUDE_FLAGS}")
  endif()
endif()

message("")
message(" ======================================================================")
message(" Building ${BoldWhite}ChemShell${EndColour}")
message(" ----------------------------------------------------------------------")
message(" Compiler architecture ${BoldWhite}${CMAKE_Fortran_COMPILER_ID}${EndColour}")
message(" Compiler version ${BoldWhite}${CMAKE_Fortran_COMPILER_VERSION}${EndColour}")
if(WITH_MPI)
    message(" With ${BoldWhite}MPI${EndColour} support")
endif()
message(" CMake version ${BoldWhite}${CMAKE_VERSION}${EndColour}")
message(" ======================================================================")
message("")

# YL 14/09/2025: moved from TD's warning mechanism in setup
if(mpi IN_LIST CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES)
    if(NOT WITH_MPI)
        message("${BoldYellow} WARNING: An MPI compilier has been given but an MPI build is not requested (--mpi)${EndColour}\n")
    endif()
endif()

# YL 19/06/2024: moved from tools/CMakeLists.txt, reported by TD about wrong location of installing Python modules
message("\nSetting environment variable PYTHONUSERBASE to ${CHEMSH_DIR}/build")
set(ENV{PYTHONUSERBASE} ${CHEMSH_DIR}/build)

set(CMAKE_C_FLAGS             "${CHEMSH_C_FLAGS}")
set(CMAKE_Fortran_FLAGS       "${CHEMSH_Fortran_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CHEMSH_LD_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS    "${CHEMSH_LD_FLAGS}")

# paths
set(CHEMSH_BUILD_DIR               ${CHEMSH_DIR}/build/${CHEMSH_ARCH})
set(DL_PY2F_BUILD_DIR              ${CHEMSH_BUILD_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CHEMSH_BUILD_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
set(CMAKE_Fortran_MODULE_DIRECTORY ${CHEMSH_BUILD_DIR}/modules)
set(CMAKE_INCLUDE_PATH             ${CHEMSH_BUILD_DIR}/include)

# TODO: make this automatic
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libdl_py2f.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libparallel.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libga.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libdl_poly.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/liblsdalton.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libgulp.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libgamess-uk.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libdlf.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libfhiaims.so)
file(REMOVE ${CHEMSH_BUILD_DIR}/lib/libnwchem.so)

if(EXISTS ${CHEMSH_DIR}/chemsh/utils/platforms/${PLATFORM}.sh.in)
    configure_file(${CHEMSH_DIR}/chemsh/utils/platforms/${PLATFORM}.sh.in
                   ${CHEMSH_DIR}/chemsh/utils/platforms/${PLATFORM}.sh @ONLY)
endif()

# check whether we are on MACOSX using Macports for library location
set(CMAKE_USEMACPORTS $ENV{USE_MACPORTS})

# Python requirements
# need to set additional versions for old cmake on Ubuntu 12.04 (2.8.7)
if(${CMAKE_USEMACPORTS} MATCHES "/*true/*")
    set(PYTHON_LIBRARY /opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/libpython3.4m.dylib)
    set(PYTHON_INCLUDE_DIR /opt/local/Library/Frameworks/Python.framework/Versions/3.4/include/python3.4m)
endif()

# TD 20/10/25: Restraint functionality requires at least 3.10 (for TypeAlias)
if(NOT DEFINED ${PYTHON_LIBRARY})
    find_package(Python3 3.10 REQUIRED COMPONENTS Interpreter Development)
endif()

# YL 19/03/2021: location to install missing Python packages
#                for example, build/lib/python3.8/site-packages
# TODO: valid with ${PYTHON_LIBRARY}?
# TD 21/08/2024: Made CHEMSH_SITE_PACKAGES a relative path rather than absolute (To support post build moves of CHEMSH_DIR)
#                Should be a relative path starting at ${CHEMSH_DIR}
# YL 20/02/2025: Python or Python3 must be consistent for CMake variables
set(CHEMSH_SITE_PACKAGES build/lib/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages)

set(Python3_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} $ENV{Python3_INCLUDE_DIRS})
include_directories(${Python3_INCLUDE_DIRS}
                    ${CHEMSH_BUILD_DIR}/modules/dl_py2f
                   )

# ChemShell Fortran code
# YL: objects/objects.f90 may be obsolete later
#set(BASE_SOURCES base/datatypes.f90)

# compile
#add_library(chemsh SHARED ${BASE_SOURCES})
#add_library(parallel SHARED ${PARALLEL_SOURCES})

# YL 27/03/2024: DL_PY2F used to use ${CHEMSH_ARCH} but it should be separated
#                by using its own variable
set(DL_PY2F_ARCH ${CHEMSH_ARCH} CACHE STRING "DL_P2F architecture, same as Py-ChemShell's" FORCE)

# subdirectories: register your subproject directories here
list(APPEND CHEMSH_SUBDIRECTORIES app
                                  addons
                                  cluster
                                  data
                                  dl_find
                                  dl_py2f
                                  interfaces
                                  parallel
                                  tools)

foreach(CHEMSH_SUBDIRECTORY ${CHEMSH_SUBDIRECTORIES})
    add_subdirectory(${CHEMSH_SUBDIRECTORY})
    # clean policy
    set(CURRENT_DIR ${CHEMSH_SUBDIRECTORY})
    include(${CHEMSH_DIR}/chemsh/utils/cmake/clean.cmake)
endforeach(CHEMSH_SUBDIRECTORY)

# clean policy for the root dir
set_directory_properties(PROPERTIES
    ADDITIONAL_CLEAN_FILES "${CMAKE_CURRENT_LIST_DIR}/Makefile;${CMAKE_CURRENT_LIST_DIR}/cmake_install.cmake;${CMAKE_CURRENT_LIST_DIR}/__pycache__;"
)
