cmake_minimum_required(VERSION 3.20)

project(cvmmap-streamer LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

option(ENABLE_GSTREAMER_LEGACY "Build the optional legacy GStreamer backend" ON)

find_package(Threads REQUIRED)
find_package(cppzmq QUIET)
if (
	NOT cvmmap-core_DIR
	AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/../cv-mmap/build/core/cvmmap-coreConfig.cmake"
	AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/../cv-mmap/build/core/cvmmap-coreTargets.cmake")
	set(cvmmap-core_DIR "${CMAKE_CURRENT_LIST_DIR}/../cv-mmap/build/core")
endif()
if (cvmmap-core_DIR)
	find_package(cvmmap-core CONFIG QUIET)
endif()
find_package(ZeroMQ QUIET)
find_package(spdlog REQUIRED)
find_package(Protobuf REQUIRED)
find_package(PkgConfig REQUIRED)

if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/lib/CLI11/CMakeLists.txt")
	add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/lib/CLI11" "${CMAKE_CURRENT_BINARY_DIR}/vendor/cli11")
endif()

pkg_check_modules(FFMPEG REQUIRED IMPORTED_TARGET libavcodec libavformat libavutil libswscale)
pkg_check_modules(PROTOBUF_PKG QUIET IMPORTED_TARGET protobuf)
pkg_check_modules(ZSTD REQUIRED IMPORTED_TARGET libzstd)
pkg_check_modules(LZ4 REQUIRED IMPORTED_TARGET liblz4)

if (NOT TARGET cvmmap::client)
	set(CVMMAP_LOCAL_ROOT "${CMAKE_CURRENT_LIST_DIR}/../cv-mmap")
	set(CVMMAP_LOCAL_BUILD "${CVMMAP_LOCAL_ROOT}/build/core")
	if (
		EXISTS "${CVMMAP_LOCAL_ROOT}/core/include/cvmmap/client.hpp"
		AND EXISTS "${CVMMAP_LOCAL_BUILD}/libcvmmap_client.a"
		AND EXISTS "${CVMMAP_LOCAL_BUILD}/libcvmmap_ipc.a"
		AND EXISTS "${CVMMAP_LOCAL_BUILD}/libcvmmap_target.a")
		add_library(cvmmap::client INTERFACE IMPORTED)
		set_target_properties(cvmmap::client PROPERTIES
			INTERFACE_INCLUDE_DIRECTORIES "${CVMMAP_LOCAL_ROOT}/core/include"
			INTERFACE_LINK_LIBRARIES "${CVMMAP_LOCAL_BUILD}/libcvmmap_client.a;${CVMMAP_LOCAL_BUILD}/libcvmmap_ipc.a;${CVMMAP_LOCAL_BUILD}/libcvmmap_target.a")
	else()
		message(FATAL_ERROR "cvmmap::client target is unavailable and local cv-mmap build artifacts were not found")
	endif()
endif()

set(CVMMAP_PROXY_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/lib/proxy/include")
if (NOT EXISTS "${CVMMAP_PROXY_INCLUDE_DIR}/proxy/proxy.h")
	message(FATAL_ERROR "proxy headers not found at ${CVMMAP_PROXY_INCLUDE_DIR}")
endif()

set(CVMMAP_STREAMER_HAS_GSTREAMER 0)
if (ENABLE_GSTREAMER_LEGACY)
	pkg_check_modules(GSTREAMER IMPORTED_TARGET gstreamer-1.0>=1.14 gstreamer-video-1.0>=1.14 gstreamer-app-1.0>=1.14)
	if (GSTREAMER_FOUND AND TARGET PkgConfig::GSTREAMER)
		set(CVMMAP_STREAMER_HAS_GSTREAMER 1)
	else()
		message(WARNING "GStreamer legacy backend disabled because required GStreamer packages were not found")
	endif()
endif()

add_library(cvmmap_streamer_foxglove_proto STATIC)
protobuf_generate(
	TARGET cvmmap_streamer_foxglove_proto
	LANGUAGE cpp
	PROTOS "${CMAKE_CURRENT_LIST_DIR}/proto/foxglove/CompressedVideo.proto"
	IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}/proto")
add_library(cvmmap_streamer_protobuf INTERFACE)
target_include_directories(cvmmap_streamer_foxglove_proto
	PUBLIC
		"${CMAKE_CURRENT_BINARY_DIR}"
		${Protobuf_INCLUDE_DIRS})
target_include_directories(cvmmap_streamer_protobuf
	INTERFACE
		${Protobuf_INCLUDE_DIRS})
if (TARGET protobuf::libprotobuf)
	target_link_libraries(cvmmap_streamer_protobuf INTERFACE protobuf::libprotobuf)
elseif (TARGET Protobuf::libprotobuf)
	target_link_libraries(cvmmap_streamer_protobuf INTERFACE Protobuf::libprotobuf)
else()
	target_link_libraries(cvmmap_streamer_protobuf INTERFACE ${Protobuf_LIBRARIES})
endif()
if (TARGET PkgConfig::PROTOBUF_PKG)
	target_link_libraries(cvmmap_streamer_protobuf INTERFACE PkgConfig::PROTOBUF_PKG)
endif()
target_link_libraries(cvmmap_streamer_foxglove_proto PUBLIC cvmmap_streamer_protobuf)

add_library(cvmmap_streamer_common STATIC
	src/ipc/help.cpp
	src/config/runtime_config.cpp
	src/core/frame_source.cpp
	src/core/ingest_runtime.cpp
	src/ipc/contracts.cpp
	src/protocol/wire_codec.cpp
	src/metrics/latency_tracker.cpp
	src/pipeline/pipeline_runtime.cpp
	src/protocol/rtmp_output.cpp
	src/protocol/rtmp_publisher.cpp
	src/protocol/rtp_publisher.cpp
	src/encode/encoder_backend.cpp
	src/encode/ffmpeg_encoder_backend.cpp
	src/encode/gstreamer_legacy_backend.cpp
	src/record/protobuf_descriptor.cpp
	src/record/mcap_record_sink.cpp)

target_include_directories(cvmmap_streamer_common
	PUBLIC
		"${CMAKE_CURRENT_LIST_DIR}/include"
		"${CMAKE_CURRENT_LIST_DIR}/lib/CLI11/include"
		"${CMAKE_CURRENT_LIST_DIR}/lib/tomlplusplus/include"
		"${CMAKE_CURRENT_LIST_DIR}/lib/mcap/include"
		"${CVMMAP_PROXY_INCLUDE_DIR}"
		"${CMAKE_CURRENT_BINARY_DIR}")

target_compile_definitions(cvmmap_streamer_common
	PUBLIC
		CVMMAP_STREAMER_HAS_GSTREAMER=${CVMMAP_STREAMER_HAS_GSTREAMER})

set(CVMMAP_STREAMER_LINK_DEPS
	Threads::Threads
	cvmmap_streamer_foxglove_proto
	PkgConfig::FFMPEG
	PkgConfig::ZSTD
	PkgConfig::LZ4
	cvmmap::client)

if (TARGET cppzmq::cppzmq)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS cppzmq::cppzmq)
elseif (TARGET cppzmq)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS cppzmq)
endif()

if (TARGET ZeroMQ::libzmq)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS ZeroMQ::libzmq)
elseif (TARGET ZeroMQ::ZeroMQ)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS ZeroMQ::ZeroMQ)
endif()

