# SDL2 project starter
#
# Copyright (c) 2022, András Bodor
# All rights reserved.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
# CMakeLists.txt --
#   The main CMake script for building and managing the project.
#   Reads the required information from the project.json file in the project
#   root, and uses it to configure CMake in a way that should just work:tm:.

cmake_minimum_required(VERSION 3.21)

# Read project configuration from project.json #################################
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/project.json" PR_CONFIG_J)

## NAME ##
string(JSON PR_NAME
       GET "${PR_CONFIG_J}" "project")

## VERSION ##
string(JSON PR_VERSION ERROR_VARIABLE _error
       GET "${PR_CONFIG_J}" "version")
if (_error)
    set(PR_VERSION "1.0.0") # default
endif ()

## WARNINGS ##
string(JSON PR_WARN ERROR_VARIABLE _error
       GET "${PR_CONFIG_J}" "warnings")
if (_error)
    set(PR_WARN YES) # default
endif ()

## WERROR ##
string(JSON PR_WERROR ERROR_VARIABLE _error
       GET "${PR_CONFIG_J}" "warnings-to-errors")
if (_error)
    set(PR_WERROR YES) # default
endif ()

## EXES ##
string(JSON PR_EXES_J
       GET "${PR_CONFIG_J}" "exes")

string(JSON PR_EXES_SZ
       LENGTH "${PR_EXES_J}")
set(PR_EXES)
foreach (i RANGE "${PR_EXES_SZ}")
    if (i EQUAL PR_EXES_SZ) # only need i < PR_EXES_SZ
        break()
    endif ()

    string(JSON _pr_exe_name
           MEMBER "${PR_EXES_J}" ${i})
    list(APPEND PR_EXES "${_pr_exe_name}")

    string(JSON _pr_exe_src_j
           GET "${PR_EXES_J}" "${_pr_exe_name}")
    string(JSON _pr_exe_src_sz
           LENGTH "${_pr_exe_src_j}")
    set("PR_EXE_${_pr_exe_name}_SRC")
    foreach (j RANGE "${_pr_exe_src_sz}")
        if (j EQUAL _pr_exe_src_sz) # only need j < _pr_exe_src_sz
            break()
        endif ()

        string(JSON "_pr_exe_src"
               GET "${_pr_exe_src_j}" ${j})
        list(APPEND "PR_EXE_${_pr_exe_name}_SRC" "${_pr_exe_src}")
    endforeach ()
endforeach ()

## DEPS ##
string(JSON PR_DEPS_J
       GET "${PR_CONFIG_J}" "deps")

string(JSON PR_DEPS_SZ
       LENGTH "${PR_DEPS_J}")
set(PR_DEPS)
foreach (i RANGE "${PR_DEPS_SZ}")
    if (i EQUAL PR_DEPS_SZ) # only need i < PR_DEPS_SZ
        break()
    endif ()

    string(JSON _pr_dep
           GET "${PR_DEPS_J}" ${i})
    list(APPEND PR_DEPS "${_pr_dep}")
endforeach ()

## Status ##
message(STATUS "project(${PR_NAME})")
message(STATUS "\tversion: ${PR_VERSION}")
message(STATUS "\tdependencies: ${PR_DEPS}")
foreach (exe IN LISTS PR_EXES)
    message(STATUS "\texe(${exe}): ${PR_EXE_${exe}_SRC}")
endforeach ()

# CMake setup ##################################################################
# Load languages into CMake, this'll allow us to figure out the type of the
# compiler we are dealing with, thus can set things accordingly
# Also include some CMake modules we need
enable_language(C)
enable_language(CXX)

include(GNUInstallDirs)
include(InstallRequiredSystemLibraries)

# vcpkg ########################################################################
# Get vcpkg, install required dependencies, and load the toolchain file.

if (WIN32)
    if (MSVC)
        set(VCPKG_TRIPLET x64-windows)
    else ()
        set(VCPKG_TRIPLET x64-mingw-dynamic)
        set(ENV{VCPKG_DEFAULT_HOST_TRIPLET} x64-mingw-dynamic)
    endif ()
    set(VCPKG_BOOT_SUFFIX .bat)
    set(VCPKG_SUFFIX .exe)
else ()
    set(VCPKG_TRIPLET)
    set(VCPKG_BOOT_SUFFIX .sh)
    set(VCPKG_SUFFIX)
endif ()

