cmake_minimum_required(VERSION 3.23)

project(ffpsd
    VERSION 0.4.7
    DESCRIPTION "ffpsd library"
    LANGUAGES CXX)

if(PROJECT_IS_TOP_LEVEL AND CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
    set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/ffpsd-out"
        CACHE PATH "Install prefix" FORCE)
endif()

option(FFPSD_BUILD_APPS "Build the apps in apps/ (img2ffpsd)" ON)
option(FFPSD_WITH_PNG "Build PNG loading, with libpng and zlib-ng from vendor/" ON)
option(FFPSD_WITH_JPEG "Build JPEG loading, with libjpeg-turbo from vendor/" ON)
option(FFPSD_WITH_WEBP "Build WebP loading, with libwebp from vendor/" ON)
option(FFPSD_WITH_IMGUI "Build the apps with a window, with Dear ImGui and SDL3 from vendor/" OFF)
option(FFPSD_BUILD_TESTS "Build the unit tests, with GoogleTest from vendor/" OFF)
option(FFPSD_BUILD_BENCHMARKS "Build the benchmarks, with Google Benchmark from vendor/" OFF)
option(FFPSD_BUILD_EXAMPLES "Build the C and C++ examples in examples/" OFF)

# /MD, as CMake gives programs; a DLL with /MT gets a second CRT that frees std types from the wrong heap.
option(FFPSD_MSVC_STATIC_RUNTIME "MSVC: link the C runtime statically (/MT)" OFF)

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

set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)

# MSVC only, ignored by other compilers.
if(FFPSD_MSVC_STATIC_RUNTIME)
    set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
else()
    set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()

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")

# Before the warning and LTO settings below, which are for our code only.
set(ffpsd_vendored)
if(FFPSD_WITH_PNG)
    list(APPEND ffpsd_vendored libpng zlib-ng)
endif()
if(FFPSD_WITH_JPEG)
    list(APPEND ffpsd_vendored libjpeg-turbo)
endif()
if(FFPSD_WITH_WEBP)
    list(APPEND ffpsd_vendored libwebp)
endif()
if(FFPSD_WITH_IMGUI)
    list(APPEND ffpsd_vendored imgui SDL)
endif()
if(FFPSD_BUILD_TESTS)
    list(APPEND ffpsd_vendored googletest)
endif()
if(FFPSD_BUILD_BENCHMARKS)
    list(APPEND ffpsd_vendored benchmark)
endif()
foreach(ffpsd_library IN LISTS ffpsd_vendored)
    if(NOT EXISTS "${PROJECT_SOURCE_DIR}/vendor/${ffpsd_library}/CMakeLists.txt"
       AND NOT EXISTS "${PROJECT_SOURCE_DIR}/vendor/${ffpsd_library}/${ffpsd_library}.h")
        message(FATAL_ERROR "vendor/${ffpsd_library} is empty: run git submodule update --init")
    endif()
endforeach()
if(ffpsd_vendored)
    add_subdirectory(vendor)
endif()

if(MSVC)
    set(ffpsd_warnings /W4 /permissive-)
    set(ffpsd_optimize /O2 /Ob3 /Oi /Ot /Gy)
    string(REPLACE "/Ob2" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
else()
    set(ffpsd_warnings -Wall -Wextra -Wpedantic -Wshadow -Wconversion)
    set(ffpsd_cxx_warnings -Wold-style-cast) # C has no other cast
    set(ffpsd_optimize -O3)
endif()

add_compile_options(
    "$<$<CONFIG:Debug>:${ffpsd_warnings}>"
    "$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:CXX>>:${ffpsd_cxx_warnings}>"
    "$<$<CONFIG:Release>:${ffpsd_optimize}>")

# C++ only: C is enabled by the vendored projects in their own directories, where CMake keeps its IPO settings.
include(CheckIPOSupported)
check_ipo_supported(RESULT ffpsd_has_lto OUTPUT ffpsd_lto_error LANGUAGES CXX)
if(NOT ffpsd_has_lto)
    message(STATUS "LTO unavailable: ${ffpsd_lto_error}")
endif()

include(GNUInstallDirs)

add_subdirectory(ffpsd)

if(FFPSD_BUILD_APPS)
    add_subdirectory(apps)
endif()

if(FFPSD_BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests/cases)
endif()

if(FFPSD_BUILD_BENCHMARKS)
    add_subdirectory(benchmarks)
endif()

if(FFPSD_BUILD_EXAMPLES)
    enable_language(C)
    add_subdirectory(examples/c)
    add_subdirectory(examples/cpp)
endif()

if(BUILD_SHARED_LIBS)
    set(ffpsd_kind shared)
else()
    set(ffpsd_kind static)
endif()

if(MSVC AND BUILD_SHARED_LIBS AND FFPSD_MSVC_STATIC_RUNTIME)
    message(WARNING "ffpsd: a DLL with the static CRT is safe to use through the C ABI only")
endif()

if(MSVC AND FFPSD_MSVC_STATIC_RUNTIME)
    set(ffpsd_crt ", static CRT")
elseif(MSVC)
    set(ffpsd_crt ", dynamic CRT")
endif()
message(STATUS "ffpsd ${PROJECT_VERSION}: ${ffpsd_kind}, C++ API + C ABI${ffpsd_crt}")
