cmake_minimum_required(VERSION 3.15)
project(spinepy VERSION 0.0.1 LANGUAGES CXX)

# ======================== 核心通用配置（跨平台基础）========================
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_SHARED_LIBRARY_PREFIX "") # 统一移除共享库前缀，适配Python扩展命名

# Windows 通用配置：移除C++库前缀，确保生成spine.pyd而非libspine.pyd
if(WIN32)
    set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "")
    set(CMAKE_STATIC_LIBRARY_PREFIX_CXX "")
endif()

# ======================== 编译器自动检测 & 动态配置（核心优化）========================
# 定义编译器类型标识
set(IS_MINGW OFF)
set(IS_MSVC OFF)
set(IS_UNIX_GCC_CLANG OFF)

# 检测 MinGW (GNU编译器且Windows平台)
if(WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
    set(IS_MINGW ON)
    message(STATUS "===== Detected Compiler: MinGW (GCC) =====")
    # MinGW 全局静态链接配置：避免依赖MinGW动态库
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Bstatic,--whole-archive -lpthread -Wl,--no-whole-archive -Wl,-Bstatic")
    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc -static-libstdc++ -Wl,-Bstatic,--whole-archive -lpthread -Wl,--no-whole-archive -Wl,-Bstatic")
    # 优先查找静态库，禁用动态pthread
    set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib ${CMAKE_FIND_LIBRARY_SUFFIXES})
