cmake_minimum_required(VERSION 3.10)

# Set the toolchain file BEFORE the project() call
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/.cmake/avr-gcc.cmake)

project(AVRDxBlink C)

# Configurable variables
set(MCU "avr128db28" CACHE STRING "Target Microcontroller")
set(F_CPU "20000000" CACHE STRING "Clock frequency in Hz")
set(DEVICE_PACK_DIR "/opt/toolchains/microchip/device-packs/AVRDx" CACHE STRING "Path to Device Packs")
set(PROGRAMMER "pickit4_updi" CACHE STRING "Programmer for avrdude") # often pickit4_updi is the right name for UPDI

# Include Device Pack
# -B instructs gcc to look for device-specific binaries/specs in this folder
# -I adds the device-specific headers
set(AVR_PACK_FLAGS "-B${DEVICE_PACK_DIR}/gcc/dev/${MCU} -I${DEVICE_PACK_DIR}/include")

# Clangd (and therefore eglot/lsp) often does not know about the AVR Device Pack.
# We define the corresponding macro manually so <avr/io.h> works.
string(TOUPPER "${MCU}" MCU_UPPER)
set(AVR_MACRO "-D__AVR_${MCU_UPPER}__ -D__AVR_DEVICE_NAME__=${MCU}")

# If the MCU is a modern AVR (Dx/Ex) or XMEGA, avr-gcc defines __AVR_XMEGA__ internally.
# Clangd needs this so <avr/fuse.h> uses NVM_FUSES_t instead of the old __fuse_t.
if(MCU MATCHES "^avr[0-9]+" OR MCU MATCHES "^atxmega")
    set(AVR_MACRO "${AVR_MACRO} -D__AVR_XMEGA__")
endif()

# Set Build Type (Default: Release)
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type: Debug or Release" FORCE)
endif()

# General Compiler Flags (for all build types)
# -ffunction-sections and -fdata-sections allow the linker to remove unused code
set(COMMON_FLAGS "-mmcu=${MCU} ${AVR_MACRO} -DF_CPU=${F_CPU}UL -Wall -Wstrict-prototypes -std=gnu99 ${AVR_PACK_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_C_FLAGS "${COMMON_FLAGS}")

# Specific flags for Debug and Release
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG")
set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG")

# Linker Flags: -Wl,--gc-sections removes everything that is not needed
set(CMAKE_EXE_LINKER_FLAGS "-mmcu=${MCU} ${AVR_PACK_FLAGS} -Wl,--gc-sections")

# Generate compile_commands.json for Emacs/clangd (code completion)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Source and Header files
file(GLOB_RECURSE SRC_FILES src/*.c)
list(FILTER SRC_FILES EXCLUDE REGEX "/[.]#") # Ignore Emacs lock files like .#main.c
include_directories(inc)

# Generate Executable (.elf)
add_executable(${PROJECT_NAME}.elf ${SRC_FILES})

# Display size after build (Hex file is obsolete thanks to ELF support)
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
    COMMAND avr-size --format=avr --mcu=${MCU} ${PROJECT_NAME}.elf
)

# --- Avrdude Programming Targets ---

# F6: Program Flash only
add_custom_target(flash
    COMMAND avrdude -c ${PROGRAMMER} -p ${MCU} -U flash:w:${PROJECT_NAME}.elf:e
    DEPENDS ${PROJECT_NAME}.elf
    COMMENT "Flashing program code (Flash)..."
)

# C-F6: Program Flash and EEPROM
add_custom_target(flash_eeprom
    COMMAND avrdude -c ${PROGRAMMER} -p ${MCU} -U flash:w:${PROJECT_NAME}.elf:e -U eeprom:w:${PROJECT_NAME}.elf:e
    DEPENDS ${PROJECT_NAME}.elf
    COMMENT "Flashing program code and EEPROM..."
)

# F7: Read Fuses
add_custom_target(read_fuses
    COMMAND avrdude -c ${PROGRAMMER} -p ${MCU} -U fuses:r:-:h
    COMMENT "Reading fuses..."
)

# C-F7: Program Fuses only
add_custom_target(flash_fuses
    COMMAND avrdude -c ${PROGRAMMER} -p ${MCU} -U fuses:w:${PROJECT_NAME}.elf:e
    DEPENDS ${PROJECT_NAME}.elf
    COMMENT "Programming fuses from ELF file..."
)
