cmake_minimum_required(VERSION 3.15...3.26)
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)

set(PYBIND11_NEWPYTHON ON)

option(BUILD_TESTS      "Build tests"    OFF)
option(BUILD_EXAMPLES   "Build examples" OFF)
option(UTPP_INCLUDE_TESTS_IN_BUILD   "Build tests" OFF)
option(MKF_INCLUDE_TESTS      "Build tests"    OFF)
option(BUILD_TESTS      "Build tests"    OFF)
option(BUILD_EXAMPLES   "Build examples" OFF)
option(BUILD_DEMO   "Build examples" FALSE)
option(HAVE_LAPACK   "HAVE_LAPACK" 0)

set(CMAKE_CXX_STANDARD 23) 
set(CMAKE_CXX_STANDARD_REQUIRED ON)

if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Ox")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0")
    set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
else ()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-deprecated-declarations -Wno-unused-parameter -Wno-switch")
    set(CMAKE_POSITION_INDEPENDENT_CODE ON)

    # set(CMAKE_BUILD_TYPE RelWithDebInfo)
    # set(CMAKE_BUILD_TYPE MinSizeRel)
    set(CMAKE_BUILD_TYPE Release)
    
    # Enable Link Time Optimization to reduce binary size
    set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE)  # TEMP: disable LTO (OOM)
    
    # Strip symbols and use size optimization
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s -Os")
    set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--strip-all")
endif()

SET(MAS_DIRECTORY "${CMAKE_BINARY_DIR}/MAS/")
if(DEFINED ENV{LOCAL_MKF_DIR})
    SET(LOCAL_MKF_MAS TRUE)
    SET(MAS_DIR "${PROJECT_SOURCE_DIR}/_mas_local")
    SET(MKF_DIR "${PROJECT_SOURCE_DIR}/_mkf_local")
else()
    SET(LOCAL_MKF_MAS FALSE)
    SET(MAS_DIR "${CMAKE_BINARY_DIR}/_deps/mas-src")
    SET(MKF_DIR "${CMAKE_BINARY_DIR}/_deps/mkf-src")
endif()
SET(FETCHCONTENT_QUIET FALSE)

message(STATUS MAS_DIRECTORY)
message(STATUS ${MAS_DIRECTORY})
message(STATUS MAS_DIR)
message(STATUS ${MAS_DIR})
message(STATUS MKF_DIR)
message(STATUS ${MKF_DIR})

include(FetchContent)

message(STATUS "Fetching https://github.com/nlohmann/json.git")
FetchContent_Declare(json
    GIT_REPOSITORY https://github.com/nlohmann/json.git
    GIT_TAG  tags/v3.11.3
    GIT_PROGRESS TRUE
    )
FetchContent_MakeAvailable(json)
include_directories("${CMAKE_BINARY_DIR}/_deps/json-src/include/nlohmann/")
include_directories("${CMAKE_BINARY_DIR}/_deps/json-src/include/")