if (TARGET spdlog::spdlog)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS spdlog::spdlog)
elseif (TARGET spdlog)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS spdlog)
endif()

if (TARGET CLI11::CLI11)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS CLI11::CLI11)
endif()

list(APPEND CVMMAP_STREAMER_LINK_DEPS cvmmap_streamer_protobuf)
if (TARGET PkgConfig::PROTOBUF_PKG)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS PkgConfig::PROTOBUF_PKG)
endif()

if (CVMMAP_STREAMER_HAS_GSTREAMER)
	list(APPEND CVMMAP_STREAMER_LINK_DEPS PkgConfig::GSTREAMER)
endif()

target_link_libraries(cvmmap_streamer_common PUBLIC ${CVMMAP_STREAMER_LINK_DEPS})

function(add_cvmmap_binary target source)
	add_executable(${target} ${source} ${ARGN})
	target_include_directories(${target}
		PRIVATE
			"${CMAKE_CURRENT_LIST_DIR}/include"
			"${CMAKE_CURRENT_LIST_DIR}/lib/CLI11/include"
			"${CMAKE_CURRENT_LIST_DIR}/lib/tomlplusplus/include"
			"${CMAKE_CURRENT_LIST_DIR}/lib/mcap/include"
			"${CVMMAP_PROXY_INCLUDE_DIR}"
			"${CMAKE_CURRENT_BINARY_DIR}")
	target_link_libraries(${target}
		PRIVATE
			cvmmap_streamer_common)
	set_target_properties(${target} PROPERTIES OUTPUT_NAME "${target}")
