cmake_minimum_required(VERSION 3.16)

project(skalacoin
    VERSION 0.1.0
    LANGUAGES C CXX
)

set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

find_package(Threads REQUIRED)
include(FetchContent)

# OpenSSL
find_package(OpenSSL QUIET)
if(NOT OpenSSL_FOUND)
    if(APPLE)
        execute_process(
            COMMAND brew --prefix openssl@3
            OUTPUT_VARIABLE HOMEBREW_OPENSSL_PREFIX
            OUTPUT_STRIP_TRAILING_WHITESPACE
            ERROR_QUIET
        )
        if(HOMEBREW_OPENSSL_PREFIX)
            set(OPENSSL_ROOT_DIR "${HOMEBREW_OPENSSL_PREFIX}")
        endif()
    endif()
    find_package(OpenSSL REQUIRED)
endif()

# libcurl (required by autolykos2 vendored code)
find_package(CURL QUIET)
if(NOT CURL_FOUND)
    message(STATUS "libcurl not found on system; attempting to fetch/build via FetchContent")
    FetchContent_Declare(
        curl
        GIT_REPOSITORY https://github.com/curl/curl.git
        GIT_TAG curl-8_4_0
        GIT_SHALLOW TRUE
    )
    # Try to make it available (this will add_subdirectory if curl provides CMake)
    FetchContent_MakeAvailable(curl)
endif()

# secp256k1 (Bitcoin Core library)
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
    pkg_check_modules(SECP256K1 QUIET IMPORTED_TARGET libsecp256k1)
endif()

# Try pkg-config / system first, then fall back to find_*(). If still not found
# attempt to fetch and build a bundled copy of libsecp256k1 using FetchContent.
if(NOT SECP256K1_FOUND)
    find_path(SECP256K1_INCLUDE_DIR NAMES secp256k1.h)
    find_library(SECP256K1_LIBRARY NAMES secp256k1)
    if(SECP256K1_INCLUDE_DIR AND SECP256K1_LIBRARY)
        set(SECP256K1_FOUND TRUE)
    endif()
endif()

if(NOT SECP256K1_FOUND)
    message(STATUS "secp256k1 not found on system; fetching and building a vendored copy")
    include(FetchContent)
    FetchContent_Declare(
        secp256k1
        GIT_REPOSITORY https://github.com/bitcoin-core/secp256k1.git
        GIT_TAG master
        GIT_SHALLOW TRUE
    )
    FetchContent_GetProperties(secp256k1)
    if(NOT secp256k1_POPULATED)
        FetchContent_Populate(secp256k1)
        # Prefer a CMake build if present
        if(EXISTS "${secp256k1_SOURCE_DIR}/CMakeLists.txt")
            add_subdirectory(${secp256k1_SOURCE_DIR} ${secp256k1_BINARY_DIR})
            if(TARGET secp256k1)
                set(SECP256K1_FOUND TRUE)
                set(SECP256K1_TARGET secp256k1)
            elseif(TARGET libsecp256k1)
                set(SECP256K1_FOUND TRUE)
                set(SECP256K1_TARGET libsecp256k1)
            endif()
        else()
            # Fall back to the autotools build path. Install into a private prefix
            set(SECP256K1_INSTALL_DIR "${CMAKE_BINARY_DIR}/_deps/secp256k1/install")
            file(MAKE_DIRECTORY ${SECP256K1_INSTALL_DIR})
            execute_process(COMMAND ./autogen.sh
                WORKING_DIRECTORY ${secp256k1_SOURCE_DIR}
                RESULT_VARIABLE _secp_autogen_result
                OUTPUT_QUIET ERROR_QUIET)
            if(NOT _secp_autogen_result EQUAL 0)
                message(FATAL_ERROR "Failed to run autogen.sh for secp256k1")
            endif()
            execute_process(COMMAND ./configure --enable-module-ecdh --enable-experimental --prefix=${SECP256K1_INSTALL_DIR}
                WORKING_DIRECTORY ${secp256k1_SOURCE_DIR}
                RESULT_VARIABLE _secp_configure_result
                OUTPUT_QUIET ERROR_QUIET)
            if(NOT _secp_configure_result EQUAL 0)
                message(FATAL_ERROR "Failed to configure secp256k1")
            endif()
            execute_process(COMMAND make
                WORKING_DIRECTORY ${secp256k1_SOURCE_DIR}
                RESULT_VARIABLE _secp_make_result
                OUTPUT_QUIET ERROR_QUIET)
            if(NOT _secp_make_result EQUAL 0)
                message(FATAL_ERROR "Failed to build secp256k1")
            endif()
            execute_process(COMMAND make install
                WORKING_DIRECTORY ${secp256k1_SOURCE_DIR}
                RESULT_VARIABLE _secp_make_install_result
                OUTPUT_QUIET ERROR_QUIET)
            set(SECP256K1_INCLUDE_DIR ${SECP256K1_INSTALL_DIR}/include)
            set(SECP256K1_LIBRARY ${SECP256K1_INSTALL_DIR}/lib/libsecp256k1.a)
            if(EXISTS ${SECP256K1_LIBRARY})
                set(SECP256K1_FOUND TRUE)
            endif()
        endif()
    endif()
endif()