message(STATUS "Fetching pybind11")
FetchContent_Declare(pybind11
        GIT_REPOSITORY https://github.com/pybind/pybind11.git)

message(STATUS "Fetching pybind11_json")
FetchContent_Declare(pybind11_json
        GIT_REPOSITORY https://github.com/pybind/pybind11_json.git)

FetchContent_MakeAvailable( pybind11 pybind11_json)
include_directories("${CMAKE_BINARY_DIR}/_deps/pybind11-src/include/")
include_directories("${CMAKE_BINARY_DIR}/_deps/pybind11_json-src/include/")

message(STATUS "Fetching spline")
FetchContent_Declare(spline
    GIT_REPOSITORY https://github.com/AlfVII/spline.git)
FetchContent_MakeAvailable(spline)
include_directories("${CMAKE_BINARY_DIR}/_deps/spline-src/src")
    
FetchContent_Declare(levmar
    GIT_REPOSITORY https://github.com/AlfVII/levmar.git
    GIT_TAG main)
FetchContent_MakeAvailable(levmar)
include_directories("${CMAKE_BINARY_DIR}/_deps/levmar-src")

FetchContent_Declare(svg
    GIT_REPOSITORY https://github.com/AlfVII/svg)
FetchContent_MakeAvailable(svg)
include_directories("${CMAKE_BINARY_DIR}/_deps/svg-src/src")

message(STATUS "Fetching magic-enum")
FetchContent_Declare(magic-enum
    GIT_REPOSITORY https://github.com/Neargye/magic_enum
    GIT_TAG  tags/v0.9.6)
FetchContent_MakeAvailable(magic-enum)
include_directories("${CMAKE_BINARY_DIR}/_deps/magic-enum-src/include/magic_enum")

FetchContent_Declare(rapidfuzz
  GIT_REPOSITORY https://github.com/rapidfuzz/rapidfuzz-cpp.git
  GIT_TAG main)
FetchContent_MakeAvailable(rapidfuzz)

message(STATUS "Fetching Eigen")
FetchContent_Declare(eigen
  GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
  GIT_TAG 3.4.0
  GIT_PROGRESS TRUE
  GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(eigen)
include_directories("${CMAKE_BINARY_DIR}/_deps/eigen-src")

if(NOT LOCAL_MKF_MAS)
    message(STATUS "Fetching MKF")
    # Force fresh clone by using a unique timestamp - update this when MKF changes
    set(MKF_FORCE_REFRESH "2025-02-26-01")
    # Tell MKF to disable matplotplusplus and use SVG-based Painter instead
    set(INCLUDE_PYMKF ON CACHE BOOL "Build Python interface" FORCE)
    FetchContent_Declare(MKF
            GIT_REPOSITORY https://github.com/OpenMagnetics/MKF.git
            GIT_TAG main
            GIT_PROGRESS TRUE
            GIT_SHALLOW TRUE)

    message(STATUS "Fetching mas")
    # Skip Git LFS to avoid bandwidth quota issues - data files are optional for build
    set(ENV{GIT_LFS_SKIP_SMUDGE} "1")
    # Force fresh clone by using a unique timestamp - update this when MAS changes
    set(MAS_FORCE_REFRESH "2025-02-26-01")
    FetchContent_Declare(
           mas
           GIT_REPOSITORY https://github.com/OpenMagnetics/MAS.git
           GIT_TAG main
           GIT_SHALLOW TRUE)

    message(STATUS "Fetching Properties mas")
    FetchContent_GetProperties(mas)
    message(STATUS "Fetching Properties MKF")
    FetchContent_GetProperties(MKF)
    message(STATUS "MAS_POPULATED: ${MAS_POPULATED}")
    message(STATUS "MKF_POPULATED: ${MKF_POPULATED}")

    # Always delete and repopulate MAS to ensure we get the latest
    if(EXISTS ${CMAKE_BINARY_DIR}/_deps/mas-src)
        message(STATUS "Removing old MAS source to force fresh clone")
        file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/_deps/mas-src)
    endif()
    message(STATUS "Populating MAS")
    FetchContent_Populate(mas)
    # Show git log to verify version
    execute_process(
        COMMAND git -C ${mas_SOURCE_DIR} log --oneline -1
        OUTPUT_VARIABLE MAS_GIT_LOG
        OUTPUT_STRIP_TRAILING_WHITESPACE
        ERROR_VARIABLE MAS_GIT_ERROR
    )
    message(STATUS "MAS version: ${MAS_GIT_LOG}")
    if(MAS_GIT_ERROR)
        message(STATUS "MAS git error: ${MAS_GIT_ERROR}")
    endif()

    if(NOT MKF_POPULATED)
        message(STATUS "Populating MKF")
        FetchContent_Populate(MKF)
    else()
        message(STATUS "MKF already populated at: ${MKF_SOURCE_DIR}")
    endif()

    message(STATUS ${MAS_SOURCE_DIR})
else()
    message(STATUS "Using local MKF at ${MKF_DIR}")
    message(STATUS "Using local MAS at ${MAS_DIR}")
endif()

message(STATUS "Compiling MAS")


add_custom_command(
  OUTPUT "${MAS_DIRECTORY}/MAS.hpp"
  # Force regeneration by removing output first
  COMMAND ${CMAKE_COMMAND} -E remove -f ${MAS_DIRECTORY}/MAS.hpp
  COMMAND quicktype -l c++ -s schema ${MAS_DIR}/schemas/MAS.json 
    -S ${MAS_DIR}/schemas/magnetic.json
    -S ${MAS_DIR}/schemas/magnetic/core.json
    -S ${MAS_DIR}/schemas/magnetic/coil.json
    -S ${MAS_DIR}/schemas/utils.json
    -S ${MAS_DIR}/schemas/magnetic/core/gap.json
    -S ${MAS_DIR}/schemas/magnetic/core/shape.json
    -S ${MAS_DIR}/schemas/magnetic/core/material.json
    -S ${MAS_DIR}/schemas/magnetic/insulation/material.json
    -S ${MAS_DIR}/schemas/magnetic/insulation/wireCoating.json
    -S ${MAS_DIR}/schemas/magnetic/bobbin.json
    -S ${MAS_DIR}/schemas/magnetic/core/piece.json
    -S ${MAS_DIR}/schemas/magnetic/core/spacer.json
    -S ${MAS_DIR}/schemas/magnetic/wire/basicWire.json
    -S ${MAS_DIR}/schemas/magnetic/wire/round.json
    -S ${MAS_DIR}/schemas/magnetic/wire/rectangular.json
    -S ${MAS_DIR}/schemas/magnetic/wire/foil.json
    -S ${MAS_DIR}/schemas/magnetic/wire/planar.json
    -S ${MAS_DIR}/schemas/magnetic/wire/litz.json
    -S ${MAS_DIR}/schemas/magnetic/wire/material.json
    -S ${MAS_DIR}/schemas/magnetic/wire.json
    -S ${MAS_DIR}/schemas/utils.json
    -S ${MAS_DIR}/schemas/magnetic/insulation/wireCoating.json
    -S ${MAS_DIR}/schemas/magnetic/insulation/material.json
    -S ${MAS_DIR}/schemas/inputs.json
    -S ${MAS_DIR}/schemas/outputs.json
    -S ${MAS_DIR}/schemas/outputs/coreLossesOutput.json
    -S ${MAS_DIR}/schemas/inputs/designRequirements.json
    -S ${MAS_DIR}/schemas/inputs/operatingConditions.json
    -S ${MAS_DIR}/schemas/inputs/operatingPoint.json
    -S ${MAS_DIR}/schemas/inputs/operatingPointExcitation.json
    -S ${MAS_DIR}/schemas/inputs/topologies/flyback.json
    -S ${MAS_DIR}/schemas/inputs/topologies/currentTransformer.json
    -S ${MAS_DIR}/schemas/inputs/topologies/boost.json
    -S ${MAS_DIR}/schemas/inputs/topologies/buck.json
    -S ${MAS_DIR}/schemas/inputs/topologies/flybuck.json
    -S ${MAS_DIR}/schemas/inputs/topologies/forward.json
    -S ${MAS_DIR}/schemas/inputs/topologies/isolatedBuck.json
    -S ${MAS_DIR}/schemas/inputs/topologies/isolatedBuckBoost.json
    -S ${MAS_DIR}/schemas/inputs/topologies/pushPull.json
    -S ${MAS_DIR}/schemas/inputs/topologies/dualActiveBridge.json
    -S ${MAS_DIR}/schemas/inputs/topologies/llcResonant.json
    -S ${MAS_DIR}/schemas/inputs/topologies/cllcResonant.json
    -S ${MAS_DIR}/schemas/inputs/topologies/phaseShiftFullBridge.json
    -o ${MAS_DIRECTORY}/MAS.hpp --namespace MAS --source-style single-source --type-style pascal-case --member-style underscore-case --enumerator-style upper-underscore-case --no-boost
  USES_TERMINAL)

add_custom_target(PyMASGeneration
                  /bin/echo "RUNNING PyMASGeneration"
                  DEPENDS "${MAS_DIRECTORY}/MAS.hpp")

message(STATUS "Compiling PyOpenMagnetics with modular structure")
file(GLOB SOURCES src/*.cpp
    ${MKF_DIR}/src/*.cpp
    ${MKF_DIR}/src/advisers/*.cpp
    ${MKF_DIR}/src/constructive_models/*.cpp
    ${MKF_DIR}/src/converter_models/*.cpp
    ${MKF_DIR}/src/physical_models/*.cpp
    ${MKF_DIR}/src/processors/*.cpp
    ${MKF_DIR}/src/support/*.cpp
    )

set(CCI_GENERATED_CPP "${CMAKE_BINARY_DIR}/generated/CciCoordinatesData.cpp")
find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_custom_command(
    OUTPUT "${CCI_GENERATED_CPP}"
    COMMAND "${Python3_EXECUTABLE}"
            "${MKF_DIR}/scripts/generate_cci_data.py"
            "${MKF_DIR}/cci_coords"
            "${CCI_GENERATED_CPP}"
    DEPENDS "${MKF_DIR}/scripts/generate_cci_data.py"
    COMMENT "Generating embedded CCI coordinates"
    VERBATIM)
add_custom_target(cci_data_gen DEPENDS "${CCI_GENERATED_CPP}")
list(APPEND SOURCES "${CCI_GENERATED_CPP}")

message(STATUS SOURCES)
message(STATUS ${SOURCES})
pybind11_add_module(PyOpenMagnetics ${SOURCES})

add_dependencies(PyOpenMagnetics PyMASGeneration cci_data_gen)

target_link_libraries(PyOpenMagnetics PUBLIC nlohmann_json::nlohmann_json levmar rapidfuzz::rapidfuzz)

file(DOWNLOAD "https://raw.githubusercontent.com/vector-of-bool/cmrc/master/CMakeRC.cmake"
                 "${CMAKE_BINARY_DIR}/CMakeRC.cmake")
include("${CMAKE_BINARY_DIR}/CMakeRC.cmake")

include_directories("${MKF_DIR}/")

cmrc_add_resource_library(insulation_standards ALIAS data::insulation_standards NAMESPACE insulationData WHENCE ${MKF_DIR}/ ${MKF_DIR}/src/data/insulation_standards/IEC_60664-1.json ${MKF_DIR}/src/data/insulation_standards/IEC_60664-4.json ${MKF_DIR}/src/data/insulation_standards/IEC_60664-5.json ${MKF_DIR}/src/data/insulation_standards/IEC_62368-1.json ${MKF_DIR}/src/data/insulation_standards/IEC_61558-1.json ${MKF_DIR}/src/data/insulation_standards/IEC_61558-2-16.json ${MKF_DIR}/src/data/insulation_standards/IEC_60335-1.json)
target_link_libraries(PyOpenMagnetics PUBLIC data::insulation_standards)


# Only embed essential data files to reduce binary size
# Skip large optional datasets like cores_stock.ndjson
cmrc_add_resource_library(data ALIAS data::data NAMESPACE data WHENCE ${MAS_DIR} PREFIX MAS 
    ${MAS_DIR}/data/core_materials.ndjson 
    ${MAS_DIR}/data/core_shapes.ndjson 
    ${MAS_DIR}/data/cores.ndjson 
    ${MAS_DIR}/data/bobbins.ndjson 
    ${MAS_DIR}/data/insulation_materials.ndjson 
    ${MAS_DIR}/data/wire_materials.ndjson 
    ${MAS_DIR}/data/wires.ndjson)
target_link_libraries(PyOpenMagnetics PUBLIC data::data)

cmrc_add_resource_library(core_losses_data ALIAS data::core_losses_data NAMESPACE coreLossesData ${MKF_DIR}/src/data/core_losses/ciGSE_coefficients.json)
target_link_libraries(PyOpenMagnetics PUBLIC data::core_losses_data)

include_directories("${CMAKE_BINARY_DIR}/_deps/json-src/include/nlohmann/")
include_directories("${CMAKE_BINARY_DIR}/_deps/pybind11-src/include/")
include_directories("${CMAKE_BINARY_DIR}/_deps/pybind11_json-src/include/pybind11_json/")
include_directories("${CMAKE_BINARY_DIR}/_deps/json-src/include/nlohmann/")
include_directories("${CMAKE_BINARY_DIR}/_deps/magic-enum-src/include")
include_directories("${CMAKE_BINARY_DIR}/_deps/svg-src/src")
include_directories("${CMAKE_BINARY_DIR}/_deps/spline-src/src")
include_directories("${CMAKE_BINARY_DIR}/_deps/json-src/include/")
include_directories("${MKF_DIR}/src/")
include_directories("${MKF_DIR}/src/advisers/")
include_directories("${MKF_DIR}/src/constructive_models/")
include_directories("${MKF_DIR}/src/converter_models/")
include_directories("${MKF_DIR}/src/physical_models/")
include_directories("${MKF_DIR}/src/processors/")
include_directories("${MKF_DIR}/src/support/")
include_directories("${CMAKE_BINARY_DIR}/_deps/eigen-src")
include_directories("${CMAKE_BINARY_DIR}/_cmrc/include")
include_directories("${MAS_DIRECTORY}")
include_directories("src/")

# target_link_libraries(PyOpenMagnetics PUBLIC MKF)



install(TARGETS PyOpenMagnetics LIBRARY DESTINATION .)

# Install documentation files for AI assistants
install(FILES AGENTS.md llms.txt PyOpenMagnetics.pyi DESTINATION .)