cmake_minimum_required(VERSION 3.22)
project(SAK_TUBE VERSION 0.1.1 LANGUAGES C CXX)

option(SAK_BUILD_PLUGIN "Build the JUCE VST3 and standalone application" ON)
option(SAK_BUILD_TESTS "Build circuit validation harness" ON)
set(SAK_JUCE_DIR "C:/JUCE" CACHE PATH "Path to JUCE source tree (tested with 8.0.6)")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_library(sak_dsp STATIC Source/DSP/Amplifier.cpp)
target_include_directories(sak_dsp PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source")
set_target_properties(sak_dsp PROPERTIES POSITION_INDEPENDENT_CODE ON)
if(MSVC)
    target_compile_options(sak_dsp PRIVATE /W4 /permissive- /fp:precise)
else()
    target_compile_options(sak_dsp PRIVATE -Wall -Wextra -Wpedantic)
endif()

if(SAK_BUILD_TESTS)
    enable_testing()
    foreach(pair IN ITEMS
        "sak_validate|validation" "sak_compensation_validate|compensation_validation"
        "sak_compensation_realtime|compensation_realtime" "sak_gain_validate|gain_staging_validation"
        "sak_gain_network|gain_network_validation" "sak_gain_extreme|gain_extreme_validation"
        "sak_resampling_tests|resampling_validation" "sak_feedback_tests|feedback_validation"
        "sak_core_validate|core_validation" "sak_core_projection_tests|core_projection_validation"
        "sak_system_reference|system_reference_validation" "sak_tube_reference|tube_reference_validation"
        "sak_hf_response|hf_response_validation")
        string(REPLACE "|" ";" fields "${pair}")
        list(GET fields 0 target)
        list(GET fields 1 source)
        add_executable(${target} Tests/${source}.cpp)
        target_link_libraries(${target} PRIVATE sak_dsp)
    endforeach()
    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
        Source/DSP/Amplifier.cpp Source/DSP/GainStaging.h Source/DSP/MagneticCore.h)
    file(SHA256 "${CMAKE_CURRENT_SOURCE_DIR}/Source/DSP/Amplifier.cpp" DSP_HASH)
    file(SHA256 "${CMAKE_CURRENT_SOURCE_DIR}/Source/DSP/GainStaging.h" GAIN_HASH)
    file(SHA256 "${CMAKE_CURRENT_SOURCE_DIR}/Source/DSP/MagneticCore.h" CORE_HASH)
    foreach(target IN ITEMS sak_gain_network sak_gain_extreme)
        target_compile_definitions(${target} PRIVATE SAK_GAIN_NETWORK_DSP_SHA256="${DSP_HASH}" SAK_GAIN_NETWORK_GAIN_SHA256="${GAIN_HASH}")
    endforeach()
    add_library(sak_core_fit_bridge SHARED EXCLUDE_FROM_ALL tools/core_fit_bridge.cpp)
    target_include_directories(sak_core_fit_bridge PRIVATE Source/DSP)
    target_compile_definitions(sak_core_fit_bridge PRIVATE SAK_CORE_HEADER_SHA256="${CORE_HASH}")
    add_test(NAME dsp_quick COMMAND sak_validate --quick)
    add_test(NAME resampling COMMAND sak_resampling_tests)
    add_test(NAME feedback_stability COMMAND sak_feedback_tests)
    add_test(NAME compensation_realtime COMMAND sak_compensation_realtime --report "${CMAKE_CURRENT_BINARY_DIR}/compensation_realtime_ctest")
    add_test(NAME musical_gain_extreme COMMAND sak_gain_extreme --report "${CMAKE_CURRENT_BINARY_DIR}/gain_extreme_ctest")
    add_test(NAME musical_gain_realtime COMMAND sak_gain_validate --only realtime --report "${CMAKE_CURRENT_BINARY_DIR}/gain_realtime_ctest")
    add_test(NAME musical_network COMMAND sak_gain_network --report "${CMAKE_CURRENT_BINARY_DIR}/gain_network_ctest")
    add_test(NAME magnetic_core_projection COMMAND sak_core_projection_tests)
    add_test(NAME magnetic_core_mechanisms COMMAND sak_core_validate --only core --report "${CMAKE_CURRENT_BINARY_DIR}/core_mechanisms_ctest")
    add_test(NAME magnetic_core_reference COMMAND sak_core_validate --only reference --report "${CMAKE_CURRENT_BINARY_DIR}/core_reference_ctest")
    add_test(NAME magnetic_core_bandwidth COMMAND sak_core_validate --only response --report "${CMAKE_CURRENT_BINARY_DIR}/core_bandwidth_ctest")
    add_test(NAME tube_reference_data COMMAND sak_tube_reference --reference "${CMAKE_CURRENT_SOURCE_DIR}/Tests/reference" --out "${CMAKE_CURRENT_BINARY_DIR}/tube_reference_ctest")
    add_test(NAME hf_response COMMAND sak_hf_response --out "${CMAKE_CURRENT_BINARY_DIR}/hf_ctest" --verify)
    add_test(NAME hf_response_rates COMMAND sak_hf_response --suite 1 --out "${CMAKE_CURRENT_BINARY_DIR}/hf_rates_ctest" --verify)
    # Immutable offline before snapshot, never linked into the product.
    set(HF_BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/../validation/hf-before")
    if(EXISTS "${HF_BEFORE}/source/DSP/Amplifier.cpp")
        add_executable(sak_hf_before EXCLUDE_FROM_ALL "${HF_BEFORE}/tests/hf_response_validation.cpp" "${HF_BEFORE}/source/DSP/Amplifier.cpp")
        target_include_directories(sak_hf_before PRIVATE "${HF_BEFORE}/source")
        add_executable(sak_hf_alias_before EXCLUDE_FROM_ALL "${HF_BEFORE}/tests/alias_comparison.cpp" "${HF_BEFORE}/source/DSP/Amplifier.cpp")
        target_include_directories(sak_hf_alias_before PRIVATE "${HF_BEFORE}/source")
        add_executable(sak_musical_before EXCLUDE_FROM_ALL "${HF_BEFORE}/tests/compensation_validation.cpp" "${HF_BEFORE}/source/DSP/Amplifier.cpp")
        target_include_directories(sak_musical_before PRIVATE "${HF_BEFORE}/source")
    endif()
endif()

if(SAK_BUILD_PLUGIN)
    if(NOT EXISTS "${SAK_JUCE_DIR}/CMakeLists.txt")
        message(FATAL_ERROR "Set SAK_JUCE_DIR to a JUCE source checkout, or SAK_BUILD_PLUGIN=OFF for the independent DSP harness.")
    endif()
    add_subdirectory("${SAK_JUCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/JUCE" EXCLUDE_FROM_ALL)
    juce_add_plugin(SAKTube
        COMPANY_NAME "SAK Audio"
        BUNDLE_ID "audio.sak.tube"
        PLUGIN_MANUFACTURER_CODE Saka
        PLUGIN_CODE Sakt
        PRODUCT_NAME "SAK-TUBE"
        DESCRIPTION "Interacting tube amplifier circuit"
        FORMATS VST3 Standalone
        IS_SYNTH FALSE
        NEEDS_MIDI_INPUT FALSE
        NEEDS_MIDI_OUTPUT FALSE
        IS_MIDI_EFFECT FALSE
        EDITOR_WANTS_KEYBOARD_FOCUS FALSE
        COPY_PLUGIN_AFTER_BUILD FALSE
        VST3_CATEGORIES Fx Distortion)
    juce_generate_juce_header(SAKTube)
    juce_add_binary_data(SAKTubeAssets SOURCES
        assets/rendered/fascia.png assets/rendered/knob.png
        assets/rendered/toggle_on.png assets/rendered/toggle_off.png
        assets/rendered/pilot_amber.png assets/rendered/pilot_red.png)
    target_sources(SAKTube PRIVATE
        Source/PluginProcessor.cpp Source/PluginProcessor.h
        Source/PluginEditor.cpp Source/PluginEditor.h Source/AudioEngine.h)
    target_compile_definitions(SAKTube PUBLIC
        JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0 JUCE_VST3_CAN_REPLACE_VST2=0)
    target_link_libraries(SAKTube PRIVATE sak_dsp juce::juce_audio_utils juce::juce_dsp
        PUBLIC SAKTubeAssets juce::juce_recommended_config_flags juce::juce_recommended_warning_flags)
    if(SAK_BUILD_TESTS)
        add_executable(sak_plugin_tests Tests/plugin_smoke.cpp)
        target_include_directories(sak_plugin_tests PRIVATE Source "${SAK_JUCE_DIR}/modules"
            "${CMAKE_CURRENT_BINARY_DIR}/SAKTube_artefacts/JuceLibraryCode")
        target_compile_definitions(sak_plugin_tests PRIVATE JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1)
        target_link_libraries(sak_plugin_tests PRIVATE SAKTube)
        add_test(NAME plugin_state_and_audio COMMAND sak_plugin_tests)
    endif()
endif()

if(SAK_BUILD_TESTS)
    add_executable(sak_hf_probes Tests/hf_probes.cpp)
    target_link_libraries(sak_hf_probes PRIVATE sak_dsp)
    if(EXISTS "${HF_BEFORE}/source/DSP/Amplifier.cpp")
        add_executable(sak_hf_probes_before EXCLUDE_FROM_ALL Tests/hf_probes.cpp "${HF_BEFORE}/source/DSP/Amplifier.cpp")
        target_compile_definitions(sak_hf_probes_before PRIVATE SAK_HF_BEFORE=1)
        target_include_directories(sak_hf_probes_before PRIVATE "${HF_BEFORE}/source")
    endif()
endif()
