cmake_minimum_required(VERSION 3.22)

project(DeepPluck VERSION 0.13.0 LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
option(DEEPPLUCK_PROFILE_SYMBOLS "Separate diagnostic Release build with PDBs and offline phase markers" OFF)

# JUCE's generated Windows resource file is not rebuilt when only its version
# metadata changes. Remove the build-local generated copies during configure so
# --fresh always produces binaries whose FileVersion matches PROJECT_VERSION.
if(WIN32)
    file(REMOVE
        "${CMAKE_CURRENT_BINARY_DIR}/DeepPluck_artefacts/JuceLibraryCode/DeepPluck_resources.rc"
        "${CMAKE_CURRENT_BINARY_DIR}/DeepPluckTests_artefacts/JuceLibraryCode/DeepPluckTests_resources.rc"
        "${CMAKE_CURRENT_BINARY_DIR}/DeepPluckToneAudit_artefacts/JuceLibraryCode/DeepPluckToneAudit_resources.rc"
        "${CMAKE_CURRENT_BINARY_DIR}/DeepPluckBenchmark_artefacts/JuceLibraryCode/DeepPluckBenchmark_resources.rc"
        "${CMAKE_CURRENT_BINARY_DIR}/DeepPluckPresetAudit_artefacts/JuceLibraryCode/DeepPluckPresetAudit_resources.rc"
        "${CMAKE_CURRENT_BINARY_DIR}/DeepPluckPresetGenerator_artefacts/JuceLibraryCode/DeepPluckPresetGenerator_resources.rc"
        "${CMAKE_CURRENT_BINARY_DIR}/DeepPluckPresetAudition_artefacts/JuceLibraryCode/DeepPluckPresetAudition_resources.rc")
endif()

add_subdirectory(JUCE)
include(CMake/FactoryContent.cmake)

juce_add_plugin(DeepPluck
    VERSION ${PROJECT_VERSION}
    COMPANY_NAME "ToneTrax"
    BUNDLE_ID "de.tonetrax.deeppluck"
    PLUGIN_MANUFACTURER_CODE Tntx
    PLUGIN_CODE DpV1
    PRODUCT_NAME "DeepPluck"
    PLUGIN_NAME "DeepPluck"
    DESCRIPTION "DeepPluck parameter compatibility freeze"
    FORMATS VST3 Standalone
    VST3_CATEGORIES Instrument Synth
    IS_SYNTH TRUE
    NEEDS_MIDI_INPUT TRUE
    NEEDS_MIDI_OUTPUT FALSE
    IS_MIDI_EFFECT FALSE
    EDITOR_WANTS_KEYBOARD_FOCUS FALSE
    COPY_PLUGIN_AFTER_BUILD FALSE)

get_target_property(DEEPPLUCK_JUCE_TARGET_VERSION DeepPluck JUCE_VERSION)
if(NOT DEEPPLUCK_JUCE_TARGET_VERSION STREQUAL PROJECT_VERSION)
    message(FATAL_ERROR
        "DeepPluck JUCE_VERSION (${DEEPPLUCK_JUCE_TARGET_VERSION}) must match PROJECT_VERSION (${PROJECT_VERSION})")
endif()

add_custom_target(DeepPluck_PreBuildVersionCheck ALL
    COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass
            -File "${CMAKE_SOURCE_DIR}/Tests/VerifyGeneratedVersion.ps1"
            -ExpectedVersion "${PROJECT_VERSION}"
            -BuildDir "${CMAKE_BINARY_DIR}"
    COMMENT "Verifying generated DeepPluck target versions before compilation"
    VERBATIM)
add_dependencies(DeepPluck_PreBuildVersionCheck DeepPluck_rc_lib)
add_dependencies(DeepPluck DeepPluck_PreBuildVersionCheck)

target_sources(DeepPluck
    PRIVATE
        Source/PluginEditor.cpp
        Source/PluginEditor.h
        Source/GUI/DeepPluckEditorComponents.cpp
        Source/GUI/DeepPluckEditorComponents.h
        Source/GUI/DeepPluckGuiSupport.cpp
        Source/GUI/DeepPluckGuiSupport.h
        Source/GUI/DeepPluckStandaloneKeyboard.cpp
        Source/GUI/DeepPluckStandaloneKeyboard.h
        Source/PluginProcessor.cpp
        Source/PluginProcessor.h
        Source/Metering/DeepPluckOutputMeter.cpp
        Source/Metering/DeepPluckOutputMeter.h
        Source/Effects/DeepPluckChorus.cpp
        Source/Effects/DeepPluckChorus.h
        Source/Effects/DeepPluckEffectMix.h
        Source/Effects/DeepPluckDelay.cpp
        Source/Effects/DeepPluckDelay.h
        Source/Effects/DeepPluckReverb.cpp
        Source/Effects/DeepPluckReverb.h
        Source/Effects/DeepPluckEffectsRack.cpp
        Source/Effects/DeepPluckEffectsRack.h
        Source/Parameters/DeepPluckParameters.cpp
        Source/Parameters/DeepPluckParameters.h
        Source/Parameters/DeepPluckParameterState.cpp
        Source/Parameters/DeepPluckParameterState.h
        Source/Presets/DeepPluckPresets.cpp
        Source/Presets/DeepPluckPresets.h
        Source/Presets/DeepPluckPresetManager.cpp
        Source/Presets/DeepPluckPresetManager.h
        Source/Presets/DeepPluckPresetCodec.cpp
        Source/Presets/DeepPluckPresetCodec.h
        Source/Presets/DeepPluckFactoryCategories.h
        Source/Presets/DeepPluckFactoryLibrary.h
        Source/Presets/DeepPluckFactoryLibrary.cpp
        Source/Randomization/DeepPluckRandomization.cpp
        Source/Randomization/DeepPluckRandomization.h
        Source/Synth/DeepPluckEnvelope.cpp
        Source/Synth/DeepPluckEnvelope.h
        Source/Synth/DeepPluckFilter.cpp
        Source/Synth/DeepPluckFilter.h
        Source/Synth/DeepPluckLfo.cpp
        Source/Synth/DeepPluckLfo.h
        Source/Synth/DeepPluckOscillator.cpp
        Source/Synth/DeepPluckOscillator.h
        Source/Synth/DeepPluckSound.h
        Source/Synth/DeepPluckSynthesiser.cpp
        Source/Synth/DeepPluckSynthesiser.h
        Source/Synth/DeepPluckTransientNoise.cpp
        Source/Synth/DeepPluckTransientNoise.h
        Source/Synth/DeepPluckUnison.cpp
        Source/Synth/DeepPluckUnison.h
        Source/Synth/DeepPluckVoice.cpp
        Source/Synth/DeepPluckVoice.h)

target_compile_features(DeepPluck PRIVATE cxx_std_20)

target_compile_definitions(DeepPluck
    PUBLIC
        JUCE_WEB_BROWSER=0
        JUCE_USE_CURL=0
        JUCE_VST3_CAN_REPLACE_VST2=0)

target_link_libraries(DeepPluck
    PRIVATE
        juce::juce_audio_utils
        juce::juce_dsp
        juce::juce_cryptography
        DeepPluckFactoryData
    PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_warning_flags)

enable_testing()

add_test(NAME DeepPluck.evidence_paths
    COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass
        -File "${CMAKE_SOURCE_DIR}/Tests/TestEvidencePaths.ps1"
        -SourceDir "${CMAKE_SOURCE_DIR}"
        -OutputRoot "${CMAKE_BINARY_DIR}/TestData/EvidencePaths")

juce_add_console_app(DeepPluckTests VERSION ${PROJECT_VERSION} PRODUCT_NAME "DeepPluckTests")

target_sources(DeepPluckTests PRIVATE Tests/TestMain.cpp)
target_compile_features(DeepPluckTests PRIVATE cxx_std_20)
target_include_directories(DeepPluckTests PRIVATE Source)
target_compile_definitions(DeepPluckTests PRIVATE
    JUCE_WEB_BROWSER=0
    JUCE_USE_CURL=0
    JUCE_VST3_CAN_REPLACE_VST2=0
    DEEPPLUCK_EXPECTED_VERSION="${PROJECT_VERSION}"
    DEEPPLUCK_TEST_ROOT="${CMAKE_BINARY_DIR}/TestData")
target_link_libraries(DeepPluckTests
    PRIVATE
        DeepPluck
        juce::juce_audio_utils
        juce::juce_dsp
        juce::juce_recommended_config_flags
        juce::juce_recommended_warning_flags)

# JUCE exposes the plugin shared-code objects to dependent targets. Serialise the
# test link after both wrappers so parallel MSBuild cannot read an object while a
# sibling wrapper target is still completing its dependency graph.
add_dependencies(DeepPluckTests DeepPluck_VST3 DeepPluck_Standalone)

# Offline evidence adapter: links the actual plugin, never a reconstructed DSP.
# Run once and preserve the executable before changing the reference sound.
juce_add_console_app(DeepPluckToneAudit VERSION ${PROJECT_VERSION} PRODUCT_NAME "DeepPluckToneAudit")
target_sources(DeepPluckToneAudit PRIVATE Tests/ToneVoicingAudit.cpp)
target_include_directories(DeepPluckToneAudit PRIVATE Source)
target_compile_definitions(DeepPluckToneAudit PRIVATE
    JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0 JUCE_VST3_CAN_REPLACE_VST2=0
    DEEPPLUCK_AUDIT_VERSION="${PROJECT_VERSION}")
target_link_libraries(DeepPluckToneAudit PRIVATE DeepPluck juce::juce_audio_utils
    juce::juce_dsp juce::juce_recommended_config_flags juce::juce_recommended_warning_flags)
add_dependencies(DeepPluckToneAudit DeepPluckTests)
add_test(NAME DeepPluck.tone_voicing
    COMMAND DeepPluckToneAudit "${CMAKE_BINARY_DIR}/TestData/ToneVoicing/$<CONFIG>")

# Offline CPU/audible-equivalence evidence links the unmodified production path.
# Reference builds keep the starting PROJECT_VERSION until baseline/profile G1.
juce_add_console_app(DeepPluckBenchmark VERSION ${PROJECT_VERSION} PRODUCT_NAME "DeepPluckBenchmark")
target_sources(DeepPluckBenchmark PRIVATE
    Tests/Benchmark/Main.cpp Tests/Benchmark/Manifest.cpp
    Tests/Benchmark/Runner.cpp Tests/Benchmark/Comparison.cpp Tests/Benchmark/Support.cpp
    Tests/Benchmark/Statistics.cpp)
target_include_directories(DeepPluckBenchmark PRIVATE Source)
target_compile_definitions(DeepPluckBenchmark PRIVATE
    JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0 JUCE_VST3_CAN_REPLACE_VST2=0
    DEEPPLUCK_BENCHMARK_VERSION="${PROJECT_VERSION}"
    DEEPPLUCK_BENCHMARK_CONFIG="$<CONFIG>")
target_link_libraries(DeepPluckBenchmark PRIVATE DeepPluck juce::juce_audio_utils
    juce::juce_dsp juce::juce_recommended_config_flags juce::juce_recommended_warning_flags)
add_dependencies(DeepPluckBenchmark DeepPluckToneAudit)

# Preserve this adapter against the current DSP before V0.12.0 content changes.
juce_add_console_app(DeepPluckPresetAudit VERSION ${PROJECT_VERSION} PRODUCT_NAME "DeepPluckPresetAudit")
target_sources(DeepPluckPresetAudit PRIVATE Tools/Presets/PresetAuditMain.cpp)
target_include_directories(DeepPluckPresetAudit PRIVATE Source)
target_compile_definitions(DeepPluckPresetAudit PRIVATE
    JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0 JUCE_VST3_CAN_REPLACE_VST2=0
    DEEPPLUCK_PRESET_AUDIT_VERSION="${PROJECT_VERSION}"
    DEEPPLUCK_PRESET_AUDIT_CONFIG="$<CONFIG>")
target_link_libraries(DeepPluckPresetAudit PRIVATE DeepPluck juce::juce_audio_utils
    juce::juce_dsp juce::juce_recommended_config_flags juce::juce_recommended_warning_flags)
add_dependencies(DeepPluckPresetAudit DeepPluckBenchmark)

# Internal content generation only; never included in either end-user wrapper.
juce_add_console_app(DeepPluckPresetGenerator VERSION ${PROJECT_VERSION} PRODUCT_NAME "DeepPluckPresetGenerator")
target_sources(DeepPluckPresetGenerator PRIVATE Tools/Presets/GeneratorMain.cpp
    Tools/Presets/CategoryProfiles.cpp Tools/Presets/CandidateFiles.cpp Tools/Presets/GeneratorTests.cpp)
target_include_directories(DeepPluckPresetGenerator PRIVATE Source Tools/Presets)
target_compile_definitions(DeepPluckPresetGenerator PRIVATE
    JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0 JUCE_VST3_CAN_REPLACE_VST2=0
    DEEPPLUCK_PRESET_TOOL_VERSION="${PROJECT_VERSION}"
    DEEPPLUCK_FACTORY_SOURCE_ROOT="${CMAKE_SOURCE_DIR}/Presets/Factory"
    DEEPPLUCK_CANDIDATE_OUTPUT_ROOT="${CMAKE_SOURCE_DIR}/Artifacts/V0.12.0"
    DEEPPLUCK_CANDIDATE_TEST_ROOT="${CMAKE_BINARY_DIR}/TestData/FactoryPresets")
target_link_libraries(DeepPluckPresetGenerator PRIVATE DeepPluck juce::juce_audio_utils
    juce::juce_dsp juce::juce_cryptography
    juce::juce_recommended_config_flags juce::juce_recommended_warning_flags)
add_dependencies(DeepPluckPresetGenerator DeepPluckPresetAudit)

juce_add_gui_app(DeepPluckPresetAudition VERSION ${PROJECT_VERSION} PRODUCT_NAME "DeepPluckPresetAudition")
target_sources(DeepPluckPresetAudition PRIVATE Tools/Presets/AuditionMain.cpp
    Tools/Presets/CategoryProfiles.cpp Tools/Presets/CandidateFiles.cpp)
target_include_directories(DeepPluckPresetAudition PRIVATE Source Tools/Presets)
target_compile_definitions(DeepPluckPresetAudition PRIVATE
    JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0 JUCE_VST3_CAN_REPLACE_VST2=0
    DEEPPLUCK_PRESET_TOOL_VERSION="${PROJECT_VERSION}"
    DEEPPLUCK_CANDIDATE_OUTPUT_ROOT="${CMAKE_SOURCE_DIR}/Artifacts/V0.12.0"
    DEEPPLUCK_CANDIDATE_TEST_ROOT="${CMAKE_BINARY_DIR}/TestData/FactoryPresets")
target_link_libraries(DeepPluckPresetAudition PRIVATE DeepPluck juce::juce_audio_utils
    juce::juce_dsp juce::juce_cryptography
    juce::juce_recommended_config_flags juce::juce_recommended_warning_flags)
add_dependencies(DeepPluckPresetAudition DeepPluckPresetGenerator)

add_custom_target(DeepPluck_FactoryContentCheck ALL
    COMMAND "$<TARGET_FILE:DeepPluckPresetGenerator>" factory-check
            --input "${CMAKE_SOURCE_DIR}/Presets/Factory" --require-final no
    DEPENDS DeepPluckPresetGenerator
    COMMENT "Validating explicitly catalogued Factory content and approval hashes"
    VERBATIM)
foreach(DEEPPLUCK_FACTORY_SUITE IN ITEMS profiles files factory integration)
    add_test(NAME "DeepPluck.factory_${DEEPPLUCK_FACTORY_SUITE}"
        COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass
            -File "${CMAKE_SOURCE_DIR}/Tests/TestFactoryPresets.ps1"
            -Executable "$<TARGET_FILE:DeepPluckPresetGenerator>"
            -Suite "${DEEPPLUCK_FACTORY_SUITE}"
            -OutputRoot "${CMAKE_BINARY_DIR}/TestData/FactoryPresets/$<CONFIG>"
            -SourceDir "${CMAKE_SOURCE_DIR}")
    set_tests_properties("DeepPluck.factory_${DEEPPLUCK_FACTORY_SUITE}" PROPERTIES TIMEOUT 600)
endforeach()
if(DEEPPLUCK_FACTORY_STATUS STREQUAL "approved")
    add_test(NAME DeepPluck.factory_final_content
        COMMAND DeepPluckPresetGenerator factory-check --input "${CMAKE_SOURCE_DIR}/Presets/Factory" --require-final yes)
endif()

if(DEEPPLUCK_PROFILE_SYMBOLS AND MSVC)
    # Separate build tree only. No FP/ISA/optimisation-quality flag changes, and
    # never use this instrumented executable for performance acceptance.
    target_compile_options(DeepPluck PRIVATE "$<$<CONFIG:Release>:/Zi>")
    target_compile_options(DeepPluckBenchmark PRIVATE "$<$<CONFIG:Release>:/Zi>")
    target_link_options(DeepPluckBenchmark PRIVATE "$<$<CONFIG:Release>:/DEBUG:FULL>"
        "$<$<CONFIG:Release>:/OPT:REF>" "$<$<CONFIG:Release>:/OPT:ICF>")
    target_compile_definitions(DeepPluckBenchmark PRIVATE DEEPPLUCK_PROFILE_MARKERS=1)
endif()
add_dependencies(DeepPluck_PreBuildVersionCheck DeepPluckTests_rc_lib
    DeepPluckToneAudit_rc_lib DeepPluckBenchmark_rc_lib
    DeepPluckPresetAudit_rc_lib DeepPluckPresetGenerator_rc_lib DeepPluckPresetAudition_rc_lib)

set(DEEPPLUCK_BENCHMARK_REFERENCE "" CACHE PATH "Preserved external CPU audio reference root (Debug/Release subfolders)")
foreach(DEEPPLUCK_BENCHMARK_TEST IN ITEMS manifest determinism audio-equivalence quick)
    string(REPLACE "-" "_" DEEPPLUCK_BENCHMARK_TEST_NAME "${DEEPPLUCK_BENCHMARK_TEST}")
    if(NOT DEEPPLUCK_BENCHMARK_TEST STREQUAL "audio-equivalence")
        set(DEEPPLUCK_BENCHMARK_TEST_NAME "benchmark_${DEEPPLUCK_BENCHMARK_TEST_NAME}")
    endif()
    add_test(NAME "DeepPluck.${DEEPPLUCK_BENCHMARK_TEST_NAME}"
        COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass
            -File "${CMAKE_SOURCE_DIR}/Tests/TestCPUBenchmark.ps1"
            -Executable "$<TARGET_FILE:DeepPluckBenchmark>"
            -Test "${DEEPPLUCK_BENCHMARK_TEST}"
            -OutputRoot "${CMAKE_BINARY_DIR}/TestData/CPUBenchmark/$<CONFIG>"
            -ReferenceRoot "${DEEPPLUCK_BENCHMARK_REFERENCE}"
            -Configuration "$<CONFIG>")
endforeach()

foreach(DEEPPLUCK_TEST_SUITE IN ITEMS
        parameters oscillator envelope filter filter_envelope transient_noise
        transient_noise_integration oscillator2 oscillator2_integration
        unison unison_integration headroom performance synthesiser sample_accuracy
        matrix gui standalone_keyboard smoothing_stability modulation modulation_integration
        waveform waveform_integration effects effects_integration
        randomization preset_files preset_integration)
    add_test(NAME "DeepPluck.${DEEPPLUCK_TEST_SUITE}"
             COMMAND DeepPluckTests "${DEEPPLUCK_TEST_SUITE}")
endforeach()

add_test(NAME DeepPluck.artifact_version
    COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass
            -File "${CMAKE_SOURCE_DIR}/Tests/VerifyArtifacts.ps1"
            -ExpectedVersion "${PROJECT_VERSION}"
            -SourceDir "${CMAKE_SOURCE_DIR}"
            -BuildDir "${CMAKE_BINARY_DIR}"
            -Configuration "$<CONFIG>")

# Keep distributable development artifacts inside the project. JUCE's own
# COPY_PLUGIN_AFTER_BUILD step remains disabled, so no system location is used.
set(DEEPPLUCK_LOCAL_ARTIFACT_DIR "${CMAKE_SOURCE_DIR}/Artifacts/$<CONFIG>")
get_target_property(DEEPPLUCK_VST3_SOURCE DeepPluck_VST3 JUCE_PLUGIN_ARTEFACT_FILE)

add_custom_target(DeepPluck_VST3_LocalArtifact ALL
    COMMAND "${CMAKE_COMMAND}" -E make_directory "${DEEPPLUCK_LOCAL_ARTIFACT_DIR}"
    COMMAND "${CMAKE_COMMAND}" -E rm -rf "${DEEPPLUCK_LOCAL_ARTIFACT_DIR}/DeepPluck.vst3"
    COMMAND "${CMAKE_COMMAND}" -E copy_directory
            "${DEEPPLUCK_VST3_SOURCE}"
            "${DEEPPLUCK_LOCAL_ARTIFACT_DIR}/DeepPluck.vst3"
    DEPENDS DeepPluck_VST3
    COMMENT "Copying the complete DeepPluck VST3 bundle to the local Artifacts directory"
    VERBATIM)

add_custom_target(DeepPluck_Standalone_LocalArtifact ALL
    COMMAND "${CMAKE_COMMAND}" -E make_directory "${DEEPPLUCK_LOCAL_ARTIFACT_DIR}"
    COMMAND "${CMAKE_COMMAND}" -E rm -f "${DEEPPLUCK_LOCAL_ARTIFACT_DIR}/DeepPluck.exe"
    COMMAND "${CMAKE_COMMAND}" -E copy_if_different
            "$<TARGET_FILE:DeepPluck_Standalone>"
            "${DEEPPLUCK_LOCAL_ARTIFACT_DIR}/DeepPluck.exe"
    DEPENDS DeepPluck_Standalone
    COMMENT "Copying the DeepPluck standalone application to the local Artifacts directory"
    VERBATIM)