# 检测 MSVC (Visual Studio编译器)
elseif(WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
    set(IS_MSVC ON)
    message(STATUS "===== Detected Compiler: MSVC (Visual Studio) =====")
    # MSVC 全局静态链接配置：/MT表示静态链接C++运行时
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MT")
    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /OPT:REF /OPT:ICF") # 剥离无用段，减小体积
# 检测 Unix/Linux (GCC/Clang)
elseif(UNIX AND (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
    set(IS_UNIX_GCC_CLANG ON)
    message(STATUS "===== Detected Compiler: ${CMAKE_CXX_COMPILER_ID} (Unix) =====")
endif()

# ======================== 自动查找Python依赖（跨平台优化）========================
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
message(STATUS "===== Python Configuration =====")
message(STATUS "Python Version: ${Python3_VERSION}")
message(STATUS "Python Executable: ${Python3_EXECUTABLE}")
message(STATUS "Python Include Dirs: ${Python3_INCLUDE_DIRS}")
message(STATUS "Python Libraries: ${Python3_LIBRARIES}")

# ======================== 自动查找pybind11（多安装方式适配）========================
find_package(pybind11 CONFIG QUIET)
if(NOT pybind11_FOUND)
    message(STATUS "pybind11 not found via config, trying subdirectory...")
    if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/pybind11/CMakeLists.txt")
        add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/third_party/pybind11")
    else()
        message(FATAL_ERROR "pybind11 not found! Please install via pip or place in third_party/pybind11")
    endif()
endif()
message(STATUS "===== pybind11 Configuration =====")
message(STATUS "pybind11 Directory: ${pybind11_DIR}")
message(STATUS "pybind11 Version: ${pybind11_VERSION}")

# ======================== Spine源码路径配置（容错+通用性优化）========================
set(SPINE_INCLUDE_DIR ${SPINE_INCLUDE_DIR} 
    "${CMAKE_CURRENT_SOURCE_DIR}/spine-runtimes/spine-cpp/spine-cpp/include"
)
set(SPINE_SRC_FILES ${SPINE_SRC_FILES}
    "${CMAKE_CURRENT_SOURCE_DIR}/spine-runtimes/spine-cpp/spine-cpp/src/spine/*.cpp"
)

message(STATUS "===== Spine Configuration =====")
if(NOT EXISTS ${SPINE_INCLUDE_DIR})
    message(FATAL_ERROR "Spine include directory not found: ${SPINE_INCLUDE_DIR}\nPlease set SPINE_INCLUDE_DIR via cmake -DSPINE_INCLUDE_DIR=xxx")
endif()
file(GLOB ACTUAL_SPINE_SRC ${SPINE_SRC_FILES})
if(NOT ACTUAL_SPINE_SRC)
    message(FATAL_ERROR "No Spine source files found in: ${SPINE_SRC_FILES}\nPlease set SPINE_SRC_FILES via cmake -DSPINE_SRC_FILES=xxx")
endif()
message(STATUS "Spine Include Dir: ${SPINE_INCLUDE_DIR}")
message(STATUS "Found ${CMAKE_ARGV0} Spine source files")

# ======================== 绑定代码配置（自动识别+容错）========================
file(GLOB BINDING_FILES 
    "${CMAKE_CURRENT_SOURCE_DIR}/bindings/*.hpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/bindings/*.cpp"
)
set(BINDING_SRC_FILES
    "${CMAKE_CURRENT_SOURCE_DIR}/bind_spine.cpp"
    ${BINDING_FILES}
)
message(STATUS "===== Binding Configuration =====")
message(STATUS "Binding source files: ${BINDING_SRC_FILES}")

file(GLOB PYTHON_RENDER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/spine_opengl/*.py")
message(STATUS "Found ${CMAKE_ARGV0} Python render files")

# ======================== 分模块编译：先编译Spine静态库（解决段数超限核心）========================
add_library(spine_core STATIC ${ACTUAL_SPINE_SRC})
target_include_directories(spine_core PUBLIC ${SPINE_INCLUDE_DIR})
target_compile_features(spine_core PUBLIC cxx_std_14)

# ======================== 编译优化：编译器专属选项（动态适配）========================
if(IS_MINGW OR IS_UNIX_GCC_CLANG)
    # MinGW/Unix GCC/Clang 编译选项
    set(COMMON_COMPILE_OPTIONS
        -fdiagnostics-color=always
        -O2
        -ffunction-sections
        -fdata-sections
    )
    if(IS_MINGW)
        list(APPEND COMMON_COMPILE_OPTIONS -m64 -DMS_WIN64)
        set(MINGW_LINK_OPTIONS -Wl,--gc-sections -s) # -s 剥离符号表，减小体积
    endif()
    if(IS_UNIX_GCC_CLANG)
        list(APPEND COMMON_COMPILE_OPTIONS -fPIC) # Unix必需：位置无关代码
    endif()
    target_compile_options(spine_core PRIVATE ${COMMON_COMPILE_OPTIONS})
elseif(IS_MSVC)
    # MSVC 编译选项
    set(COMMON_COMPILE_OPTIONS
        /W4 /WX # 高警告级别，将警告视为错误
        /O2     # 优化级别2
        /GL     # 全域优化
        /EHsc   # 标准C++异常处理
    )
    target_compile_options(spine_core PRIVATE ${COMMON_COMPILE_OPTIONS})
    # MSVC 链接选项：全域优化+段清理
    target_link_options(spine_core PRIVATE /LTCG /OPT:REF /OPT:ICF)
endif()

# ======================== 生成Python扩展模块（最终链接）========================
pybind11_add_module(spine SHARED ${BINDING_SRC_FILES})

set_target_properties(spine PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION_MAJOR}
    PREFIX "" # 确保Python扩展无前缀
)

target_include_directories(spine PRIVATE
    ${SPINE_INCLUDE_DIR}
    "${CMAKE_CURRENT_SOURCE_DIR}/bindings"
    ${Python3_INCLUDE_DIRS}
)
target_link_libraries(spine PRIVATE
    spine_core
    Python3::Python
    pybind11::module
)

# 应用编译器专属链接选项
if(IS_MINGW)
    target_link_options(spine PRIVATE ${MINGW_LINK_OPTIONS})
    target_link_libraries(spine PRIVATE -static-libgcc -static-libstdc++ -lpthread) # 覆盖pybind11默认配置
elseif(IS_MSVC)
    target_link_options(spine PRIVATE /LTCG /OPT:REF /OPT:ICF) # MSVC 链接优化
endif()

# ======================== 安装配置（跨平台可选）========================
install(TARGETS spine
    LIBRARY DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
    RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
)
if(PYTHON_RENDER_FILES)
    install(FILES ${PYTHON_RENDER_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/spine_opengl)
endif()