##############################################################################
#
#  Cmake file for XAD library and headers
#
#  This file is part of XAD, a comprehensive C++ library for
#  automatic differentiation.
#
#  Copyright (C) 2010-2026 Xcelerit Computing Ltd.
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Affero General Public License as published
#  by the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program 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 Affero General Public License for more details.
#
#  You should have received a copy of the GNU Affero General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

configure_file(XAD/Version.hpp.in
               ${CMAKE_CURRENT_BINARY_DIR}/XAD/Version.hpp
               @ONLY)
configure_file(XAD/Config.hpp.in
               ${CMAKE_CURRENT_BINARY_DIR}/XAD/Config.hpp
               @ONLY)
configure_file(XAD/Export.hpp.in
               ${CMAKE_CURRENT_BINARY_DIR}/XAD/Export.hpp
               @ONLY)

file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/XAD/GenerateMode.hpp "//Generated by Cmake")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/XAD/Instantiations.hpp "//Generated by Cmake")

set(XAD_MODES
    "adjoint:2:float"
    "adjoint:2:double"
    "adjoint:4:float"
    "adjoint:4:double"
    "forward:1:adjoint:4"
    "adjoint:2:forward:2"
    "adjoint:2:forward:4"
    "adjoint:1:adjoint:2"
    "forward:2:adjoint:4")
set(XAD_EXTRA_MODES "forward:2:adjoint:4:float" CACHE STRING
   "Additional Modes format (forward|adjoint):(N):(forward|adjoint):(M):(baseType);...")


foreach(extra_mode IN LISTS XAD_EXTRA_MODES)
    list(FIND XAD_MODES ${extra_mode} find)
    if (find EQUAL -1)
        list(APPEND XAD_MODES ${extra_mode})
    endif()
endforeach()

list(APPEND modes "")
foreach(listextra IN LISTS XAD_MODES)
    set(basetype "double")
    string(REPLACE ":" ";" listmode ${listextra})
    list(LENGTH listmode SIZE)
    if(SIZE LESS 4)
        # first order
        if(SIZE EQUAL 3)
            list(GET listmode 2 basetype)
        endif()
        list(GET listmode 0 INNERTYPE)
        list(GET listmode 1 N)
        if(INNERTYPE STREQUAL "adjoint" AND N GREATER 1)
            list(APPEND modes "${basetype}, ${N}")
        endif()
    else()
        # second order
        if(SIZE EQUAL 5)
            list(GET listmode 4 basetype)
        endif()
        list(GET listmode 0 INNERTYPE)
        list(GET listmode 1 N)
        list(GET listmode 2 ACTIVETYPE)
        list(GET listmode 3 M)

        if (M EQUAL 1 AND N EQUAL 1)
            continue()
        endif()
        if(INNERTYPE STREQUAL "forward" AND ACTIVETYPE STREQUAL "adjoint")
            list(APPEND modes "FReal<${basetype},${N}>, ${M}")
        elseif(INNERTYPE STREQUAL "adjoint" AND ACTIVETYPE STREQUAL "forward")
            if(NOT M EQUAL 1)
                list(APPEND modes "${basetype}, ${N}")
            endif()
        elseif(INNERTYPE STREQUAL "adjoint" AND ACTIVETYPE STREQUAL "adjoint")
            list(APPEND modes "AReal<${basetype}, ${N}>, ${M}")
        endif()
    endif()
endforeach()

list(REMOVE_DUPLICATES modes)

foreach(mode IN LISTS modes)
    file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/XAD/GenerateMode.hpp "\nXAD_DECLARE_EXTERN_TAPE(XAD_SINGLE_ARG(${mode}))")
    file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/XAD/Instantiations.hpp "\nMAKE_TAPE_TLS(XAD_SINGLE_ARG(${mode}))")
endforeach()

set(public_headers
    XAD/Vec.hpp
    XAD/AlignedAllocator.hpp
    XAD/BinaryDerivativeImpl.hpp
    XAD/BinaryExpr.hpp
    XAD/BinaryFunctors.hpp
    XAD/BinaryMathFunctors.hpp
    XAD/BinaryOperatorMacros.hpp
    XAD/BinaryOperators.hpp
    XAD/CheckpointCallback.hpp
    XAD/ChunkContainer.hpp
    XAD/Complex.hpp
    XAD/Exceptions.hpp
    XAD/Expression.hpp
    XAD/Hessian.hpp
    XAD/Interface.hpp
    XAD/Jacobian.hpp
    XAD/Literals.hpp
    XAD/Macros.hpp
    XAD/MathFunctions.hpp
    XAD/OperationsContainer.hpp
    XAD/OperationsContainerPaired.hpp
    XAD/RealDirect.hpp
    XAD/ReusableRange.hpp
    XAD/StdCompatibility.hpp
    XAD/Tape.hpp
    XAD/TapeContainer.hpp
    XAD/Traits.hpp
    XAD/TypeTraits.hpp
    XAD/UnaryExpr.hpp
    XAD/UnaryFunctors.hpp
    XAD/UnaryMathFunctors.hpp
    XAD/UnaryOperatorMacros.hpp
    XAD/UnaryOperators.hpp
    XAD/FRealDirect.hpp
    XAD/ARealDirect.hpp

    XAD/XAD.hpp
    # generated files
    ${CMAKE_CURRENT_BINARY_DIR}/XAD/Version.hpp
    ${CMAKE_CURRENT_BINARY_DIR}/XAD/Config.hpp
    ${CMAKE_CURRENT_BINARY_DIR}/XAD/Export.hpp
    ${CMAKE_CURRENT_BINARY_DIR}/XAD/GenerateMode.hpp
)

