#  Copyright (C) 2019 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/>.

# you.lu@stfc.ac.uk
# Jul 2022

set(BSE_DEFAULT_VERSION 0.9)

include(ExternalProject)

# function to get the BSE path
function(get_bse_dir BSE)

    # try to convert to absolte full path: when it is a URL, it appears as "${CMAKE_CURRENT_SOURCE_DIR}${BSE}" and will not be taken as "EXISTS"
    get_filename_component(BSE_ABSOLUTE "${BSE}" ABSOLUTE)

    # BSE source location
    # if file or directory
    if(EXISTS ${BSE_ABSOLUTE})
        # do not use git
        set(BSE_GIT_REPO "")
        set(BSE_GIT      OFF)
        # set a git revision number (notice the missing '$'!)
        if(DEFINED ENV{BSE_GIT_REVISION})
          set(BSE_GIT_REVISION $ENV{BSE_GIT_REVISION})
        else()
          set(BSE_GIT_REVISION "unknown")
        endif()
        # if directory
        if(IS_DIRECTORY ${BSE_ABSOLUTE})
            # TODO: should be obsolete
            # stop if not absolute path
            if(NOT IS_ABSOLUTE ${BSE_ABSOLUTE})
                message(FATAL_ERROR "${BoldRed}BSE_REPO not given in full absolute path. Exiting...${EndColour}")
            endif()
            set(BSE_DIR     ${BSE_ABSOLUTE})
            set(BSE_SRC_DIR ${BSE_ABSOLUTE})
            set(BSE_SRC_DIR ${BSE_ABSOLUTE} PARENT_SCOPE)
            set(BSE_TARBALL "")
            set(BSE_URL     ${BSE})
        # if file (tar ball)
        else()
            set(BSE_TARBALL ${BSE})
            set(BSE_URL     ${BSE})
            # YL 05/11/2018: a charm++ name must be analysed for getting a proper WORKING_DIRECTORY during the extraction of the BSE tarball which should be in theory at the "configure" stage but has to be executed within the "build"-stage ExternalProject_Add(). However at the "build" stage, CMake has NO way to get a returned value from an external script (or even NO way to define an evnvar in the script). Therefore we apply this execute_process() at the "configure" stage.
            # YL 05/11/2018: we have to use `cmake --build dir` because `cmake -P script.cmake` is not allowed to do anything target-related and functions such as ExternalProject_Add() is not scriptable!
            set(BSE_DIR           ${CHEMSH_DIR}/chemsh/interfaces/bse)
            set(BSE_SRC_DIR       ${BSE_DIR})
            set(BSE_SRC_DIR       ${BSE_DIR} PARENT_SCOPE)
            set(BSE_BINARY_DIR    ${BSE_DIR}/${CHEMSH_ARCH}/bse)
            file(MAKE_DIRECTORY ${BSE_BINARY_DIR})
        endif()
    # else: use git repository
    else()
        message("${BoldWhite}BSE${EndColour} repository URL: ${BSE}")
        set(BSE_GIT      ON                            PARENT_SCOPE)
        set(BSE_GIT_REPO ${BSE}                    PARENT_SCOPE)
        set(BSE_GIT_TAG  "v${BSE_DEFAULT_VERSION}" PARENT_SCOPE)
        set(BSE_TARBALL  ""                            PARENT_SCOPE)
        set(BSE_DIR      ${CMAKE_CURRENT_LIST_DIR}/git PARENT_SCOPE)
        set(BSE_SRC_DIR  ${BSE_DIR}                PARENT_SCOPE)
        set(BSE_URL      ""                            PARENT_SCOPE)
        set(BSE_BUILD_COMMAND ${PYTHON_EXECUTABLE} setup.py build > build.log PARENT_SCOPE) # do NOT add quotation marks here!
    endif()

endfunction()

if(NOT DEBUG)
    set(PIP_FLAGS -q)
endif()

if(NOT "${BSE}" STREQUAL "")
    # FetchContent isn't available until CMake 3.11