if (VCPKG_TRIPLET)
    set(VCPKG_TRIPLET_CMD "--triplet=${VCPKG_TRIPLET}")
    list(PREPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/${VCPKG_TRIPLET}")
else ()
    # ¯\_(ツ)_/¯
    list(PREPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x86-windows") # maybe?
    list(PREPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-windows")
    list(PREPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/arm64-windows")
    list(PREPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-linux")
    list(PREPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-osx")
    # unlikely any of the uwp targets to be used
endif ()

if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/vcpkg${VCPKG_SUFFIX}")
    # Setup vcpkg
    file(DOWNLOAD
         https://github.com/microsoft/vcpkg/archive/refs/heads/master.tar.gz
         ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg.tar.gz)
    file(ARCHIVE_EXTRACT INPUT
         ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg.tar.gz)
    execute_process(COMMAND
                    "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/bootstrap-vcpkg${VCPKG_BOOT_SUFFIX}" -disableMetrics
                    COMMAND_ECHO STDOUT
                    OUTPUT_VARIABLE _ignore
                    ECHO_OUTPUT_VARIABLE)

    # Get depdencies
    execute_process(COMMAND
                    "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/vcpkg${VCPKG_SUFFIX}"
                    install
                    ${VCPKG_TRIPLET_CMD}
                    ${PR_DEPS}
                    COMMAND_ECHO STDOUT
                    OUTPUT_VARIABLE _ignore
                    ECHO_OUTPUT_VARIABLE)
endif ()

set(CMAKE_TOOLCHAIN_FILE
    "${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/scripts/buildsystems/vcpkg.cmake")

# CMake policies ###############################################################
# Set CMake policies to the expected behavior.

if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
    cmake_policy(SET CMP0135 NEW)
endif ()
cmake_policy(SET CMP0125 NEW)
cmake_policy(SET CMP0118 NEW)
cmake_policy(SET CMP0115 NEW)
cmake_policy(SET CMP0111 NEW)
cmake_policy(SET CMP0110 NEW)
cmake_policy(SET CMP0096 NEW)
cmake_policy(SET CMP0092 NEW)
cmake_policy(SET CMP0077 NEW)
cmake_policy(SET CMP0048 NEW)

# CTK project ##################################################################
# Specify the CTK project's main CMake project
project("${PR_NAME}"
        VERSION "${PR_VERSION}"
        LANGUAGES C CXX)

## Project backup ##
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/used.json"
                   DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/project.json"
                   COMMAND "${CMAKE_COMMAND}" -E copy
                   "${CMAKE_CURRENT_SOURCE_DIR}/project.json"
                   "${CMAKE_CURRENT_BINARY_DIR}/used.json"
                   COMMENT "Backing-up project.json")

# check if project is trying to be GUI
# needed for Windows builds, because you cannot set it after add_executable,
# ... because that makes infinite sense:
# we need to do `add_executable(exe WIN32 ...)`
find_package(SDL2 CONFIG)
if (TARGET SDL2::SDL2)
    if (WIN32)
        set(_PR_GUI WIN32)
    endif ()
endif ()

foreach (exe IN LISTS PR_EXES)
    add_executable("${exe}" ${_PR_GUI}
                   ${PR_EXE_${exe}_SRC})
    target_compile_definitions("${exe}" PRIVATE
                               $<$<C_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS>)
    target_include_directories("${exe}" PRIVATE
                               # brute-forcing include paths ... ?
                               # most of these paths probably doesn't even exist
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-osx/include>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-osx/include/SDL2>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-linux/include>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-linux/include/SDL2>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-windows/include>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-windows/include/SDL2>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-mingw-static/include>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-mingw-static/include/SDL2>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-mingw-dynamic/include>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x64-mingw-dynamic/include/SDL2>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x86-windows/include>
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/vcpkg-master/installed/x86-windows/include/SDL2>)
    install(TARGETS "${exe}")

    if (PR_WARN)
        target_compile_options("${exe}" PRIVATE
                               $<$<C_COMPILER_ID:MSVC>:/W3>
                               $<$<C_COMPILER_ID:Clang,AppleClang,GNU>:-Wall>
                               $<$<C_COMPILER_ID:Clang,AppleClang,GNU>:-Wextra>)
    endif ()

    if (PR_WERROR)
        target_compile_options("${exe}" PRIVATE
                               $<$<C_COMPILER_ID:MSVC>:/wd4244 /WX>
                               $<$<C_COMPILER_ID:Clang,AppleClang,GNU>:-Werror>)
    endif ()

    ## Link libs if we have them ##
    # TODO: rewrite this into functions ...somehow?
    if (TARGET SDL2::SDL2)
        target_link_libraries("${exe}" PRIVATE SDL2::SDL2 SDL2::SDL2main)
        add_custom_command(TARGET "${exe}" POST_BUILD
                           COMMENT "Copying SDL2 runtime"
                           COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:SDL2::SDL2> $<TARGET_FILE_DIR:${exe}>)
    endif ()

    find_package(sdl2-gfx CONFIG)
    if (TARGET SDL2::SDL2_gfx)
        target_link_libraries("${exe}" PRIVATE SDL2::SDL2_gfx)
        add_custom_command(TARGET "${exe}" POST_BUILD
                           COMMENT "Copying SDL2_gfx runtime"
                           COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:SDL2::SDL2_gfx> $<TARGET_FILE_DIR:${exe}>)
    endif ()

    find_package(sdl2-image CONFIG)
    if (TARGET SDL2::SDL2_image)
        target_link_libraries("${exe}" PRIVATE SDL2::SDL2_image)
        add_custom_command(TARGET "${exe}" POST_BUILD
                           COMMENT "Copying SDL2_image runtime"
                           COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:SDL2::SDL2_image> $<TARGET_FILE_DIR:${exe}>)
    endif ()

    find_package(SDL2_mixer CONFIG)
    if (TARGET SDL2_mixer::SDL2_mixer
        OR TARGET SDL2_mixer::SDL2_mixer-static)
        set(_sdl2_mixer $<IF:$<TARGET_EXISTS:SDL2_mixer::SDL2_mixer>,SDL2_mixer::SDL2_mixer,SDL2_mixer::SDL2_mixer-static>)
        target_link_libraries("${exe}" PRIVATE ${_sdl2_mixer})
        add_custom_command(TARGET "${exe}" POST_BUILD
                           COMMENT "Copying SDL2_mixer runtime"
                           COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:${_sdl2_mixer}> $<TARGET_FILE_DIR:${exe}>)
    endif ()

    find_package(sdl2-net CONFIG)
    if (TARGET SDL2::SDL2_net)
        target_link_libraries("${exe}" PRIVATE SDL2::SDL2_net)
        add_custom_command(TARGET "${exe}" POST_BUILD
                           COMMENT "Copying SDL2_net runtime"
                           COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:SDL2::SDL2_net> $<TARGET_FILE_DIR:${exe}>)
    endif ()

    find_package(SDL2_ttf CONFIG)
    if (TARGET SDL2_ttf::SDL2_ttf
        OR TARGET SDL2_ttf::SDL2_ttf-static)
        set(_sdl2_ttf $<IF:$<TARGET_EXISTS:SDL2_ttf::SDL2_ttf>,SDL2_ttf::SDL2_ttf,SDL2_ttf::SDL2_ttf-static>)
        target_link_libraries("${exe}" PRIVATE ${_sdl2_ttf})
        add_custom_command(TARGET "${exe}" POST_BUILD
                           COMMENT "Copying SDL2_ttf runtime"
                           COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:${_sdl2_ttf}> $<TARGET_FILE_DIR:${exe}>)
    endif ()

    if (WIN32)
        if (NOT MSVC)
            cmake_path(GET CMAKE_C_COMPILER PARENT_PATH _msys_bin)
            cmake_path(GET _msys_bin PARENT_PATH _msys_root)
        endif ()
        install(CODE "
            file(GET_RUNTIME_DEPENDENCIES
                 DIRECTORIES
                    [[${_msys_bin}]]
                    [[${_msys_root}/bin]]
                 PRE_EXCLUDE_REGEXES
                    [[api-ms-.*]]
                 POST_EXCLUDE_REGEXES
                    [[.*system32/]]
                 RESOLVED_DEPENDENCIES_VAR rt_dll
                 EXECUTABLES $<TARGET_FILE:${exe}>)
             file(WRITE [[${CMAKE_CURRENT_BINARY_DIR}/what.log]] \"\${rt_dll}\")
             file(INSTALL DESTINATION [[${CMAKE_INSTALL_BINDIR}]] TYPE EXECUTABLE FILES \${rt_dll})")
    endif ()
endforeach ()

## Packaging ###################################################################
# TODO: add extra project.json variables to insert custom files for installation
set(CPACK_GENERATOR "ZIP")
include(CPack)
