cmake_minimum_required(VERSION 3.13)

if("$ENV{DEVKITPRO}" STREQUAL "")
        message(FATAL_ERROR "DEVKITPRO is not set. Please export DEVKITPRO to your devkitPro root.")
endif()

set(DEVKITPRO_ROOT "$ENV{DEVKITPRO}")
set(DEVKITPRO_CMAKE "${DEVKITPRO_ROOT}/cmake/NDS.cmake")

if(NOT EXISTS "${DEVKITPRO_CMAKE}")
        message(FATAL_ERROR "Could not find NDS.cmake at ${DEVKITPRO_CMAKE}. Check DEVKITPRO.")
endif()

# Set target SD directory for generated NDS ROM
set(OUTPUT_DIRECTORY "/mnt/6d0c4735-6031-46df-bf0a-b44eeea7dcca")

# Ensure re-configure runs in DS context as well.
set(CMAKE_SYSTEM_NAME NintendoDS CACHE INTERNAL "Target system" FORCE)
set(CMAKE_SYSTEM_PROCESSOR armv5te CACHE INTERNAL "Target CPU" FORCE)

include("${DEVKITPRO_CMAKE}")

project(snakePlus LANGUAGES C CXX ASM)

option(ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF)

# Export compile_commands.json for tooling (clangd, IntelliSense, static analysis).
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)

if(ENABLE_CLANG_TIDY)
        find_program(CLANG_TIDY_EXE NAMES clang-tidy)
        if(NOT CLANG_TIDY_EXE)
                message(FATAL_ERROR "ENABLE_CLANG_TIDY is ON, but clang-tidy was not found")
        endif()

        set(CMAKE_C_CLANG_TIDY
                ${CLANG_TIDY_EXE}
                "--header-filter=^(include|source)/"
                --extra-arg=--target=arm-none-eabi
        )
        set(CMAKE_CXX_CLANG_TIDY
                ${CLANG_TIDY_EXE}
                "--header-filter=^(include|source)/"
                --extra-arg=--target=arm-none-eabi
        )
endif()

set(TARGET_NAME ${PROJECT_NAME})

set(GAME_TITLE "Snake Plus")
set(GAME_SUBTITLE1 "Snake with a twist")
set(GAME_SUBTITLE2 "Phillip Kuehne")
set(GAME_ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.bmp)

file(GLOB_RECURSE SRC_C CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.c)
file(GLOB_RECURSE SRC_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp)
file(GLOB_RECURSE SRC_ASM CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.s)

set(TILEMAP_PNG ${CMAKE_CURRENT_SOURCE_DIR}/gfx/tilemap.png)
set(TILEMAP_GRIT ${CMAKE_CURRENT_SOURCE_DIR}/gfx/tilemap.grit)
set(TILEMAP_S ${CMAKE_CURRENT_BINARY_DIR}/tilemap.s)
set(TILEMAP_H ${CMAKE_CURRENT_BINARY_DIR}/tilemap.h)

add_custom_command(
        OUTPUT ${TILEMAP_S} ${TILEMAP_H}
        COMMAND ${GRIT_EXE} ${TILEMAP_PNG} -fts -o${CMAKE_CURRENT_BINARY_DIR}/tilemap
        DEPENDS ${TILEMAP_PNG} ${TILEMAP_GRIT}
        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
        COMMENT "Generating tilemap assets with grit"
        VERBATIM
)

add_custom_target(tilemap_assets DEPENDS ${TILEMAP_S} ${TILEMAP_H})

