# FRST Firmware Build Helpers
#
# Copyright (c) 2024-2025 Jon Sharp
# SPDX-License-Identifier: MIT

BOARD_ROOT ?= $(CURDIR)

# Model definitions: board spec, build dir, serial port
MODEL1_BOARD := esp32s3_devkitm/esp32s3/procpu
MODEL1_BUILD := build_model1
MODEL1_PORT  := /dev/ttyUSB0

MODEL4_BOARD := frst_model_4/esp32s3/procpu
MODEL4_BUILD := build_model4
MODEL4_PORT  := /dev/ttyACM0

# Default single-model targets use Model 4
BOARD ?= $(MODEL4_BOARD)
BUILD_DIR ?= $(MODEL4_BUILD)
PORT ?= $(MODEL4_PORT)

# LittleFS partition offset (must match DTS)
LFS_OFFSET := 0x256000

# Find esptool.py from the HAL Espressif module
ESPRESSIF_HAL := $(shell west list -f '{abspath}' hal_espressif 2>/dev/null)
ESPTOOL := python3 $(ESPRESSIF_HAL)/tools/esptool_py/esptool.py

.PHONY: all build flash clean lfs lfs-flash lfs-clean help \
	build-model1 build-model4 build-all \
	flash-model1 flash-model4 flash-both \
	clean-model1 clean-model4 clean-all \
	test test-unit test-interop test-sim

# Test build dirs
TESTS_BUILD ?= build_tests
SIM_BUILD   ?= build_sim

help:
	@echo "FRST Firmware Build Targets:"
	@echo ""
	@echo "  Per-model:"
	@echo "    make build-model1   - Build Model 1 (DevKitM) → $(MODEL1_BUILD)/"
	@echo "    make build-model4   - Build Model 4           → $(MODEL4_BUILD)/"
	@echo "    make flash-model1   - Flash Model 1 → $(MODEL1_PORT)"
	@echo "    make flash-model4   - Flash Model 4 → $(MODEL4_PORT)"
	@echo "    make clean-model1   - Clean Model 1 build"
	@echo "    make clean-model4   - Clean Model 4 build"
	@echo ""
	@echo "  Both models:"
	@echo "    make build-all      - Build both models"
	@echo "    make flash-both     - Flash both models"
	@echo "    make clean-all      - Clean all build dirs"
	@echo ""
	@echo "  Legacy (defaults to Model 4):"
	@echo "    make build          - Build firmware"
	@echo "    make flash          - Flash firmware"
	@echo "    make clean          - Clean build dir"
	@echo ""
	@echo "  LittleFS:"
	@echo "    make lfs            - Build LittleFS image"
	@echo "    make lfs-flash      - Flash LittleFS image (PORT=$(PORT))"
	@echo ""
	@echo "  Tests:"
	@echo "    make test           - Self-contained tiers (ztest + lrzsz interop)"
	@echo "    make test-unit      - ztest unit/integration suite (native_sim)"
	@echo "    make test-interop   - ZMODEM engine vs reference lrzsz (sz/rz)"
	@echo "    make test-sim       - pty-driven full-firmware tests (needs $(SIM_BUILD))"
	@echo ""
	@echo "  Ports: Model1=$(MODEL1_PORT)  Model4=$(MODEL4_PORT)"

all: build-all lfs

# --- Per-model targets ---

build-model1:
	west build -b $(MODEL1_BOARD) --sysbuild -d $(MODEL1_BUILD) -- -DBOARD_ROOT=$(BOARD_ROOT)

build-model4:
	west build -b $(MODEL4_BOARD) --sysbuild -d $(MODEL4_BUILD) -- -DBOARD_ROOT=$(BOARD_ROOT)

build-all: build-model4 build-model1

flash-model1:
	west flash --skip-rebuild -d $(MODEL1_BUILD) --esp-device $(MODEL1_PORT)

flash-model4:
	west flash --skip-rebuild -d $(MODEL4_BUILD) --esp-device $(MODEL4_PORT)

flash-both: flash-model4 flash-model1

clean-model1:
	rm -rf $(MODEL1_BUILD)/

clean-model4:
	rm -rf $(MODEL4_BUILD)/

clean-all: clean-model4 clean-model1

# --- Legacy single-model targets (default: Model 4) ---

build:
	west build -b $(BOARD) --sysbuild -d $(BUILD_DIR) -- -DBOARD_ROOT=$(BOARD_ROOT)

flash:
	west flash --skip-rebuild -d $(BUILD_DIR) --esp-device $(PORT)

clean:
	rm -rf $(BUILD_DIR)/

# --- LittleFS image ---

lfs: $(MODEL4_BUILD)/littlefs.bin

$(MODEL4_BUILD)/littlefs.bin: scripts/rc/*/* scripts/build_lfs_image.py
	python3 scripts/build_lfs_image.py --output $@

lfs-flash: $(MODEL4_BUILD)/littlefs.bin
	$(ESPTOOL) --chip esp32s3 --port $(PORT) write_flash $(LFS_OFFSET) $<

lfs-clean:
	rm -f $(MODEL4_BUILD)/littlefs.bin

# --- Tests ---
#
# `make test` runs the self-contained, deterministic tiers (no hardware, no
# external services): the ztest unit/integration suite and the ZMODEM<->lrzsz
# interop harness.  `test-sim` (pty-driven full-firmware) is separate — it
# needs $(SIM_BUILD) built first and some cases need a live aetherd/mock.

test: test-unit test-interop
	@echo "== self-contained tests passed (unit + interop) =="

# ztest unit/integration suite on native_sim (src/*.c under test).
test-unit:
	west build -b native_sim/native/64 -d $(TESTS_BUILD) tests
	./$(TESTS_BUILD)/zephyr/zephyr.exe

# ZMODEM engine vs the reference lrzsz sz/rz (SKIPs if lrzsz not installed).
test-interop:
	$(MAKE) -C tests/zmodem run

# pty-driven full-firmware integration (see tests/sim/README.md; build the sim
# into $(SIM_BUILD) first).  Each script SKIPs cleanly when its external
# dependency (e.g. a live aetherd) is absent.
test-sim:
	@for t in tests/sim/test_*.py; do \
		echo "== $$t =="; \
		python3 $$t $(SIM_BUILD)/zephyr/zephyr.exe || exit 1; \
	done

# Combined targets
flash-all: flash-both lfs-flash
