cmake_minimum_required(VERSION 3.22)

set(SRC_ROOT ${CMAKE_SOURCE_DIR}/../../../../../src/)
set(COMMON ${SRC_ROOT}/common)
set(UPDATE ${SRC_ROOT}/cyclone_boot)
set(THIRD_PARTY ${CMAKE_SOURCE_DIR}/../../../../../third_party)
set(HAL_DRIVERS ${THIRD_PARTY}/st/drivers/stm32f4xx_hal_driver/src)
set(BSP ${THIRD_PARTY}/st/boards/stm32f4xx_nucleo_144)
set(TOOLS ${CMAKE_SOURCE_DIR}/../../../../../tools)
set(SCRIPTS ${CMAKE_SOURCE_DIR}/../../../../../scripts)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)

# Define the build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Debug")
endif()

# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})

# Enable CMake support for ASM and C languages
enable_language(C ASM)

# Create an executable object type
add_executable(${CMAKE_PROJECT_NAME})

target_sources(${CMAKE_PROJECT_NAME} PRIVATE
        ${COMMON}/cpu_endian.c
        ${COMMON}/os_port_none.c
        ${COMMON}/date_time.c
        ${COMMON}/str.c
        ${COMMON}/path.c
        ${COMMON}/resource_manager.c

        ${UPDATE}/core/mailbox.c

        ${THIRD_PARTY}/st/boards2/stm32h750b_discovery/stm32h750b_discovery.c
        ${THIRD_PARTY}/st/boards2/stm32h750b_discovery/stm32h750b_discovery_bus.c
        ${THIRD_PARTY}/st/boards2/stm32h750b_discovery/stm32h750b_discovery_lcd.c
        ${THIRD_PARTY}/st/boards2/stm32h750b_discovery/stm32h750b_discovery_sdram.c
        ${THIRD_PARTY}/st/boards2/utilities/lcd/stm32_lcd.c
        ${THIRD_PARTY}/st/boards2/components/ft5336/ft5336.c
        ${THIRD_PARTY}/st/boards2/components/ft5336/ft5336_reg.c
        ${THIRD_PARTY}/st/boards2/components/mt48lc4m32b2/mt48lc4m32b2.c

        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_dma2d.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_ltdc.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_sdram.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_ll_fmc.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_qspi.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_cortex.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_rcc.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_rcc_ex.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_flash.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_flash_ex.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_gpio.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_hsem.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_dma.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_dma_ex.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_mdma.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_pwr.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_pwr_ex.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_i2c.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_i2c_ex.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_exti.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_tim.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_tim_ex.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_uart.c
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/src/stm32h7xx_hal_uart_ex.c

        ${CMAKE_SOURCE_DIR}/cmake/startup_stm32h750xbhx.s
        ${CMAKE_SOURCE_DIR}/cmake/syscalls.c
        ${CMAKE_SOURCE_DIR}/cmake/sysmem.c
        ${CMAKE_SOURCE_DIR}/src/system_stm32h7xx.c
        ${CMAKE_SOURCE_DIR}/src/stm32h7xx_hal_msp.c
        ${CMAKE_SOURCE_DIR}/src/stm32h7xx_it.c
        ${CMAKE_SOURCE_DIR}/src/main.c
        ${CMAKE_SOURCE_DIR}/src/board.c
        ${CMAKE_SOURCE_DIR}/src/debug.c
)

# Build an application that never confirms itself, to exercise the bootloader recovery:
#   cmake --preset Debug -DAPP_CONFIRM_UPDATE=OFF
option(APP_CONFIRM_UPDATE "Confirm the running image to the bootloader" ON)
if(NOT APP_CONFIRM_UPDATE)
    target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE APP_NO_CONFIRM)
endif()

target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
        ${CMAKE_SOURCE_DIR}/src
        ${THIRD_PARTY}/cmsis/include
        ${THIRD_PARTY}/st/devices/stm32h7xx
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/inc
        ${THIRD_PARTY}/st/drivers/stm32h7xx_hal_driver/inc/legacy
        ${THIRD_PARTY}/st/boards2/stm32h750b_discovery
        ${THIRD_PARTY}/st/boards2/utilities/
        ${THIRD_PARTY}/st/boards2/utilities/lcd
        ${THIRD_PARTY}/st/boards2/utilities/Fonts
        ${THIRD_PARTY}/st/boards2/components
        ${THIRD_PARTY}/st/boards2/components/common
        ${THIRD_PARTY}/st/boards2/components/ft5336
        ${THIRD_PARTY}/st/boards2/components/mt48lc4m32b2
        ${THIRD_PARTY}/st/boards2/components/rk043fn48h
        ${COMMON}
        ${UPDATE}
        ${UPDATE}/modules
)

target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
    STM32H750xx
    APPLICATION_ADDRESS=0x90000000U
    USE_HAL_DRIVER
    USE_STM32H750B_DISCO
    USE_PWR_LDO_SUPPLY
    __USE_C99_MATH

    $<$<CONFIG:Debug>:DEBUG>
)


# Add the map file to the list of files to be removed with 'clean' target
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES ADDITIONAL_CLEAN_FILES ${CMAKE_PROJECT_NAME}.map)

# Convert .elf to .bin
add_custom_command(TARGET app_blinky POST_BUILD
        COMMAND ${CMAKE_OBJCOPY} -O binary
        ${CMAKE_CURRENT_BINARY_DIR}/app_blinky.elf
        ${CMAKE_CURRENT_BINARY_DIR}/app_blinky.bin
        COMMENT "Generating binary file from ELF"
)

