cmake_minimum_required(VERSION 3.20)

project(
  SPICEUnion
  VERSION 0.2.0
  DESCRIPTION "C++ execution core for interactive SPICE-class simulator workflows"
  LANGUAGES CXX
)

option(SPICEUNION_BUILD_TESTS "Build SPICEUnion tests" ON)
option(SPICEUNION_ENABLE_EXTERNAL_TESTS "Enable tests that require external EDA tools" OFF)
option(SPICEUNION_ENABLE_LIBPSF_READER "Enable optional henjo/libpsf PSF result reader backend" OFF)
option(SPICEUNION_BUILD_PYTHON_BINDINGS "Build optional pybind11 Python bindings" OFF)
option(SPICEUNION_BUILD_C_API "Build optional C ABI facade" OFF)

set(SPICEUNION_LIBPSF_INCLUDE_DIR "" CACHE PATH "Directory that contains henjo/libpsf psf.h")
set(SPICEUNION_LIBPSF_LIBRARY "" CACHE FILEPATH "Path to henjo/libpsf library")
set(SPICEUNION_ORDERED_POOL_SOURCE_DIR
    "${CMAKE_CURRENT_SOURCE_DIR}/../OrderedConcurrentPool"
    CACHE PATH
    "Path to the standalone OrderedConcurrentPool project"
)
set(SPICEUNION_ORDERED_POOL_GIT_URL
    "https://github.com/Snappersontheprowl/OrderedConcurrentPool.git"
    CACHE STRING
    "OrderedConcurrentPool repository used by FetchContent fallback"
)
set(SPICEUNION_ORDERED_POOL_GIT_TAG
    "80511bd9a9420fa72fdf2e0ee9b4bd3686b920e7"
    CACHE STRING
    "OrderedConcurrentPool commit used by FetchContent fallback"
)

set(SPICEUNION_SPECTRE_MATERIALS_DIR
    "${CMAKE_CURRENT_SOURCE_DIR}/../spectre_materials"
    CACHE PATH
    "Path to the private materials directory with external/netlist and external/pdk"
)

if(SPICEUNION_ENABLE_EXTERNAL_TESTS AND NOT EXISTS
   "${SPICEUNION_SPECTRE_MATERIALS_DIR}/external/netlist")
  message(FATAL_ERROR
    "SPICEUNION_ENABLE_EXTERNAL_TESTS=ON requires SPICEUNION_SPECTRE_MATERIALS_DIR "
    "pointing to a directory that contains external/netlist and external/pdk. "
    "Current value: "
    "${SPICEUNION_SPECTRE_MATERIALS_DIR}"
  )
endif()

set(ORDERED_CONCURRENT_POOL_BUILD_TESTS OFF CACHE BOOL
    "Build OrderedConcurrentPool tests when embedded by SPICEUnion" FORCE)
set(ORDERED_CONCURRENT_POOL_BUILD_EXAMPLES OFF CACHE BOOL
    "Build OrderedConcurrentPool examples when embedded by SPICEUnion" FORCE)
if(EXISTS "${SPICEUNION_ORDERED_POOL_SOURCE_DIR}/CMakeLists.txt")
  add_subdirectory(
    "${SPICEUNION_ORDERED_POOL_SOURCE_DIR}"
    "${CMAKE_BINARY_DIR}/_deps/ordered_concurrent_pool"
  )
else()
  # 单仓构建（pip / sdist / wheel）回退：sibling 不存在时从上游拉取。
  include(FetchContent)
  FetchContent_Declare(
    ordered_concurrent_pool
    GIT_REPOSITORY "${SPICEUNION_ORDERED_POOL_GIT_URL}"
    GIT_TAG "${SPICEUNION_ORDERED_POOL_GIT_TAG}"
  )
  FetchContent_MakeAvailable(ordered_concurrent_pool)
  message(STATUS "OrderedConcurrentPool fetched via FetchContent (${SPICEUNION_ORDERED_POOL_GIT_TAG})")
endif()