endfunction()

add_cvmmap_binary(cvmmap_streamer src/main_streamer.cpp)
add_cvmmap_binary(rtp_receiver_tester src/testers/rtp_receiver_tester.cpp)
add_cvmmap_binary(rtmp_stub_tester src/testers/rtmp_stub_tester.cpp)
add_cvmmap_binary(rtmp_output_tester src/testers/rtmp_output_tester.cpp)
add_cvmmap_binary(ipc_snapshot_tester src/testers/ipc_snapshot_tester.cpp)

add_executable(mcap_reader_tester src/testers/mcap_reader_tester.cpp)
target_include_directories(mcap_reader_tester
	PRIVATE
		"${CMAKE_CURRENT_LIST_DIR}/include"
		"${CMAKE_CURRENT_LIST_DIR}/lib/CLI11/include"
		"${CMAKE_CURRENT_LIST_DIR}/lib/mcap/include"
		"${CVMMAP_PROXY_INCLUDE_DIR}"
		"${CMAKE_CURRENT_BINARY_DIR}")
target_link_libraries(mcap_reader_tester
	PRIVATE
		cvmmap_streamer_foxglove_proto
		PkgConfig::ZSTD
		PkgConfig::LZ4)
if (TARGET spdlog::spdlog)
	target_link_libraries(mcap_reader_tester PRIVATE spdlog::spdlog)
elseif (TARGET spdlog)
	target_link_libraries(mcap_reader_tester PRIVATE spdlog)
endif()
if (TARGET CLI11::CLI11)
	target_link_libraries(mcap_reader_tester PRIVATE CLI11::CLI11)
endif()
target_link_libraries(mcap_reader_tester PRIVATE cvmmap_streamer_protobuf)
if (TARGET PkgConfig::PROTOBUF_PKG)
	target_link_libraries(mcap_reader_tester PRIVATE PkgConfig::PROTOBUF_PKG)
endif()

add_executable(mcap_replay_tester src/testers/mcap_replay_tester.cpp)
target_include_directories(mcap_replay_tester
	PRIVATE
		"${CMAKE_CURRENT_LIST_DIR}/include"
		"${CMAKE_CURRENT_LIST_DIR}/lib/CLI11/include"
		"${CMAKE_CURRENT_LIST_DIR}/lib/mcap/include"
		"${CVMMAP_PROXY_INCLUDE_DIR}"
		"${CMAKE_CURRENT_BINARY_DIR}")
target_link_libraries(mcap_replay_tester
	PRIVATE
		Threads::Threads
		cvmmap_streamer_foxglove_proto
		PkgConfig::ZSTD
		PkgConfig::LZ4)
if (TARGET spdlog::spdlog)
	target_link_libraries(mcap_replay_tester PRIVATE spdlog::spdlog)
elseif (TARGET spdlog)
	target_link_libraries(mcap_replay_tester PRIVATE spdlog)
endif()
if (TARGET CLI11::CLI11)
	target_link_libraries(mcap_replay_tester PRIVATE CLI11::CLI11)
endif()
target_link_libraries(mcap_replay_tester PRIVATE cvmmap_streamer_protobuf)
if (TARGET PkgConfig::PROTOBUF_PKG)
	target_link_libraries(mcap_replay_tester PRIVATE PkgConfig::PROTOBUF_PKG)
endif()