# Autolykos2 CPU reference backend (optional)
option(SKALACOIN_ENABLE_AUTOLYKOS2_REF "Enable Autolykos2 CPU reference backend" ON)
set(SKALACOIN_AUTOLYKOS2_REF_AVAILABLE OFF)

if(SKALACOIN_ENABLE_AUTOLYKOS2_REF)
    FetchContent_Declare(
        autolykos2_ref_src
        GIT_REPOSITORY https://github.com/mhssamadani/Autolykos2_NV_Miner.git
        GIT_TAG main
        GIT_SHALLOW TRUE
    )
    FetchContent_MakeAvailable(autolykos2_ref_src)

    set(AUTOLYKOS2_REF_BASE ${autolykos2_ref_src_SOURCE_DIR}/secp256k1)
    set(AUTOLYKOS2_REF_SOURCES
        ${AUTOLYKOS2_REF_BASE}/src/cpuAutolykos.cc
        ${AUTOLYKOS2_REF_BASE}/src/conversion.cc
        ${AUTOLYKOS2_REF_BASE}/src/cryptography.cc
        ${AUTOLYKOS2_REF_BASE}/src/definitions.cc
        ${AUTOLYKOS2_REF_BASE}/src/easylogging++.cc
        ${AUTOLYKOS2_REF_BASE}/src/jsmn.c
        ${PROJECT_SOURCE_DIR}/src/autolykos2/easylogging_init.cpp
        ${PROJECT_SOURCE_DIR}/src/autolykos2/autolykos2_ref_wrapper.cpp
    )

    add_library(autolykos2_ref STATIC ${AUTOLYKOS2_REF_SOURCES})
    target_include_directories(autolykos2_ref PRIVATE ${AUTOLYKOS2_REF_BASE}/include)
    # Upstream source uses `malloc/free/exit/EXIT_FAILURE` without including
    # stdlib headers in some C++ translation units. AppleClang can compile this,
    # while Linux Clang fails. Force-include stdlib.h for C++ in this vendored lib.
    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
        target_compile_options(autolykos2_ref PRIVATE
            $<$<COMPILE_LANGUAGE:CXX>:-include>
            $<$<COMPILE_LANGUAGE:CXX>:stdlib.h>
        )
    elseif(MSVC)
        target_compile_options(autolykos2_ref PRIVATE
            $<$<COMPILE_LANGUAGE:CXX>:/FIstdlib.h>
        )
    endif()
    if(TARGET CURL::libcurl)
        target_link_libraries(autolykos2_ref PRIVATE
            ${CMAKE_THREAD_LIBS_INIT}
            OpenSSL::SSL
            OpenSSL::Crypto
            CURL::libcurl
        )
    elseif(DEFINED CURL_LIBRARIES AND CURL_LIBRARIES)
        target_link_libraries(autolykos2_ref PRIVATE
            ${CMAKE_THREAD_LIBS_INIT}
            OpenSSL::SSL
            OpenSSL::Crypto
            ${CURL_LIBRARIES}
        )
    else()
        message(FATAL_ERROR "autolykos2_ref requires libcurl (curl/curl.h). Install libcurl devel package or allow FetchContent to build it.")
    endif()
    set(SKALACOIN_AUTOLYKOS2_REF_AVAILABLE ON)
endif()

# ---------------------------------------------------------
# Output directories
# ---------------------------------------------------------
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

foreach(OUTPUTCONFIG DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
    string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/bin)
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib)
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib)
endforeach()

# Node
file(GLOB_RECURSE NODE_SRC CONFIGURE_DEPENDS src/*.c)
add_executable(node ${NODE_SRC})
target_link_libraries(node PRIVATE
    ${CMAKE_THREAD_LIBS_INIT}
    OpenSSL::SSL
    OpenSSL::Crypto
)

if(TARGET PkgConfig::SECP256K1)
    target_link_libraries(node PRIVATE PkgConfig::SECP256K1)
elseif(DEFINED SECP256K1_TARGET AND TARGET ${SECP256K1_TARGET})
    target_link_libraries(node PRIVATE ${SECP256K1_TARGET})
elseif(SECP256K1_FOUND AND SECP256K1_LIBRARY)
    target_include_directories(node PRIVATE ${SECP256K1_INCLUDE_DIR})
    target_link_libraries(node PRIVATE ${SECP256K1_LIBRARY})
else()
    message(FATAL_ERROR "secp256k1 not found and no vendored target available. Install libsecp256k1 or enable FetchContent builds.")
endif()

if(SKALACOIN_AUTOLYKOS2_REF_AVAILABLE)
    target_link_libraries(node PRIVATE autolykos2_ref)
endif()

target_include_directories(node PRIVATE 
    ${PROJECT_SOURCE_DIR}/include
)
target_compile_options(node PRIVATE
    -Wall
    -Wextra
    -Wpedantic
    -g
)
target_compile_definitions(node PRIVATE
    CHAIN_DATA_DIR="${CMAKE_BINARY_DIR}/chain_data"
    $<$<BOOL:${SKALACOIN_AUTOLYKOS2_REF_AVAILABLE}>:SKALACOIN_AUTOLYKOS2_REF_AVAILABLE>
    $<$<BOOL:1>:_POSIX_C_SOURCE=200809L>
)
set_target_properties(node PROPERTIES OUTPUT_NAME "skalacoin_node")
