cmake_minimum_required(VERSION 3.21)
cmake_policy(SET CMP0123 NEW)

# Create a symbolic link from ${base_name} in the binary directory
# to the corresponding path in the source directory.
function(link_to_source base_name)
    # Get OS dependent path to use in `execute_process`
    if(CMAKE_HOST_WIN32)
        # mklink is an internal command of cmd.exe it can only work with \
        string(REPLACE "/" "\\" link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
        string(REPLACE "/" "\\" target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
    else()
        set(link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
        set(target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
    endif()

    if(NOT EXISTS ${link})
        if(CMAKE_HOST_UNIX)
            set(command ln -s ${target} ${link})
        else()
            if(IS_DIRECTORY ${target})
                set(command cmd.exe /c mklink /j ${link} ${target})
            else()
                set(command cmd.exe /c mklink /h ${link} ${target})
            endif()
        endif()

        execute_process(COMMAND ${command}
            RESULT_VARIABLE result
            ERROR_VARIABLE output)

        if(NOT ${result} EQUAL 0)
            message(FATAL_ERROR "Could not create symbolic link for: ${target} --> ${output}")
        endif()
    endif()
endfunction(link_to_source)

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_VERSION "5")
set(CMAKE_SYSTEM_PROCESSOR ARM)

# Optimization level: -O0 (none), -O1, -O2, -O3, -Os (size), -Oz (clang size)
set(OPTIMIZATION_LEVEL "-O2" CACHE STRING "Compiler optimization level")

if(C_COMPILER_ID STREQUAL ARMCLANG)

    find_program(ARM_CC NAMES armclang REQUIRED)
    find_program(ARM_CXX NAMES armclang REQUIRED)
    find_program(ARM_ASM NAMES armclang REQUIRED)
    find_program(ARM_AR NAMES armar REQUIRED)
    find_program(ARM_LINK NAMES armlink REQUIRED)
    find_program(ARM_FROMELF NAMES fromelf REQUIRED)

    set(CMAKE_C_COMPILER ${ARM_CC} CACHE STRING "")
    set(CMAKE_CXX_COMPILER ${ARM_CXX} CACHE STRING "")
    set(CMAKE_ASM_COMPILER ${ARM_ASM} CACHE STRING "")
    set(CMAKE_LINKER ${ARM_LINK} CACHE STRING "")

    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

    set(LINK_MCU_CORE "Cortex-M33")

    set(C_FLAGS "--target=arm-arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard -c -fno-rtti -funsigned-char -fshort-enums -fshort-wchar -D__MICROLIB -gdwarf-3 ${OPTIMIZATION_LEVEL} -flto -fno-function-sections -Wno-packed -Wno-missing-variable-declarations -Wno-missing-prototypes -Wno-missing-noreturn -Wno-sign-conversion -Wno-nonportable-include-path -Wno-reserved-id-macro -Wno-unused-macros -Wno-documentation-unknown-command -Wno-documentation -Wno-license-management -Wno-parentheses-equality -Wconditional-uninitialized -Wunused-variable -D__MICROLIB -DMBEDTLS_CONFIG_FILE=mbedtls_config.h -Ic:/Keil_v5/ARM/ARMCLANG/include")
    set(CMAKE_C_FLAGS "-xc -std=c99  ${C_FLAGS}")
    set(CMAKE_CXX_FLAGS ${C_FLAGS})
    set(CMAKE_ASM_FLAGS "--target=arm-arm-none-eabi -mcpu=cortex-m33")
    set(CMAKE_EXE_LINKER_FLAGS "--lto")
    set(CMAKE_EXECUTABLE_SUFFIX ".axf")
endif()

# ---------------------------------------------------------------------
if(C_COMPILER_ID STREQUAL GCC)
    # Prevent CMake from testing the toolchain
    set(CMAKE_C_COMPILER_FORCED 1)
    set(CMAKE_CXX_COMPILER_FORCED 1)

    find_program(ARM_CC NAMES arm-none-eabi-gcc REQUIRED)
    find_program(ARM_CXX NAMES arm-none-eabi-gcc REQUIRED)
    find_program(ARM_OBJCOPY NAMES arm-none-eabi-objcopy REQUIRED)
    find_program(ARM_GCC_AR NAMES arm-none-eabi-gcc-ar REQUIRED)
    find_program(ARM_GCC_RANLIB NAMES arm-none-eabi-gcc-ranlib REQUIRED)

    set(CMAKE_C_COMPILER ${ARM_CC} CACHE STRING "")
    set(CMAKE_CXX_COMPILER ${ARM_CXX} CACHE STRING "")
    set(CMAKE_AR ${ARM_GCC_AR} CACHE FILEPATH "")
    set(CMAKE_C_COMPILER_AR ${ARM_GCC_AR} CACHE FILEPATH "")
    set(CMAKE_RANLIB ${ARM_GCC_RANLIB} CACHE FILEPATH "")
    set(CMAKE_C_COMPILER_RANLIB ${ARM_GCC_RANLIB} CACHE FILEPATH "")

    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

    set(LINK_MCU_CORE "Cortex-M33")

    set(STACK_SIZE 0x4000)
    set(HEAP_SIZE 0x20000)

    set(C_FLAGS "-mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 --specs=nano.specs --specs=nosys.specs -Wall -Wno-format -Wno-return-type -Wno-unused-function -Wmaybe-uninitialized -Wunused-variable -fdata-sections -ffunction-sections -fno-builtin -fshort-enums -funsigned-char -mthumb -std=gnu11 ${OPTIMIZATION_LEVEL} -flto=auto -DMBEDTLS_CONFIG_FILE=mbedtls_config.h")
    set(CMAKE_C_FLAGS ${C_FLAGS})
    set(CMAKE_CXX_FLAGS ${C_FLAGS})
    set(CMAKE_ASM_FLAGS "-mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16")
endif()

project(m3331bsp)

set(RETARGET "${CMAKE_CURRENT_LIST_DIR}/Library/StdDriver/src/retarget.c")
set(STARTUP "${CMAKE_CURRENT_LIST_DIR}/Library/Device/Nuvoton/m3331/Source/startup_m3331.c" "${CMAKE_CURRENT_LIST_DIR}/Library/Device/Nuvoton/m3331/Source/system_m3331.c")

function(create_project PRJ)
    add_executable(${PRJ} ${ARGN} ${RETARGET} ${STARTUP})
    target_link_libraries(${PRJ} standard_driver)

    if(C_COMPILER_ID STREQUAL ARMCLANG)
        target_link_options(${PRJ}
            PUBLIC
            --cpu=Cortex-M33
            --library_type=microlib
            --lto
            --scatter ${BSP_DIR}/Library/Device/Nuvoton/m3331/Config/m3331_ac6.sct
            --entry Reset_Handler
            --map
            --datacompressor=off
            --info=inline
            --summary_stderr
            --info totals
            --map
            --load_addr_map_info
            --xref
            --callgraph
            --symbols
        )

        add_custom_command(TARGET ${PRJ} POST_BUILD
            COMMAND ${ARM_FROMELF} --bin --output ${PRJ}.bin ${PRJ}.elf
            VERBATIM
        )
    endif()

    if(C_COMPILER_ID STREQUAL GCC)
        target_link_options(${PRJ}
            PUBLIC
            -T ${BSP_DIR}/Library/Device/Nuvoton/m3331/Config/m3331_gcc.ld
            -Xlinker
            --gc-sections
            -Wl,-Map=${PRJ}.map
            -gdwarf-3
            -flto=auto
        )

        add_custom_command(TARGET ${PRJ} POST_BUILD
            COMMAND ${ARM_OBJCOPY} -O binary ${PRJ} ${PRJ}.bin
            VERBATIM
        )
    endif()
endfunction()


function(create_project_no_startup PRJ)
    add_executable(${PRJ} ${ARGN})
    target_link_libraries(${PRJ} standard_driver)

    if(C_COMPILER_ID STREQUAL ARMCLANG)
        target_link_options(${PRJ}
            PUBLIC
            --cpu=Cortex-M33
            --library_type=microlib
            --lto
            --scatter ${BSP_DIR}/Library/Device/Nuvoton/m3331/Config/m3331_ac6.sct
            --entry Reset_Handler
            --strict
            --map
            --datacompressor=off
            --info=inline
            --entry Reset_Handler
            --summary_stderr
            --info totals
            --map
            --load_addr_map_info
            --xref
            --callgraph
            --symbols
        )

        add_custom_command(TARGET ${PRJ} POST_BUILD
            COMMAND ${ARM_FROMELF} --bin --output ${PRJ}.bin ${PRJ}.elf
            VERBATIM
        )
    endif()

    if(C_COMPILER_ID STREQUAL GCC)
        target_link_options(${PRJ}
            PUBLIC
            -T ${BSP_DIR}/Library/Device/Nuvoton/m3331/Config/m3331_gcc.ld
            -Xlinker
            --gc-sections
            -Wl,-Map=${PRJ}.map
            -gdwarf-3
            -flto=auto
        )

        add_custom_command(TARGET ${PRJ} POST_BUILD
            COMMAND ${ARM_OBJCOPY} -O binary ${PRJ} ${PRJ}.bin
            VERBATIM
        )
    endif()
endfunction()

set(BSP_DIR ${CMAKE_CURRENT_LIST_DIR})

include_directories(${TOOLCHAIN_PATH}/include)
include_directories(${BSP_DIR}/Library/CMSIS/Core/Include)
include_directories(${BSP_DIR}/Library/Device/Nuvoton/m3331/Include)
include_directories(${BSP_DIR}/Library/StdDriver/inc)
include_directories(${BSP_DIR}/Library/NuMaker/xmodem)

# Third-party libraries
include_directories(${BSP_DIR}/ThirdParty/FatFs/source)
include_directories(${BSP_DIR}/ThirdParty/LibMAD/inc)


# Build library
add_subdirectory(${BSP_DIR}/Library/StdDriver/src)
add_subdirectory(${BSP_DIR}/Library/NuMaker/xmodem)
add_subdirectory(${BSP_DIR}/Library/UsbHostLib)
add_subdirectory(${BSP_DIR}/ThirdParty/FatFs/source)
add_subdirectory(${BSP_DIR}/ThirdParty/LibMAD)

# Build sample code
add_subdirectory(${BSP_DIR}/SampleCode/NuMaker-M3334KIv2)
add_subdirectory(${BSP_DIR}/SampleCode/StdDriver)
add_subdirectory(${BSP_DIR}/SampleCode/ISP)
add_subdirectory(${BSP_DIR}/SampleCode/PowerManagement)
# create_project(Hard_Fault_Sample ${BSP_DIR}/SampleCode/Hard_Fault_Sample/main.c)
create_project(Template ${BSP_DIR}/SampleCode/Template/main.c)
