# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)

# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
    set(USERHOME $ENV{USERPROFILE})
else()
    set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.3.0)
set(toolchainVersion 15_2_Rel1)
set(picotoolVersion 2.3.0)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
    include(${picoVscode})
endif()
# ====================================================================================

set(PICO_BOARD pico_w CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

project(RGBClock C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()


# -----------------------------------------------------------------------------
# Flash layout
#
#   0x000000 - 0x007FFF   32kiB bootloader
#   0x008000 - 0x009FFF    8kiB bootloader metadata
#   0x00A000 - 0x0FCFFF  972kiB Slot A
#   0x0FD000 - 0x1EFFFF  972kiB Slot B
#   0x1F0000 - 0x1FFFFF   64kiB configuration data
#
# Slot XIP addresses are flash offsets + 0x10000000.
# -----------------------------------------------------------------------------

set(RGBCLOCK_SLOT_SIZE 972k)
set(RGBCLOCK_SLOT_A_XIP_BASE 0x1000A000)
set(RGBCLOCK_SLOT_B_XIP_BASE 0x100FD000)


# -----------------------------------------------------------------------------
# Generate SDK 2.3 linker-script overrides for the two OTA application slots.
#
# section_boot2.incl is intentionally empty: boot2 will eventually live in our
# bootloader and must not be included in either application slot.
# -----------------------------------------------------------------------------

set(SLOT_A_LINKER_DIR ${CMAKE_CURRENT_BINARY_DIR}/linker_slot_a)
set(SLOT_B_LINKER_DIR ${CMAKE_CURRENT_BINARY_DIR}/linker_slot_b)

file(MAKE_DIRECTORY ${SLOT_A_LINKER_DIR})
file(MAKE_DIRECTORY ${SLOT_B_LINKER_DIR})

file(WRITE ${SLOT_A_LINKER_DIR}/memory_flash.incl
"MEMORY
{
    FLASH(rx) : ORIGIN = ${RGBCLOCK_SLOT_A_XIP_BASE}, LENGTH = ${RGBCLOCK_SLOT_SIZE}
}
")

file(WRITE ${SLOT_B_LINKER_DIR}/memory_flash.incl
"MEMORY
{
    FLASH(rx) : ORIGIN = ${RGBCLOCK_SLOT_B_XIP_BASE}, LENGTH = ${RGBCLOCK_SLOT_SIZE}
}
")

file(WRITE ${SLOT_A_LINKER_DIR}/section_boot2.incl "")
file(WRITE ${SLOT_B_LINKER_DIR}/section_boot2.incl "")

set(BOOTLOADER_LINKER_DIR ${CMAKE_CURRENT_BINARY_DIR}/linker_bootloader)

file(MAKE_DIRECTORY ${BOOTLOADER_LINKER_DIR})

file(WRITE ${BOOTLOADER_LINKER_DIR}/memory_flash.incl
"MEMORY
{
    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 32k
}
")

# -----------------------------------------------------------------------------
# Common source files
# -----------------------------------------------------------------------------

set(RGBCLOCK_SOURCES
    RGBClock.cpp
    ClockFace.cpp
    WS2812.cpp
    http_server.cpp
    Config.cpp
    minmea.c
    GNSS.cpp
    NTP.cpp
    LDR.cpp
    WiFi.cpp
    EEPROM.cpp
    dhcpserver.c
    dnsserver.c
    Timezone.cpp
    OTA.cpp
    BootMetadata.cpp
    Log.cpp
)


# -----------------------------------------------------------------------------
# Embedded HTTP content
# -----------------------------------------------------------------------------

pico_add_library(rgbclock_http_content NOFLAG)

pico_set_lwip_httpd_content(rgbclock_http_content INTERFACE
    ${CMAKE_CURRENT_LIST_DIR}/web/index.html
    ${CMAKE_CURRENT_LIST_DIR}/web/gnss-debug.html
    ${CMAKE_CURRENT_LIST_DIR}/web/wifi-scan.html
    ${CMAKE_CURRENT_LIST_DIR}/web/log.html
)


# -----------------------------------------------------------------------------
# Common setup for all three RGB Clock builds
# -----------------------------------------------------------------------------

function(configure_rgbclock_target TARGET)

    add_executable(${TARGET} ${RGBCLOCK_SOURCES})

    pico_generate_pio_header(${TARGET} ${CMAKE_CURRENT_LIST_DIR}/WS2812.pio)

    pico_set_program_name(${TARGET} "RGBClock")
    pico_set_program_version(${TARGET} "0.1")

    pico_enable_stdio_uart(${TARGET} 0)
    pico_enable_stdio_usb(${TARGET} 1)

    target_link_libraries(${TARGET}
        pico_stdlib
        pico_cyw43_arch_lwip_threadsafe_background
        pico_aon_timer
        pico_lwip_sntp
        pico_sync
        hardware_adc
        hardware_pio
        pico_lwip_http
        pico_lwip_mdns
        rgbclock_http_content
        hardware_timer
        hardware_watchdog
    )

    target_include_directories(${TARGET} PRIVATE
        ${CMAKE_CURRENT_LIST_DIR}
        ${PICO_LWIP_CONTRIB_PATH}/apps/httpd
    )

endfunction()


# -----------------------------------------------------------------------------
# Normal development build
#
# This remains based at 0x10000000 and produces the same ELF/BIN/UF2 files
# you've been using up to now.
# -----------------------------------------------------------------------------

configure_rgbclock_target(RGBClock)
pico_add_extra_outputs(RGBClock)


# -----------------------------------------------------------------------------
# OTA Slot A build
# -----------------------------------------------------------------------------

configure_rgbclock_target(RGBClock_A)

pico_add_linker_script_override_path(
    RGBClock_A
    ${SLOT_A_LINKER_DIR}
    FILES memory_flash.incl section_boot2.incl
)

pico_add_bin_output(RGBClock_A)


# -----------------------------------------------------------------------------
# OTA Slot B build
# -----------------------------------------------------------------------------

configure_rgbclock_target(RGBClock_B)

pico_add_linker_script_override_path(
    RGBClock_B
    ${SLOT_B_LINKER_DIR}
    FILES memory_flash.incl section_boot2.incl
)

pico_add_bin_output(RGBClock_B)


# -----------------------------------------------------------------------------
# Display executable sizes after linking
# -----------------------------------------------------------------------------

find_program(ARM_SIZE arm-none-eabi-size REQUIRED)

add_custom_command(TARGET RGBClock POST_BUILD
    COMMAND ${ARM_SIZE} $<TARGET_FILE:RGBClock>
    VERBATIM
)

add_custom_command(TARGET RGBClock_A POST_BUILD
    COMMAND ${ARM_SIZE} $<TARGET_FILE:RGBClock_A>
    VERBATIM
)

add_custom_command(TARGET RGBClock_B POST_BUILD
    COMMAND ${ARM_SIZE} $<TARGET_FILE:RGBClock_B>
    VERBATIM
)


# -----------------------------------------------------------------------------
# OTA package
#
# make_ota_package.py creates:
#
#   16-byte header
#   Slot A binary
#   Slot B binary
#
# The header contains:
#   uint32_t magic
#   uint32_t image_length
#   uint32_t slot_a_crc
#   uint32_t slot_b_crc
# -----------------------------------------------------------------------------

find_package(Python3 REQUIRED COMPONENTS Interpreter)

set(OTA_PACKAGE ${CMAKE_CURRENT_BINARY_DIR}/RGBClock-update.bin)
set(SLOT_A_BIN ${CMAKE_CURRENT_BINARY_DIR}/RGBClock_A.bin)
set(SLOT_B_BIN ${CMAKE_CURRENT_BINARY_DIR}/RGBClock_B.bin)

add_custom_command(
    OUTPUT ${OTA_PACKAGE}

    COMMAND ${Python3_EXECUTABLE}
        ${CMAKE_CURRENT_LIST_DIR}/tools/make_ota_package.py
        ${SLOT_A_BIN}
        ${SLOT_B_BIN}
        ${OTA_PACKAGE}

    DEPENDS
        RGBClock_A
        RGBClock_B
        ${SLOT_A_BIN}
        ${SLOT_B_BIN}
        ${CMAKE_CURRENT_LIST_DIR}/tools/make_ota_package.py

    COMMENT "Building RGB Clock OTA firmware package"
    VERBATIM
)

add_custom_target(RGBClock_OTA ALL
    DEPENDS ${OTA_PACKAGE}
)

# -----------------------------------------------------------------------------
# Bootloader
# -----------------------------------------------------------------------------

add_executable(RGBClockBoot
    Bootloader.cpp
    BootMetadata.cpp
)

pico_set_program_name(RGBClockBoot "RGBClockBoot")
pico_set_program_version(RGBClockBoot "0.1")

pico_enable_stdio_uart(RGBClockBoot 0)
pico_enable_stdio_usb(RGBClockBoot 0)

target_include_directories(RGBClockBoot PRIVATE
    ${CMAKE_CURRENT_LIST_DIR}
)

target_link_libraries(RGBClockBoot
    pico_stdlib
    pico_flash
    hardware_flash
    hardware_watchdog
)

pico_add_linker_script_override_path(
    RGBClockBoot
    ${BOOTLOADER_LINKER_DIR}
    FILES memory_flash.incl
)

pico_add_extra_outputs(RGBClockBoot)

set(FULL_BIN ${CMAKE_CURRENT_BINARY_DIR}/RGBClock-full.bin)
set(FULL_UF2 ${CMAKE_CURRENT_BINARY_DIR}/RGBClock-full.uf2)
set(BOOT_BIN ${CMAKE_CURRENT_BINARY_DIR}/RGBClockBoot.bin)

add_custom_command(
    OUTPUT ${FULL_BIN}

    COMMAND ${Python3_EXECUTABLE}
        ${CMAKE_CURRENT_LIST_DIR}/tools/make_full_image.py
        ${BOOT_BIN}
        ${SLOT_A_BIN}
        ${FULL_BIN}

    DEPENDS
        RGBClockBoot
        RGBClock_A
        ${CMAKE_CURRENT_LIST_DIR}/tools/make_full_image.py

    COMMENT "Building combined bootloader + Slot A image"
    VERBATIM
)

add_custom_command(
    OUTPUT ${FULL_UF2}

    COMMAND picotool uf2 convert
        --quiet
        ${FULL_BIN}
        ${FULL_UF2}
        -o 0x10000000
        --family rp2040

    DEPENDS ${FULL_BIN}

    COMMENT "Building combined RGB Clock UF2"
    VERBATIM
)

add_custom_target(RGBClock_Full ALL
    DEPENDS ${FULL_UF2}
)
pico_enable_stdio_usb(RGBClockBoot 1)