file(GLOB AUDIO_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/sounds/*.*)

set(SOUNDBANK_BIN ${CMAKE_CURRENT_BINARY_DIR}/soundbank.bin)
set(SOUNDBANK_H ${CMAKE_CURRENT_BINARY_DIR}/soundbank.h)
set(SOUNDBANK_BIN_S ${CMAKE_CURRENT_BINARY_DIR}/soundbank_bin.S)
set(SOUNDBANK_BIN_H ${CMAKE_CURRENT_BINARY_DIR}/soundbank_bin.h)

find_program(MMUTIL_EXE NAMES mmutil HINTS "${DEVKITPRO_ROOT}/tools/bin")
find_program(BIN2S_EXE NAMES bin2s HINTS "${DEVKITPRO_ROOT}/tools/bin")

if(NOT MMUTIL_EXE)
        message(FATAL_ERROR "Could not find mmutil")
endif()
if(NOT BIN2S_EXE)
        message(FATAL_ERROR "Could not find bin2s")
endif()

add_custom_command(
        OUTPUT ${SOUNDBANK_BIN} ${SOUNDBANK_H} ${SOUNDBANK_BIN_S} ${SOUNDBANK_BIN_H}
        COMMAND ${MMUTIL_EXE} ${AUDIO_FILES} -d -o${SOUNDBANK_BIN} -h${SOUNDBANK_H}
        COMMAND /bin/sh -c "\"${BIN2S_EXE}\" -a 4 -H \"${SOUNDBANK_BIN_H}\" \"${SOUNDBANK_BIN}\" > \"${SOUNDBANK_BIN_S}\""
        DEPENDS ${AUDIO_FILES}
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
        COMMENT "Generating maxmod soundbank and embedded binary sources"
        VERBATIM
)

add_custom_target(soundbank_assets DEPENDS ${SOUNDBANK_BIN} ${SOUNDBANK_H} ${SOUNDBANK_BIN_S} ${SOUNDBANK_BIN_H})

add_executable(${TARGET_NAME}
        ${SRC_C}
        ${SRC_CPP}
        ${SRC_ASM}
        ${TILEMAP_S}
        ${SOUNDBANK_BIN_S}
)

add_dependencies(${TARGET_NAME}
        tilemap_assets
        soundbank_assets
)

target_include_directories(${TARGET_NAME} PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/include
        ${CMAKE_CURRENT_BINARY_DIR}
        ${DEVKITPRO_ROOT}/libnds/include
        ${DEVKITPRO_ROOT}/portlibs/nds/include
        ${DEVKITPRO_ROOT}/portlibs/armv5te/include
)

target_link_directories(${TARGET_NAME} PRIVATE
        ${DEVKITPRO_ROOT}/calico/lib
        ${DEVKITPRO_ROOT}/libnds/lib
        ${DEVKITPRO_ROOT}/portlibs/nds/lib
        ${DEVKITPRO_ROOT}/portlibs/armv5te/lib
)

target_compile_definitions(${TARGET_NAME} PRIVATE
        ARM9
        __NDS__
)

target_compile_options(${TARGET_NAME} PRIVATE
        -g
        -Wall
        -Wextra
        -Wpedantic
        -Wconversion
        -Wsign-conversion
        -Wshadow
        -Wformat=2
        -Wundef
        -Wnull-dereference
        -Wimplicit-fallthrough
        -O3
        -marm
        -mthumb-interwork
        -march=armv5te
        -mtune=arm946e-s
        -fno-rtti
        -fno-exceptions
)

set(DS9_SPECS_FLAG "-specs=${DEVKITPRO_ROOT}/calico/share/ds9.specs")
string(FIND "${CMAKE_EXE_LINKER_FLAGS}" "${DS9_SPECS_FLAG}" _has_ds9_specs)
if(_has_ds9_specs EQUAL -1)
        set(EXTRA_DS9_SPECS_FLAG "${DS9_SPECS_FLAG}")
endif()

target_link_options(${TARGET_NAME} PRIVATE
        ${EXTRA_DS9_SPECS_FLAG}
        -marm
        -mthumb-interwork
        -march=armv5te
        -mtune=arm946e-s
        -Wl,-Map,${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.map
)

target_link_libraries(${TARGET_NAME} PRIVATE
        mm9
)

set_target_properties(${TARGET_NAME} PROPERTIES
        OUTPUT_NAME ${PROJECT_NAME}
        SUFFIX ".elf"
        RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

set(NDS_OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.nds)

find_program(NDSTOOL_EXE NAMES ndstool HINTS "${DEVKITPRO_ROOT}/tools/bin")
if(NOT NDSTOOL_EXE)
        message(FATAL_ERROR "Could not find ndstool")
endif()

set(ARM7_DEFAULT ${DEVKITPRO_ROOT}/calico/bin/ds7_maine.elf)
if(NOT EXISTS ${ARM7_DEFAULT})
        message(FATAL_ERROR "Could not find default ARM7 binary: ${ARM7_DEFAULT}")
endif()

add_custom_command(
        OUTPUT ${NDS_OUTPUT}
        COMMAND ${NDSTOOL_EXE} -c ${NDS_OUTPUT} -9 $<TARGET_FILE:${TARGET_NAME}> -7 ${ARM7_DEFAULT} -b ${GAME_ICON} "${GAME_TITLE}\;${GAME_SUBTITLE1}\;${GAME_SUBTITLE2}"
        DEPENDS ${TARGET_NAME} ${GAME_ICON}
        COMMENT "Building NDS ROM image"
        VERBATIM
)

add_custom_target(${TARGET_NAME}_nds ALL DEPENDS ${NDS_OUTPUT})

add_custom_target(run
        COMMAND melonDS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.nds
        DEPENDS ${TARGET_NAME}_nds
        USES_TERMINAL
        COMMENT "Running ROM in melonDS"
)

add_custom_target(install_sd
        COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.nds ${OUTPUT_DIRECTORY}
        DEPENDS ${TARGET_NAME}_nds
        COMMENT "Copying ROM to SD card"
)