# JIT/Codegen compilation support headers (optional)
if(XAD_ENABLE_CODEGEN)
    list(APPEND public_headers
        XAD/JITCompiler.hpp
        XAD/JITGraph.hpp
        XAD/JITBackendInterface.hpp
        XAD/JITGraphInterpreter.hpp
        XAD/JITOpCodeTraits.hpp
        XAD/JITExprTraits.hpp
        XAD/ABool.hpp
    )
endif()


set(srcfiles Tape.cpp ${public_headers})
if (MSVC)
    list(APPEND srcfiles ${PROJECT_SOURCE_DIR}/XAD.natvis)
endif()

# JIT TLS storage (required when codegen/JIT is ON)
if(XAD_ENABLE_CODEGEN)
    list(APPEND srcfiles
        XAD/JITGraphInterpreter.cpp
        XAD/JITCompilerTLS.cpp
    )
endif()

if(XAD_SHARED_LIBS)
    set(xad_lib_type SHARED)
else()
    set(xad_lib_type STATIC)
endif()
xad_add_library(xad ${xad_lib_type} ${srcfiles})
target_include_directories(xad PUBLIC
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
    "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>"
)
# Link codegen when enabled
if(XAD_ENABLE_CODEGEN)
    target_link_libraries(xad PUBLIC xad_codegen)
endif()

# Install targets
install(FILES ${public_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/XAD)
install(TARGETS xad
    EXPORT XADTargets
    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
if(MSVC AND XAD_SHARED_LIBS)
    install(FILES "$<TARGET_PDB_FILE:xad>" DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
elseif(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")   # clang-cl does not generate PDB files, but rather embeds them
    # install PDB files
    get_target_property(xad_pdb_name xad COMPILE_PDB_NAME)
    get_target_property(xad_pdb_name_debug xad COMPILE_PDB_NAME_DEBUG)
    get_target_property(xad_pdb_output_dir xad PDB_OUTPUT_DIRECTORY)
    get_property(multiconf GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
    if(multiconf)
        install(FILES "${xad_pdb_output_dir}/\${CMAKE_INSTALL_CONFIG_NAME}/$<$<CONFIG:Debug>:${xad_pdb_name_debug}>$<$<NOT:$<CONFIG:Debug>>:${xad_pdb_name}>.pdb"
            DESTINATION "${CMAKE_INSTALL_LIBDIR}"
            CONFIGURATIONS Debug RelWithDebInfo
        )
    else()
        install(FILES "${xad_pdb_output_dir}/$<$<CONFIG:Debug>:${xad_pdb_name_debug}>$<$<NOT:$<CONFIG:Debug>>:${xad_pdb_name}>.pdb"
            DESTINATION "${CMAKE_INSTALL_LIBDIR}"
            CONFIGURATIONS Debug RelWithDebInfo
        )
    endif()
endif()

# install CMake export configuration files
include(CMakePackageConfigHelpers)

set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(version_file "${generated_dir}/XADConfigVersion.cmake")
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/XAD")

write_basic_package_version_file(${version_file}
    VERSION ${XAD_VERSION}
    COMPATIBILITY SameMajorVersion
)
export(EXPORT XADTargets
    FILE "${PROJECT_BINARY_DIR}/cmake/XADTargets.cmake"
    NAMESPACE XAD::
)
install(EXPORT XADTargets
    FILE XADTargets.cmake
    NAMESPACE XAD::
    DESTINATION ${config_install_dir}
)
configure_package_config_file(
    "${PROJECT_SOURCE_DIR}/cmake/XADConfig.cmake.in"
    "${generated_dir}/XADConfig.cmake"
    INSTALL_DESTINATION ${config_install_dir}
)
install(FILES
            "${generated_dir}/XADConfigVersion.cmake"
            "${generated_dir}/XADConfig.cmake"
        DESTINATION
            ${config_install_dir}
)