if(SPICEUNION_ENABLE_LIBPSF_READER)
  if(SPICEUNION_LIBPSF_INCLUDE_DIR)
    set(_spiceunion_libpsf_include_dir "${SPICEUNION_LIBPSF_INCLUDE_DIR}")
  else()
    unset(_spiceunion_libpsf_include_dir)
    find_path(_spiceunion_libpsf_include_dir
      NAMES psf.h
      HINTS
        "${CMAKE_CURRENT_SOURCE_DIR}/local/external/libpsf/install-pic/include"
        "${CMAKE_CURRENT_SOURCE_DIR}/local/external/libpsf/install/include"
    )
  endif()

  if(SPICEUNION_LIBPSF_LIBRARY)
    set(_spiceunion_libpsf_library "${SPICEUNION_LIBPSF_LIBRARY}")
  else()
    unset(_spiceunion_libpsf_library)
    find_library(_spiceunion_libpsf_library
      NAMES psf libpsf.a
      HINTS
        "${CMAKE_CURRENT_SOURCE_DIR}/local/external/libpsf/install-pic/lib"
        "${CMAKE_CURRENT_SOURCE_DIR}/local/external/libpsf/install-pic/lib64"
        "${CMAKE_CURRENT_SOURCE_DIR}/local/external/libpsf/install/lib"
        "${CMAKE_CURRENT_SOURCE_DIR}/local/external/libpsf/install/lib64"
    )
  endif()

  if(NOT _spiceunion_libpsf_include_dir OR NOT _spiceunion_libpsf_library)
    message(FATAL_ERROR
      "SPICEUNION_ENABLE_LIBPSF_READER=ON requires henjo/libpsf. "
      "Set SPICEUNION_LIBPSF_INCLUDE_DIR and SPICEUNION_LIBPSF_LIBRARY, "
      "or build it under local/external/libpsf/install."
    )
  endif()

  set(SPICEUNION_LIBPSF_INCLUDE_DIR "${_spiceunion_libpsf_include_dir}" CACHE PATH
      "Directory that contains henjo/libpsf psf.h" FORCE)
  set(SPICEUNION_LIBPSF_LIBRARY "${_spiceunion_libpsf_library}" CACHE FILEPATH
      "Path to henjo/libpsf library" FORCE)
endif()

if(SPICEUNION_BUILD_C_API)
  message(FATAL_ERROR
    "SPICEUNION_BUILD_C_API is reserved for a later M4 stage and is not implemented yet."
  )
endif()

add_library(spiceunion_core STATIC
  src/core/evaluator.cpp
  src/core/metrics.cpp
  src/core/task_result.cpp
  src/parse/result.cpp
  src/parse/result_reader.cpp
  src/parse/psf_ascii_backend.cpp
  src/pool/simulator_pool.cpp
  src/session/ngspice_session.cpp
  src/session/spectre_protocol.cpp
  src/session/spectre_session.cpp
  src/toolchain/simulator_probe.cpp
  src/workflow/simulation.cpp
  src/core/version.cpp
)

set_target_properties(spiceunion_core PROPERTIES
  POSITION_INDEPENDENT_CODE ON
)

if(SPICEUNION_ENABLE_LIBPSF_READER)
  target_sources(spiceunion_core
    PRIVATE
      src/parse/libpsf_backend.cpp
  )
endif()

target_compile_features(spiceunion_core PUBLIC cxx_std_17)

target_include_directories(spiceunion_core
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
  PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}
)

target_compile_options(spiceunion_core PRIVATE
  $<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic>
)

target_link_libraries(spiceunion_core
  PRIVATE
    ocp::ordered_concurrent_pool
)

if(SPICEUNION_ENABLE_LIBPSF_READER)
  target_compile_definitions(spiceunion_core
    PRIVATE
      SPICEUNION_ENABLE_LIBPSF_READER=1
  )

  target_include_directories(spiceunion_core SYSTEM
    PRIVATE
      ${SPICEUNION_LIBPSF_INCLUDE_DIR}
  )

  target_link_libraries(spiceunion_core
    PRIVATE
      ${SPICEUNION_LIBPSF_LIBRARY}
  )
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9)
  target_link_libraries(spiceunion_core PUBLIC stdc++fs)
endif()

if(SPICEUNION_BUILD_TESTS)
  enable_testing()
endif()

if(SPICEUNION_BUILD_PYTHON_BINDINGS)
  add_subdirectory(bindings/python)
endif()

if(SPICEUNION_BUILD_TESTS)
  add_subdirectory(tests)
endif()
