cmake_minimum_required(VERSION 3.22)
project(vidur_native LANGUAGES CXX)

################################################################################
# Build settings
################################################################################

set(CMAKE_CXX_STANDARD          20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS             "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_RELEASE     "-g -O3")
set(BUILD_SHARED_LIBS           OFF)

# Generate compile_commands.json for clang tools
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

################################################################################
# Python Configuration
################################################################################

# Supported python versions
set(PYTHON_SUPPORTED_VERSIONS "3.8;3.9;3.10;3.11")

# Include utility functions
include(${CMAKE_CURRENT_LIST_DIR}/cmake/utils.cmake)

# Find Python
if(DEFINED VIDUR_PYTHON_EXECUTABLE)
    find_python_from_executable(
    "${VIDUR_PYTHON_EXECUTABLE}"
    "${PYTHON_SUPPORTED_VERSIONS}"
    )
else()
    message(FATAL_ERROR
        "Please set VIDUR_PYTHON_EXECUTABLE to the path of the desired python version"
        " before running cmake configure."
    )
endif()

################################################################################
# Dependencies
################################################################################

# Fetch and include pybind11
include(FetchContent)
FetchContent_Declare(
    pybind11
    GIT_REPOSITORY https://github.com/pybind/pybind11.git
    GIT_TAG        v2.11.1
)

# Fetch and include fmt
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG        10.1.1
)

FetchContent_MakeAvailable(pybind11 fmt)

# Set include directories
include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/csrc/include
)

################################################################################
# Source Files
################################################################################

# Core source files
file(GLOB CORE_SOURCES
    "csrc/vidur/native/config/*.cpp"
    "csrc/vidur/native/entities/*.cpp"
    "csrc/vidur/native/execution_time_predictor/*.cpp"
)

# Python binding source files
file(GLOB PYBIND_SOURCES
    "csrc/vidur/native/config/config_pybind.cpp"
    "csrc/vidur/native/entities/entities_pybind.cpp"
    "csrc/vidur/native/execution_time_predictor/execution_time_predictor_pybind.cpp"
    "csrc/vidur/native/pybind.cpp"
)

list(REMOVE_ITEM CORE_SOURCES ${PYBIND_SOURCES})

# Core header files
file(GLOB CORE_HEADERS
    "csrc/include/vidur/config/*.h"
    "csrc/include/vidur/entities/*.h"
    "csrc/include/vidur/execution_time_predictor/*.h"
)

# Python binding header files
file(GLOB PYBIND_HEADERS
    "csrc/include/vidur/config/config_pybind.h"
    "csrc/include/vidur/entities/entities_pybind.h"
    "csrc/include/vidur/execution_time_predictor/execution_time_predictor_pybind.h"
    "csrc/include/vidur/pybind.h"
)

list(REMOVE_ITEM CORE_HEADERS ${PYBIND_HEADERS})

################################################################################
# Library Output Directory
################################################################################

# Set output directory
if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/vidur)
endif()

################################################################################
# Vidur Library
################################################################################

# Create the shared library
add_library(vidur SHARED ${CORE_SOURCES})
target_include_directories(vidur PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/csrc/include
)
target_link_libraries(vidur PUBLIC fmt::fmt)

# Set library properties
set_target_properties(vidur PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib"
    DEBUG_POSTFIX "_d"
    POSITION_INDEPENDENT_CODE ON
)

################################################################################
# Python Module
################################################################################

# Create the Python module
pybind11_add_module(_native ${CORE_SOURCES} ${PYBIND_SOURCES})

target_include_directories(_native PRIVATE
    ${CORE_HEADERS}
    ${PYBIND_HEADERS}
)

target_link_libraries(_native PRIVATE
    ${pybind11_LIBRARIES}
    fmt::fmt
)

set_target_properties(_native PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
    DEBUG_POSTFIX "_d"
)

################################################################################
# Install Headers
################################################################################

# Create directory for headers
add_custom_command(TARGET vidur POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib/include"
)

# Copy each header file while preserving directory structure
foreach(HEADER ${CORE_HEADERS})
    # Get the relative path of the header (relative to csrc/include)
    file(RELATIVE_PATH RELATIVE_HEADER_PATH 
        "${CMAKE_CURRENT_SOURCE_DIR}/csrc/include" 
        ${HEADER}
    )
    
    # Get the destination path
    get_filename_component(DEST_DIR 
        "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib/include/${RELATIVE_HEADER_PATH}" 
        DIRECTORY
    )
    get_filename_component(HEADER_NAME ${HEADER} NAME)
    
    # Create the destination directory
    add_custom_command(TARGET vidur POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E make_directory "${DEST_DIR}"
    )
    
    # Copy the header file
    add_custom_command(TARGET vidur POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
            ${HEADER}
            "${DEST_DIR}/${HEADER_NAME}"
    )
endforeach()