# ============================================================================
# =========================  PROJECT SETUP  ==================================
# ============================================================================
cmake_minimum_required(VERSION 3.22)

set(VARIANT "EVAL" CACHE STRING "CycloneBOOT Edition" FORCE)
set_property(CACHE VARIANT PROPERTY STRINGS OPEN EVAL LITE PRO ULTIMATE)

set(valid_variants OPEN EVAL LITE PRO ULTIMATE)
list(FIND valid_variants "${VARIANT}" variant_index)
if(variant_index EQUAL -1)
  message(FATAL_ERROR
            "Invalid VARIANT='${VARIANT}'. Must be one of: ${valid_variants}")
endif()

# Take the version from the product header, so it is maintained in one place
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_boot/core/cboot_error.h _cboot_error_h)
string(REGEX MATCH "@version ([0-9]+\\.[0-9]+\\.[0-9]+)" _ "${_cboot_error_h}")
set(IMAGE_BUILDER_VERSION ${CMAKE_MATCH_1})
if(NOT IMAGE_BUILDER_VERSION)
  message(FATAL_ERROR "Could not read @version from src/cyclone_boot/core/cboot_error.h")
endif()

# set the project name and languages
project(image_builder VERSION ${IMAGE_BUILDER_VERSION} LANGUAGES C)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

string(TOUPPER "${CMAKE_BUILD_TYPE}" IMAGE_BUILDER_BUILD_TYPE)

# Create a common target for compile definitions
add_library(common INTERFACE)

# Project-wide, since lib/cargs honours ENABLE_COVERAGE too
option(ENABLE_COVERAGE "Enable code coverage generation" OFF)
if (ENABLE_COVERAGE)
  if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANGC)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
  endif ()
endif ()

# Include cargs, a library for command-line parsing
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/cargs)

# Include CycloneCRYPTO, our crypto library
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
  set(CYCLONE_CRYPTO_PORT_SRC
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common/os_port_posix.c
    )
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
  set(CYCLONE_CRYPTO_PORT_SRC
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common/os_port_windows.c
    )
endif ()

set(CYCLONE_CRYPTO_SRC
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common/debug.c
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common/cpu_endian.c
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common/date_time.c

        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/hash/sha512.c
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/hash/sha256.c
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/hash/sha224.c
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/hash/sha384.c
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/hash/sha1.c
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/hash/md5.c
        ${CYCLONE_CRYPTO_PORT_SRC}
)

if(VARIANT STREQUAL "OPEN" OR VARIANT STREQUAL "EVAL" OR VARIANT STREQUAL "ULTIMATE")
  list(APPEND CYCLONE_CRYPTO_SRC

            ${CMAKE_CURRENT_SOURCE_DIR}/src/check_probable_prime.c
            ${CMAKE_CURRENT_SOURCE_DIR}/src/mpi.c

            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/rng/hmac_drbg.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/cipher/aes.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/cipher_modes/cbc.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/mac/hmac.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkc/rsa.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkc/rsa_misc.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkc/dsa.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/encoding/oid.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/encoding/asn1.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/encoding/base64.c
            #${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/mpi/mpi.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/mpi/mpi_misc.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/pem_common.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/pem_key_import.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/pem_key_export.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/pem_decrypt.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/x509_common.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/x509_key_parse.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/x509_key_format.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/pem_import.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/pkcs8_key_parse.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix/pkcs8_key_format.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/ecc/ec_curves.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/ecc/ecdsa.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/ecc/ec.c
            ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/ecc/ec_misc.c
    )
endif()

include_directories(
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common/
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/hash
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/rng
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/cipher
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/cipher_mode
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/mac
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkc
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/encoding
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/mpi
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/pkix
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/ecc
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_boot
)

# The tool sources, shared with the test targets in tests/CMakeLists.txt
set(IMAGE_BUILDER_SRC
        ${CMAKE_CURRENT_SOURCE_DIR}/src/cli.c
        ${CMAKE_CURRENT_SOURCE_DIR}/src/header.c
        ${CMAKE_CURRENT_SOURCE_DIR}/src/body.c
        ${CMAKE_CURRENT_SOURCE_DIR}/src/footer.c
        ${CMAKE_CURRENT_SOURCE_DIR}/src/utils.c
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_boot/core/crc32.c
)

