cmake_minimum_required(VERSION 3.18)
project(pMoleculeCMake LANGUAGES C)

# -------------------------------
# Python
# -------------------------------
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module)
include(PDynamoPython)
include(PDynamoTests)
include(PDynamoCython)
include(PDynamoExtensions)
include(PDynamoLibraries)
include(PDynamoInstall)

pd_configure_cython_option()
option(PDYNAMO_ENABLE_PMOLECULE_FUNCTIONAL_TESTS "Enable legacy pMolecule functional tests." OFF)

# -------------------------------
# Paths
# -------------------------------
set(PMOLECULE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
set(EXT_DIR        ${PMOLECULE_ROOT}/extensions)

set(LIBFILE    ${EXT_DIR}/libraries)
set(CINC_DIR   ${EXT_DIR}/cinclude)
set(CSRC_DIR   ${EXT_DIR}/csource)
set(PSRC_DIR   ${EXT_DIR}/psource)
set(PYREX_DIR  ${EXT_DIR}/pyrex)

set(PCORE_ROOT     ${CMAKE_CURRENT_SOURCE_DIR}/../pCore-2.0.0)
set(PCORE_EXT_DIR  ${PCORE_ROOT}/extensions)
set(PCORE_CINC_DIR ${PCORE_EXT_DIR}/cinclude)
set(PCORE_PYREX_DIR ${PCORE_EXT_DIR}/pyrex)

# Shared runtime root:
#   build/python/pCore
#   build/python/pMolecule
set(PYTHON_ROOT_DIR ${CMAKE_BINARY_DIR}/python)
set(PY_OUTPUT_DIR   ${PYTHON_ROOT_DIR}/pMolecule)
set(GENERATED_DIR   ${CMAKE_BINARY_DIR}/generated)

file(MAKE_DIRECTORY ${PYTHON_ROOT_DIR})
file(MAKE_DIRECTORY ${PY_OUTPUT_DIR})
file(MAKE_DIRECTORY ${GENERATED_DIR})

set(PMOLECULE_SHIM_MODULES
    ADIISObjectiveFunction
    DFTFunctional
    Element
    EnergyTerms
    QCChargeModelOptions
    QCMMLinkAtomCouplingOptions
    QCOnePDM
    QCParameters
    System
)
set(PMOLECULE_SHIM_FILES "")

foreach(MOD IN LISTS PMOLECULE_SHIM_MODULES)
    set(SHIM_PATH "${PYTHON_ROOT_DIR}/${MOD}.py")
    file(WRITE "${SHIM_PATH}"
"from pMolecule.${MOD} import *\n")
    list(APPEND PMOLECULE_SHIM_FILES "${SHIM_PATH}")
endforeach()

# -------------------------------
# Copy Python files automatically
# - copy pMolecule/*.py
# - exclude original __init__.py
# - exclude junk files
# -------------------------------
set(PMOLECULE_PY_EXCLUDE)

pd_copy_python_package(
    "${CMAKE_CURRENT_SOURCE_DIR}/pMolecule"
    "pMolecule"
    "${PYTHON_ROOT_DIR}"
    "${PMOLECULE_PY_EXCLUDE}"
    PMOLECULE_COPIED_PY_FILES
)

set(PMOLECULE_MODULE_EXCLUDE
    NBModelPB
    NBModelPBState
)
# -------------------------------
# Optional module exclusion list
# -------------------------------
#set(PMOLECULE_MODULE_EXCLUDE
#    # Add temporarily broken modules here, e.g.
#    # QCModelORCAState
#)

# -------------------------------
# Build low-level C libraries from libraries file
# -------------------------------
pd_load_c_libraries(
    "${LIBFILE}"
    "${EXT_DIR}"
    "${PCORE_CINC_DIR}"
    PMOLECULE_LIBS
)

# -------------------------------
# Build extension modules
# -------------------------------

message(STATUS "PCORE_LIBS_FOR_PMolecule in pMolecule: ${PCORE_LIBS_FOR_PMolecule}")

pd_build_python_extensions(
    "pMolecule"
    "${PYREX_DIR}"
    "${PSRC_DIR}"
    "${CINC_DIR}"
    "${GENERATED_DIR}"
    "${PY_OUTPUT_DIR}"
    "${PMOLECULE_MODULE_EXCLUDE}"
    "${PCORE_PYREX_DIR}"
    "${PCORE_CINC_DIR}"
    "${PMOLECULE_LIBS};${PCORE_LIBS_FOR_PMolecule}"
    "pMolecule"
    PMOLECULE_EXTENSION_TARGETS
    PMOLECULE_DETECTED_MODULES
)
# -------------------------------
# Summary
# -------------------------------
message(STATUS "QBX_USE_CYTHON          : ${QBX_USE_CYTHON}")
message(STATUS "pMolecule libraries     : ${PMOLECULE_LIBS}")
message(STATUS "Copied Python files     : ${PMOLECULE_COPIED_PY_FILES}")
message(STATUS "Detected modules        : ${PMOLECULE_DETECTED_MODULES}")
message(STATUS "Extension targets       : ${PMOLECULE_EXTENSION_TARGETS}")
message(STATUS "Python root dir         : ${PYTHON_ROOT_DIR}")
message(STATUS "Python output dir       : ${PY_OUTPUT_DIR}")

# -------------------------------
# Tests
# -------------------------------
pd_add_import_tests(
    "${CMAKE_CURRENT_SOURCE_DIR}/tests"
    "${PYTHON_ROOT_DIR}"
    "${PY_OUTPUT_DIR}"
    "test_import_pmolecule"
    "test_import_all_pmolecule_extensions"
)

if(PDYNAMO_ENABLE_PMOLECULE_FUNCTIONAL_TESTS)
    set(PMOLECULE_FUNCTIONAL_TESTS
        CrystalMMEnergies
        CrystalQCEnergies
        CrystalQCMMEnergies
        DFTRKSEnergies
        DFTUKSEnergies
        DihydrogenDissociation
        GridUpdating
        MNDOCIEnergies
        MNDOCIEnergiesQCMM
        MNDOQCMMEnergies
        MNDORHFEnergies
        MNDOUHFEnergies
        MergePrune
        NBModelABFSCentering
        OPLSProteinParameters
        PickleMM
        PickleQCMM
        QCMMWaterDimerBinding
        RadiiOfGyration
        SQLAtomSelection
        SurfaceCrossing
    )

    set(PMOLECULE_FUNCTIONAL_SCRATCH_DIR "${CMAKE_BINARY_DIR}/scratch/pmolecule-tests")
    file(MAKE_DIRECTORY "${PMOLECULE_FUNCTIONAL_SCRATCH_DIR}")

    foreach(TEST_NAME IN LISTS PMOLECULE_FUNCTIONAL_TESTS)
        add_test(
            NAME "test_pmolecule_${TEST_NAME}"
            COMMAND ${Python3_EXECUTABLE}
                    "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_legacy_test.py"
                    "${PYTHON_ROOT_DIR}"
                    "${CMAKE_CURRENT_SOURCE_DIR}/tests"
                    "${TEST_NAME}"
                    "${CMAKE_SOURCE_DIR}"
                    "${CMAKE_CURRENT_SOURCE_DIR}"
                    "${PMOLECULE_FUNCTIONAL_SCRATCH_DIR}"
        )

        set_tests_properties(
            "test_pmolecule_${TEST_NAME}"
            PROPERTIES
            ENVIRONMENT "PYTHONPATH=${PYTHON_ROOT_DIR};PDYNAMO_PARAMETERS=${CMAKE_BINARY_DIR}/parameters;PDYNAMO_PMOLECULE=${CMAKE_CURRENT_SOURCE_DIR};PDYNAMO_ROOT=${CMAKE_SOURCE_DIR};PDYNAMO_SCRATCH=${PMOLECULE_FUNCTIONAL_SCRATCH_DIR};PDYNAMO_TEST_QUIET=1;PDYNAMO_TEST_SHORT=1;PYTHONDONTWRITEBYTECODE=1"
            TIMEOUT 600
        )
    endforeach()
endif()

pd_install_python_package(
    "${CMAKE_CURRENT_SOURCE_DIR}/pMolecule"
    "pMolecule"
)

pd_install_extension_targets(
    "pMolecule"
    ${PMOLECULE_EXTENSION_TARGETS}
)

pd_install_root_python_files(
    ${PMOLECULE_SHIM_FILES}
)

# -------------------------------
# Convenience target
# -------------------------------
add_custom_target(check
    COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
    DEPENDS ${PMOLECULE_EXTENSION_TARGETS}
)
