cmake_minimum_required(VERSION 3.24)

project(video_shader LANGUAGES C CXX)

include(CTest)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
set(TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests")

set(LEGACY_THIRD_PARTY_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/3rdParty")
set(DEFAULT_THIRD_PARTY_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/video-io-3rdParty")
if(NOT EXISTS "${DEFAULT_THIRD_PARTY_ROOT}")
	set(DEFAULT_THIRD_PARTY_ROOT "${LEGACY_THIRD_PARTY_ROOT}")
endif()

set(THIRD_PARTY_ROOT "${DEFAULT_THIRD_PARTY_ROOT}" CACHE PATH "Path to the third-party SDK bundle")
if(EXISTS "${DEFAULT_THIRD_PARTY_ROOT}" AND THIRD_PARTY_ROOT STREQUAL LEGACY_THIRD_PARTY_ROOT AND NOT EXISTS "${THIRD_PARTY_ROOT}")
	set(THIRD_PARTY_ROOT "${DEFAULT_THIRD_PARTY_ROOT}" CACHE PATH "Path to the third-party SDK bundle" FORCE)
endif()
set(SLANG_ROOT "${THIRD_PARTY_ROOT}/slang-2026.8-windows-x86_64" CACHE PATH "Path to a Slang binary release containing bin/slangc.exe")
set(MSDF_ATLAS_GEN_ROOT "${THIRD_PARTY_ROOT}/msdf-atlas-gen" CACHE PATH "Path to msdf-atlas-gen binary release")
set(NDI_SDK_ROOT "${THIRD_PARTY_ROOT}/NDI 6 SDK" CACHE PATH "Path to the NDI SDK")
set(DECKLINK_SDK_ROOT "${THIRD_PARTY_ROOT}/Blackmagic DeckLink SDK 16.0" CACHE PATH "Path to the Blackmagic DeckLink SDK")

set(LEGACY_SLANG_ROOT "${LEGACY_THIRD_PARTY_ROOT}/slang-2026.8-windows-x86_64")
set(LEGACY_MSDF_ATLAS_GEN_ROOT "${LEGACY_THIRD_PARTY_ROOT}/msdf-atlas-gen")
set(LEGACY_NDI_SDK_ROOT "${LEGACY_THIRD_PARTY_ROOT}/NDI 6 SDK")
set(LEGACY_DECKLINK_SDK_ROOT "${LEGACY_THIRD_PARTY_ROOT}/Blackmagic DeckLink SDK 16.0")
if(EXISTS "${DEFAULT_THIRD_PARTY_ROOT}")
	if(SLANG_ROOT STREQUAL LEGACY_SLANG_ROOT AND NOT EXISTS "${SLANG_ROOT}")
		set(SLANG_ROOT "${THIRD_PARTY_ROOT}/slang-2026.8-windows-x86_64" CACHE PATH "Path to a Slang binary release containing bin/slangc.exe" FORCE)
	endif()
	if(MSDF_ATLAS_GEN_ROOT STREQUAL LEGACY_MSDF_ATLAS_GEN_ROOT AND NOT EXISTS "${MSDF_ATLAS_GEN_ROOT}")
		set(MSDF_ATLAS_GEN_ROOT "${THIRD_PARTY_ROOT}/msdf-atlas-gen" CACHE PATH "Path to msdf-atlas-gen binary release" FORCE)
	endif()
	if(NDI_SDK_ROOT STREQUAL LEGACY_NDI_SDK_ROOT AND NOT EXISTS "${NDI_SDK_ROOT}")
		set(NDI_SDK_ROOT "${THIRD_PARTY_ROOT}/NDI 6 SDK" CACHE PATH "Path to the NDI SDK" FORCE)
	endif()
	if(DECKLINK_SDK_ROOT STREQUAL LEGACY_DECKLINK_SDK_ROOT AND NOT EXISTS "${DECKLINK_SDK_ROOT}")
		set(DECKLINK_SDK_ROOT "${THIRD_PARTY_ROOT}/Blackmagic DeckLink SDK 16.0" CACHE PATH "Path to the Blackmagic DeckLink SDK" FORCE)
	endif()
endif()

set(VIDEO_SHADER_INCLUDE_DIRS
	"${SRC_DIR}"
	"${SRC_DIR}/app"
	"${SRC_DIR}/control"
	"${SRC_DIR}/control/http"
	"${SRC_DIR}/frames"
	"${SRC_DIR}/json"
	"${SRC_DIR}/logging"
	"${SRC_DIR}/platform"
	"${SRC_DIR}/preview"
	"${SRC_DIR}/render"
	"${SRC_DIR}/render/readback"
	"${SRC_DIR}/render/thread"
	"${SRC_DIR}/render/runtime"
	"${SRC_DIR}/runtime"
	"${SRC_DIR}/runtime/catalog"
	"${SRC_DIR}/runtime/layers"
	"${SRC_DIR}/runtime/shader"
	"${SRC_DIR}/runtime/state"
	"${SRC_DIR}/runtime/text"
	"${SRC_DIR}/shader"
	"${SRC_DIR}/telemetry"
	"${SRC_DIR}/video"
	"${SRC_DIR}/video/core"
	"${SRC_DIR}/video/decklink"
	"${SRC_DIR}/video/ndi"
	"${SRC_DIR}/video/playout"
)

function(video_shader_target_defaults target)
	target_include_directories(${target} PRIVATE ${VIDEO_SHADER_INCLUDE_DIRS})
	target_compile_definitions(${target} PRIVATE _UNICODE UNICODE)
	if(MSVC)
		target_compile_options(${target} PRIVATE /W3)
	endif()
endfunction()

function(video_shader_files_exist out_var)
	set(missing_files)
	foreach(file IN LISTS ARGN)
		if(NOT EXISTS "${file}")
			list(APPEND missing_files "${file}")
		endif()
	endforeach()
	set(${out_var} "${missing_files}" PARENT_SCOPE)
endfunction()

function(add_video_shader_test name)
	add_executable(${name} ${ARGN})
	video_shader_target_defaults(${name})
	add_test(NAME ${name} COMMAND ${name})
endfunction()

set(RUNTIME_JSON_SOURCES
	"${SRC_DIR}/runtime/state/RuntimeJson.cpp"
)

set(RUNTIME_PARAMETER_SOURCES
	${RUNTIME_JSON_SOURCES}
	"${SRC_DIR}/runtime/state/RuntimeParameterUtils.cpp"
)

set(RUNTIME_STATE_SOURCES
	${RUNTIME_JSON_SOURCES}
	"${SRC_DIR}/runtime/state/RuntimeStatePersistence.cpp"
)

set(RUNTIME_LAYER_SOURCES
	"${SRC_DIR}/runtime/layers/RuntimeLayerModel.cpp"
	"${SRC_DIR}/runtime/layers/RuntimeLayerReload.cpp"
	"${SRC_DIR}/runtime/layers/RuntimeLayerSnapshot.cpp"
	"${SRC_DIR}/runtime/layers/RuntimeLayerStateRestore.cpp"
)

set(RUNTIME_TEXT_SOURCES
	"${SRC_DIR}/runtime/text/FontAtlasBuilder.cpp"
	"${SRC_DIR}/runtime/text/FontAtlasImageLoader.cpp"
	"${SRC_DIR}/runtime/text/FontAtlasMetadata.cpp"
	"${SRC_DIR}/runtime/text/FontAtlasProcess.cpp"
	"${SRC_DIR}/runtime/text/RuntimeTextTextureComposer.cpp"
)

set(RUNTIME_CATALOG_SOURCES
	"${SRC_DIR}/runtime/catalog/SupportedShaderCatalog.cpp"
)

set(SHADER_MANIFEST_SOURCES
	"${SRC_DIR}/shader/ShaderManifestAssets.cpp"
	"${SRC_DIR}/shader/ShaderManifestParameters.cpp"
	"${SRC_DIR}/shader/ShaderManifestParser.cpp"
	"${SRC_DIR}/shader/ShaderPackageRegistry.cpp"
)

set(VIDEO_MODE_SOURCES
	"${SRC_DIR}/video/core/VideoMode.cpp"
)

set(VIDEO_FORMAT_SOURCES
	"${SRC_DIR}/video/core/VideoIOFormat.cpp"
)

set(SLANG_RUNTIME_FILES
	"${SLANG_ROOT}/bin/slangc.exe"
	"${SLANG_ROOT}/bin/slang-compiler.dll"
	"${SLANG_ROOT}/bin/slang-glslang.dll"
)
set(SLANG_LICENSE_FILE "${SLANG_ROOT}/LICENSE")
set(MSDF_ATLAS_GEN_EXE_FILE "${MSDF_ATLAS_GEN_ROOT}/msdf-atlas-gen.exe")
file(GLOB MSDF_ATLAS_GEN_DLL_FILES CONFIGURE_DEPENDS
	"${MSDF_ATLAS_GEN_ROOT}/*.dll"
)
set(MSDF_ATLAS_GEN_RUNTIME_FILES
	"${MSDF_ATLAS_GEN_EXE_FILE}"
	${MSDF_ATLAS_GEN_DLL_FILES}
)
set(MSDF_ATLAS_GEN_LICENSE_FILE "${MSDF_ATLAS_GEN_ROOT}/LICENSE.txt")
set(MSDF_ATLAS_GEN_README_FILE "${MSDF_ATLAS_GEN_ROOT}/README.md")
set(NDI_INCLUDE_DIR "${NDI_SDK_ROOT}/Include")
set(NDI_RUNTIME_DLL "${NDI_SDK_ROOT}/Bin/x64/Processing.NDI.Lib.x64.dll")
set(NDI_IMPORT_LIB "${NDI_SDK_ROOT}/Lib/x64/Processing.NDI.Lib.x64.lib")
set(NDI_LICENSE_FILE "${NDI_SDK_ROOT}/NDI SDK License Agreement.pdf")
set(NDI_NOTICES_FILE "${NDI_SDK_ROOT}/Bin/x64/Processing.NDI.Lib.Licenses.txt")
set(DECKLINK_SDK_IDL_FILE "${DECKLINK_SDK_ROOT}/Win/include/DeckLinkAPI.idl")
set(DECKLINK_SDK_LICENSE_FILE "${DECKLINK_SDK_ROOT}/End User License Agreement.pdf")

set(RENDER_CADENCE_APP_REQUIRED_FILES
	"${SRC_DIR}/RenderCadenceCompositor.cpp"
	"${SRC_DIR}/video/decklink/DeckLinkAPI_i.c"
	"${SRC_DIR}/video/decklink/DeckLinkAPI_h.h"
)

video_shader_files_exist(RENDER_CADENCE_APP_MISSING_FILES ${RENDER_CADENCE_APP_REQUIRED_FILES})
if(EXISTS "${DECKLINK_SDK_IDL_FILE}")
	message(STATUS "Blackmagic DeckLink SDK found: ${DECKLINK_SDK_ROOT}")
else()
	message(STATUS "Blackmagic DeckLink SDK not found at ${DECKLINK_SDK_ROOT}; using checked-in DeckLink API shim files only")
endif()
if(RENDER_CADENCE_APP_MISSING_FILES)
	message(STATUS "RenderCadenceCompositor target skipped; required app entry or external DeckLink SDK shim files are missing:")
	foreach(missing_file IN LISTS RENDER_CADENCE_APP_MISSING_FILES)
		message(STATUS "  ${missing_file}")
	endforeach()
else()
	file(GLOB_RECURSE RENDER_CADENCE_APP_SOURCES CONFIGURE_DEPENDS
		"${SRC_DIR}/*.c"
		"${SRC_DIR}/*.cpp"
		"${SRC_DIR}/*.h"
	)
	add_executable(RenderCadenceCompositor ${RENDER_CADENCE_APP_SOURCES})
	video_shader_target_defaults(RenderCadenceCompositor)
	if(EXISTS "${NDI_INCLUDE_DIR}" AND EXISTS "${NDI_IMPORT_LIB}")
		target_include_directories(RenderCadenceCompositor PRIVATE "${NDI_INCLUDE_DIR}")
		target_link_libraries(RenderCadenceCompositor PRIVATE "${NDI_IMPORT_LIB}")
		if(EXISTS "${NDI_RUNTIME_DLL}")
			add_custom_command(TARGET RenderCadenceCompositor POST_BUILD
				COMMAND ${CMAKE_COMMAND} -E copy_if_different
					"${NDI_RUNTIME_DLL}"
					"$<TARGET_FILE_DIR:RenderCadenceCompositor>/Processing.NDI.Lib.x64.dll"
			)
		endif()
	else()
		message(STATUS "NDI SDK headers/import library not found; NDI backends will not build correctly: ${NDI_SDK_ROOT}")
	endif()
	target_link_libraries(RenderCadenceCompositor PRIVATE
		opengl32
		Ole32
		Windowscodecs
		Ws2_32
	)
	source_group(TREE "${SRC_DIR}" FILES ${RENDER_CADENCE_APP_SOURCES})
endif()

if(BUILD_TESTING)
	add_subdirectory(tests)
endif()

if(TARGET RenderCadenceCompositor)
	install(TARGETS RenderCadenceCompositor
		RUNTIME DESTINATION "."
	)
endif()

foreach(slang_runtime_file IN LISTS SLANG_RUNTIME_FILES)
	if(EXISTS "${slang_runtime_file}")
		install(FILES "${slang_runtime_file}"
			DESTINATION "3rdParty/slang/bin"
		)
	else()
		message(STATUS "Slang runtime file not found and will not be installed: ${slang_runtime_file}")
	endif()
endforeach()

foreach(msdf_runtime_file IN LISTS MSDF_ATLAS_GEN_RUNTIME_FILES)
	if(EXISTS "${msdf_runtime_file}")
		install(FILES "${msdf_runtime_file}"
			DESTINATION "3rdParty/msdf-atlas-gen"
		)
	else()
		message(STATUS "msdf-atlas-gen runtime file not found and will not be installed: ${msdf_runtime_file}")
	endif()
endforeach()

if(EXISTS "${MSDF_ATLAS_GEN_LICENSE_FILE}")
	install(FILES "${MSDF_ATLAS_GEN_LICENSE_FILE}"
		DESTINATION "third_party_notices"
		RENAME "MSDF_ATLAS_GEN_LICENSE.txt"
	)
else()
	message(STATUS "msdf-atlas-gen license file not found: ${MSDF_ATLAS_GEN_LICENSE_FILE}")
endif()

if(EXISTS "${MSDF_ATLAS_GEN_README_FILE}")
	install(FILES "${MSDF_ATLAS_GEN_README_FILE}"
		DESTINATION "third_party_notices"
		RENAME "MSDF_ATLAS_GEN_README.md"
	)
else()
	message(STATUS "msdf-atlas-gen readme file not found: ${MSDF_ATLAS_GEN_README_FILE}")
endif()

if(EXISTS "${SLANG_LICENSE_FILE}")
	install(FILES "${SLANG_LICENSE_FILE}"
		DESTINATION "third_party_notices"
		RENAME "SLANG_LICENSE.txt"
	)
else()
	message(STATUS "Slang license file not found and will not be installed: ${SLANG_LICENSE_FILE}")
endif()

if(EXISTS "${NDI_RUNTIME_DLL}")
	install(FILES "${NDI_RUNTIME_DLL}"
		DESTINATION "."
	)
else()
	message(STATUS "NDI runtime DLL not found and will not be installed: ${NDI_RUNTIME_DLL}")
endif()

if(EXISTS "${NDI_LICENSE_FILE}")
	install(FILES "${NDI_LICENSE_FILE}"
		DESTINATION "third_party_notices"
		RENAME "NDI_SDK_LICENSE_AGREEMENT.pdf"
	)
else()
	message(STATUS "NDI license file not found: ${NDI_LICENSE_FILE}")
endif()

if(EXISTS "${NDI_NOTICES_FILE}")
	install(FILES "${NDI_NOTICES_FILE}"
		DESTINATION "third_party_notices"
		RENAME "NDI_RUNTIME_LICENSES.txt"
	)
else()
	message(STATUS "NDI runtime notices file not found: ${NDI_NOTICES_FILE}")
endif()

if(EXISTS "${DECKLINK_SDK_LICENSE_FILE}")
	install(FILES "${DECKLINK_SDK_LICENSE_FILE}"
		DESTINATION "third_party_notices"
		RENAME "BLACKMAGIC_DECKLINK_SDK_EULA.pdf"
	)
else()
	message(STATUS "Blackmagic DeckLink SDK license file not found: ${DECKLINK_SDK_LICENSE_FILE}")
endif()

install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/shaders/SHADER_CONTRACT.md"
	DESTINATION "."
)

install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/config/"
	DESTINATION "config"
)

install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shaders/"
	DESTINATION "shaders"
)

install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/runtime/templates/"
	DESTINATION "runtime/templates"
)

install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/runtime/README.md"
	DESTINATION "runtime"
)

install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ui/dist/"
	DESTINATION "ui/dist"
	OPTIONAL
)

install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/docs/"
	DESTINATION "docs"
	OPTIONAL
)