# YL 02/10/2020: Ubuntu 18.04 is still with 3.10
#    cmake_minimum_required(VERSION 3.11)
#    include(FetchContent)
    get_bse_dir("${BSE}")
    # cache-level variable cannot be set within a function
    set(BSE_SRC_DIR "${BSE_SRC_DIR}" CACHE FILEPATH "" FORCE)

    # get the current pip version
    if(${PIP})
        execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip --version
                        OUTPUT_VARIABLE PIP_VERSION
                        ERROR_QUIET)
    endif()
    string(REGEX MATCH "[0-9]+[.]+[0-9]+([.]?[0-9])?" PIP_VERSION "${PIP_VERSION}")
    # --no-warn-script-location not available until pip v20.0
    if("${PIP_VERSION}" VERSION_GREATER_EQUAL 20.0)
        set(PIP_EXTRA_FLAGS --no-warn-script-location)
    endif()
    if(${PIP})
        message("pip version before upgrading: ${PIP_VERSION}")
    endif()

    # check if --system is available
    if(${PIP})
        execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip install --system
                        OUTPUT_VARIABLE PIP_SYSTEM_OUTPUT
                        ERROR_VARIABLE PIP_SYSTEM_ERROR)
        string(FIND ${PIP_SYSTEM_ERROR} "no such option: --system" FOUND_ERROR)
        # --system available
        if("${FOUND_ERROR}" EQUAL -1)
            set(PIP_SYSTEM_FLAGS --system)
        endif()
    endif()

    # YL 10/11/2021: first force an update of pip and numpy (dependency of pandas) so that pip won't try to
    #                uninstall the existing system numpy (which is usually out of date) when installing pandas
    # YL 18/11/2021: we can't install modules with --user because on some weird systems such as A* the compute nodes can't access $HOME/.local
    message("Setting environment variable PYTHONUSERBASE to ${CHEMSH_DIR}/build")
    set(ENV{PYTHONUSERBASE} ${CHEMSH_DIR}/build)
    if(${PIP})
        execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install --user --upgrade ${PIP_EXTRA_FLAGS} pip)
    # YL 11/11/2021: let's manually install the pandas dependencies so that we can use --no-deps below
    #                requests is a BSE dependency
# YL 11/04/2022: no longer required
        execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install --user --upgrade ${PIP_EXTRA_FLAGS} jsonschema argcomplete regex)
    endif()
# YL 11/04/2022: since v3.2.0 pandas became no longer a dependency, reported by HMS
#    # pandas
#    execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pandas; print(pandas.__version__)"
#                    OUTPUT_VARIABLE PANDAS_VERSION
#                    ERROR_QUIET)
#    if("${PANDAS_VERSION}" STREQUAL "")
#        message("Installing missing BSE dependency pandas...")
#        # YL 12/05/2021: use --system to prevent --user as some versions throw out an error that --user and --prefix
#        #                can't be used at the same time (but I didn't use --user at all!)
#        # YL 11/11/2021: use --no-deps to prevent pip uninstalling old versions of NumPy
#        if("${PYTHON_VERSION_MINOR}" STREQUAL "6")
#            # YL 11/11/2021: the minimal version of pandas required by the current BSE 3.1 is only 1.0
#            #                so let's install a 1.0.5 which needs NumPy>=1.13 so that some systems (SCARF) with
#            #                old system NumPy can also run
#            message("Command: ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install --user pandas==1.0.5 --no-deps")
#            execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install --user pandas==1.0.5 --no-deps)
#        else()
#            message("Command: ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install ${PIP_SYSTEM_FLAGS} --user pandas==1.0.5 --no-deps")
#            execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install ${PIP_SYSTEM_FLAGS} --user pandas==1.0.5 --no-deps)
#        endif()
#    endif()
    # install the BSE dependencies: mmcif_pdbx 
    execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pdbx; print(pdbx.__version__)"
                    OUTPUT_VARIABLE PDBX_VERSION
                    ERROR_QUIET)
    if("${PDBX_VERSION}" STREQUAL "" AND ${PIP})
        message("Installing missing BSE dependency mmcif_pdbx...")
        if("${PYTHON_VERSION_MINOR}" STREQUAL "6")
            message("Command: ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install --user --upgrade mmcif_pdbx")
            execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install --user --upgrade mmcif_pdbx)
        else()
            message("Command: ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install ${PIP_SYSTEM_FLAGS} --user --upgrade mmcif_pdbx")
            execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip ${PIP_FLAGS} --disable-pip-version-check install ${PIP_SYSTEM_FLAGS} --user --upgrade mmcif_pdbx)
        endif()
    endif()