# Copy the generated .bin file to ../
set(VERSION_HEADER "${CMAKE_SOURCE_DIR}/src/version.h")
# Read the contents of the header
file(READ "${VERSION_HEADER}" VERSION_HEADER_CONTENT)
# Extract the version string from e.g., #define APP_VERSION "1.0.0"
string(REGEX MATCH "#define APP_VERSION_STRING \"([0-9\\.]+)\"" _ UNUSED ${VERSION_HEADER_CONTENT})
set(APP_VERSION_RAW "${CMAKE_MATCH_1}")
string(REPLACE "." "_" APP_VERSION "${APP_VERSION_RAW}")

configure_file(${VERSION_HEADER} ${CMAKE_CURRENT_BINARY_DIR}/version.h COPYONLY)

message("Detected application version: ${APP_VERSION_RAW}")

# Build with another version without editing src/version.h (e.g. the v2.0.0 update of the README):
#   cmake --preset Debug -DAPP_VERSION_OVERRIDE=2.0.0 && cmake --build --preset Debug
set(APP_VERSION_OVERRIDE "" CACHE STRING "Application version overriding src/version.h (x.y.z)")
if(APP_VERSION_OVERRIDE)
    set(APP_VERSION_RAW "${APP_VERSION_OVERRIDE}")
    string(REPLACE "." "_" APP_VERSION "${APP_VERSION_RAW}")
    string(REPLACE "." ";" APP_VERSION_PARTS "${APP_VERSION_RAW}")
    list(GET APP_VERSION_PARTS 0 APP_VERSION_MAJOR)
    list(GET APP_VERSION_PARTS 1 APP_VERSION_MINOR)
    list(GET APP_VERSION_PARTS 2 APP_VERSION_REV)
    target_compile_definitions(app_blinky PRIVATE
        APP_MAJOR_VERSION=${APP_VERSION_MAJOR}
        APP_MINOR_VERSION=${APP_VERSION_MINOR}
        APP_REV_NUMBER=${APP_VERSION_REV}
        APP_VERSION_STRING="${APP_VERSION_RAW}")
    message("Application version overridden: ${APP_VERSION_RAW}")
endif()
add_custom_command(TARGET app_blinky POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy
        ${CMAKE_CURRENT_BINARY_DIR}/app_blinky.bin
        ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}.bin
        COMMENT "Copying iap_demo.bin to parent directory"
)

if(WIN32)
    add_custom_command(TARGET app_blinky POST_BUILD
            COMMAND ${TOOLS}/ImageBuilder/bin/image_builder_windows.exe
            --input ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}.bin
            --output ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}_bootable.img.bin
            --runtime-sign-algo rsa-sha256
            --runtime-sign-key ${SCRIPTS}/keys/my_rsa_private_key.pem
            --firmware-version ${APP_VERSION_RAW}
            --vtor-align

            COMMAND ${TOOLS}/ImageBuilder/bin/image_builder_windows.exe
            --input ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}.bin
            --output ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}_update.img
            --runtime-sign-algo rsa-sha256
            --runtime-sign-key ${SCRIPTS}/keys/my_rsa_private_key.pem
            --firmware-version ${APP_VERSION_RAW}
            --enc-algo aes-cbc
            --enc-key-ascii aa3ff7d43cc015682c7dfd00de9379e7
            --sign-algo rsa-sha256
            --sign-key ${SCRIPTS}/keys/my_rsa_private_key.pem
            --vtor-align

            COMMENT "Generating app_blinky images using ImageBuilder"
    )
else()
    add_custom_command(TARGET app_blinky POST_BUILD
            COMMAND ${TOOLS}/ImageBuilder/bin/image_builder_linux
            --input ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}.bin
            --output ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}_bootable.img.bin
            --runtime-sign-algo rsa-sha256
            --runtime-sign-key ${SCRIPTS}/keys/my_rsa_private_key.pem
            --firmware-version ${APP_VERSION_RAW}
            --vtor-align

            COMMAND ${TOOLS}/ImageBuilder/bin/image_builder_linux
            --input ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}.bin
            --output ${CMAKE_CURRENT_SOURCE_DIR}/../app_blinky_${APP_VERSION}_update.img
            --runtime-sign-algo rsa-sha256
            --runtime-sign-key ${SCRIPTS}/keys/my_rsa_private_key.pem
            --firmware-version ${APP_VERSION_RAW}
            --enc-algo aes-cbc
            --enc-key-ascii aa3ff7d43cc015682c7dfd00de9379e7
            --sign-algo rsa-sha256
            --sign-key ${SCRIPTS}/keys/my_rsa_private_key.pem
            --vtor-align

            COMMENT "Generating app_blinky images using ImageBuilder"
    )
endif()

# Refresh the footprint report on a Release build. A Debug build leaves the committed
# numbers alone, so a report always describes the configuration customers ship.
if (CMAKE_BUILD_TYPE STREQUAL "Release")
	add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
		COMMAND python3 ${CMAKE_SOURCE_DIR}/footprint.py
		--map "${CMAKE_CURRENT_BINARY_DIR}/app_blinky.map"
		--ld "${CMAKE_SOURCE_DIR}/cmake/QSPIflash_InternalSRAM.ld"
		--elf "${CMAKE_CURRENT_BINARY_DIR}/app_blinky.elf"
		--output-summary "${CMAKE_SOURCE_DIR}/reports/footprint_summary.json"
		--build-type Release
		--folders ${SRC_ROOT}/cyclone_boot ${SRC_ROOT}/cyclone_crypto ${SRC_ROOT}/cyclone_tcp ${SRC_ROOT}/common
	)
endif ()