set(IMAGE_BUILDER_INCLUDE_DIRS
        ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/inc
        ${CMAKE_CURRENT_SOURCE_DIR}/config
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/ecc
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/rng
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/cipher
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_crypto/cipher_modes
        ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cyclone_boot/modules/image
        ${PROJECT_BINARY_DIR}
)

# add the executable
add_executable(image_builder
        src/main.c
        ${IMAGE_BUILDER_SRC}
        ${CYCLONE_CRYPTO_SRC}
)
# =============================================================================


# =============================================================================
# =========================  PROJECT CONFIG  ==================================
# =============================================================================

# let the preprocessor know about the system name
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
  target_compile_definitions(common INTERFACE IS_LINUX)
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL Darwin)
  target_compile_definitions(common INTERFACE IS_MACOS) # No Macros for MacOS as of yet.
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
  target_compile_definitions(common INTERFACE IS_WINDOWS)
endif ()
if (MINGW)
  # MinGW: no weak symbols, lean Windows headers, static runtime
  target_compile_definitions(common INTERFACE __weak_func= WIN32_LEAN_AND_MEAN)
  set(CMAKE_C_FLAGS_RELEASE "-O1 -DNDEBUG")
  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
endif ()
if (DEFINED ENV{DOCKER_ENV})
  message(STATUS "Running inside Docker (environment variable detected)!")
  target_compile_definitions(common INTERFACE IS_DOCKER) # Not used as of yet.
endif ()

# Accept the licence terms; commercial editions carry no guard
if (VARIANT STREQUAL "EVAL")
  target_compile_definitions(common INTERFACE EVAL_LICENSE_TERMS_ACCEPTED)
elseif (VARIANT STREQUAL "OPEN")
  target_compile_definitions(common INTERFACE GPL_LICENSE_TERMS_ACCEPTED)
endif ()

set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED True)

# The tool version the CLI prints
set(APP_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(APP_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(APP_VERSION_PATCH ${PROJECT_VERSION_PATCH})

# set header version info
set(HEADER_VERSION_MAJOR 1)
set(HEADER_VERSION_MINOR 3)
set(HEADER_VERSION_PATCH 0)

# built time
string(TIMESTAMP currentTime "%Y%m%dT%H%M")
set(TIMESTAMP ${currentTime})

include_directories(${PROJECT_BINARY_DIR}/config)
configure_file(${PROJECT_SOURCE_DIR}/config/ImageBuilderConfig.h.in ${PROJECT_BINARY_DIR}/config/ImageBuilderConfig.h @ONLY)

# =============================================================================


# =============================================================================
# =========================  PROJECT LINKING  =================================
# =============================================================================

# add the binary tree to the search path for include files
# so that we will find ImageBuilderConfig.h
target_include_directories(image_builder PUBLIC ${IMAGE_BUILDER_INCLUDE_DIRS})

target_compile_definitions(image_builder PRIVATE VARIANT_${VARIANT})
target_link_libraries(image_builder PRIVATE common)
target_link_libraries(image_builder PUBLIC cargs)
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
  target_link_libraries(image_builder PUBLIC pthread) # Needed on Linux to compile crypto
endif ()

# =============================================================================
# Refreshing the binary shipped in bin/ is a deliberate step, not a side effect of a build
option(IMAGE_BUILDER_INSTALL_BIN "Copy the Release binary into bin/" OFF)

if (IMAGE_BUILDER_BUILD_TYPE STREQUAL "RELEASE" AND IMAGE_BUILDER_INSTALL_BIN)
  if (CMAKE_SYSTEM_NAME STREQUAL Linux)
    set(IMAGE_BUILDER_BIN_NAME image_builder_linux)
  elseif (CMAKE_SYSTEM_NAME STREQUAL Windows)
    set(IMAGE_BUILDER_BIN_NAME image_builder_windows.exe)
  endif ()

  if (IMAGE_BUILDER_BIN_NAME)
    add_custom_command(TARGET image_builder POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:image_builder>
                    ${CMAKE_CURRENT_SOURCE_DIR}/bin/${IMAGE_BUILDER_BIN_NAME}
            COMMENT "Copying the built binary to bin/${IMAGE_BUILDER_BIN_NAME}..."
    )
  endif ()
endif ()

# =============================================================================
# =========================  PROJECT TESTING  =================================
# =============================================================================

# The packages ship this file but not the test suite
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt)
  enable_testing()
  add_subdirectory(tests)
endif ()