else()
    return()
endif()

# get the BSE version
string(REGEX MATCH "([0-9]*[.])+([0-9]*[.])+[0-9]+" BSE_VERSION "${BSE}")
if("${BSE_VERSION}" STREQUAL "")
    set(BSE_VERSION ${BSE_DEFAULT_VERSION})
endif()
set(BSE_NAME bse-${BSE_VERSION})

# SOURCE_DIR is the destination of extraction
#   BUILD_ALWAYS:
#   UPDATE_DISCONNECTED: ${BSE_NO_UPDATE}, supported only after cmake 3.0
#   URL:                 file://${BSE_TARBALL}
#    UPDATE_COMMAND      git pull --allow-unrelated-histories ${BSE_GIT_REPO}
if(NOT TARGET bse)
    set(BSE_RECOMPILE YES)
endif()

# Supported compilers: clang craycc gcc gcc3 icc icc8 iccstatic mpicxx pathscale pgcc xlc xlc64
# Supported options: bigemulator bigsim causalft cuda g95 gfortran gm gm2 ifort lam lcs mic mlogft mpt ooc papi persistent pgf90 pthreads scyld smp syncft tsan
if(${CHEMSH_ARCH} STREQUAL "intel")
    set(BSE_ARCH Linux-x86_64-icc)
    set(BSE_C_COMPILER   "icc -fPIC -m64 -O3 -D__PURE_INTEL_C99_HEADERS__")
    set(BSE_C_FLAGS      "-diag-enable=power")
    set(BSE_CXX_FLAGS    "-diag-enable=power")
    set(BSE_CXX_COMPILER "${CMAKE_CXX_COMPILER} -fPIC -m64 -std=c++0x -O3 -D__PURE_INTEL_C99_HEADERS__")
elseif(${CHEMSH_ARCH} STREQUAL "gnu")
    set(BSE_ARCH Linux-x86_64-g++)
    set(BSE_C_COMPILER "$gcc -fPIC -m64 -O3")
    set(BSE_C_FLAGS    "-fexpensive-optimizations -ffast-math")
    set(BSE_CXX_COMPILER "${CMAKE_CXX_COMPILER} -fPIC -m64 -std=c++0x -O3")
    set(BSE_CXX_FLAGS    "-fexpensive-optimizations -ffast-math")
endif()

ExternalProject_Add(
    ${BSE_NAME}
    PREFIX              ${CMAKE_CURRENT_SOURCE_DIR}
    STAMP_DIR           stamp
    TMP_DIR             tmp
    DOWNLOAD_DIR        download
    SOURCE_DIR          src
    URL                 ${BSE_URL}
    GIT_REPOSITORY      ${BSE_GIT_REPO}
    GIT_TAG             ${BSE_GIT_TAG}
    GIT_SHALLOW         1
    GIT_PROGRESS        ${DEBUG}
    GIT_CONFIG          advice.detachedHead=false
    TIMEOUT             600
    DOWNLOAD_NO_PROGRESS YES
    CONFIGURE_COMMAND   ""
    BUILD_COMMAND       "${BSE_BUILD_COMMAND}"
    BUILD_IN_SOURCE     YES # important to prevent looking for directory BSE-build
    BUILD_ALWAYS        NO
    INSTALL_COMMAND     ""
    WORKING_DIRECTORY   src
)

set(BSE_OBJ_DIR ${BSE_SRC_DIR}/${BSE_ARCH}/obj)


set_directory_properties(PROPERTIES
    ADDITIONAL_MAKE_CLEAN_FILES "CMakeFiles;Makefile;cmake_install.cmake;download;src;stamp;tmp;__pycache__;"
)
