# Build/flash automation for the Waveshare RP2040-Zero SOC CAN bridge.
#
# Targets:
#   make install-cli  Install Arduino-Pico 4.5.2 + RP2040PIO_CAN 0.0.6
#   make qdefs        Build quill C headers from soc_bridge.py into ./src/qdefs
#   make compile      Build quill qdefs + stage + compile the sketch into ./bin
#   make flash        USB-upload the compiled firmware
#   make rp2040       compile + flash
#   make buildmon     compile + flash + monitor
#   make monitor      Serial console (picocom)
#   make clean        Remove build artifacts
#
# Board: Waveshare RP2040-Zero (Earle Philhower Arduino-Pico)
# Sketch requires Arduino-Pico 4.5.2+ and RP2040PIO_CAN 0.0.6.

# Anchor paths to this Makefile's directory so the targets work from any cwd.
MAKEFILE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
FEATHEROS_ROOT := $(abspath $(MAKEFILE_DIR)/../../../../../..)
QUILL_DIR := $(FEATHEROS_ROOT)/dev_tools/quill
QUILL_SRC := $(QUILL_DIR)/src
QUILL_DEF := $(abspath $(MAKEFILE_DIR)/../../../quill_defs/defs/soc_bridge.py)
QDEFS_DIR := $(MAKEFILE_DIR)/src/qdefs
# Quill needs Python 3.8+ (typing.Literal). Prefer 3.10 when `python3` is older.
PYTHON ?= $(shell command -v python3.10 >/dev/null && echo python3.10 || echo python3)

FQBN         := rp2040:rp2040:waveshare_rp2040_zero
CORE_VERSION := 4.5.2
LIB_VERSION  := 0.0.6
RP2040_URL   := https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json

BIN_DIR      := $(MAKEFILE_DIR)/bin
# Arduino requires the sketch folder name to match the .ino basename (soc_bridge/soc_bridge.ino).
SKETCH_NAME  := soc_bridge
SKETCH_SRC   := $(MAKEFILE_DIR)/src/$(SKETCH_NAME).ino
SKETCH_DIR   := $(BIN_DIR)/$(SKETCH_NAME)

# Optional override. Otherwise flash resolves a live /dev/ttyACM* at upload time.
# Note: arduino-cli rejects /dev/serial/by-id paths ("port not found").
PORT ?=
BAUD := 115200

.PHONY: install-cli qdefs sketch-dir compile flash rp2040 buildmon monitor clean help
.DEFAULT_GOAL := help

help:
	@echo "Targets:"
	@echo "  make install-cli  Install Arduino-Pico $(CORE_VERSION) + RP2040PIO_CAN $(LIB_VERSION)"
	@echo "  make qdefs        Generate C headers from soc_bridge.py into ./src/qdefs"
	@echo "  make compile      Build qdefs + compile $(SKETCH_NAME).ino for Waveshare RP2040-Zero"
	@echo "  make flash        Upload firmware over USB (serial or BOOTSEL/UF2)"
	@echo "  make rp2040       Compile and flash"
	@echo "  make buildmon     Compile, flash, then open serial console"
	@echo "  make monitor      Open serial console"
	@echo "  make clean        Remove ./bin"
	@echo ""
	@echo "Override port: make flash PORT=/dev/ttyACM1"

# Generate C headers from the soc_bridge quill definition into ./src/qdefs/.
qdefs:
	@rm -rf "$(QDEFS_DIR)"
	@mkdir -p "$(QDEFS_DIR)"
	@echo "Building quill definition: $(QUILL_DEF) -> $(QDEFS_DIR)"
	PYTHONPATH="$(QUILL_SRC)" $(PYTHON) -m quill "$(QUILL_DEF)" "$(QDEFS_DIR)" -l c

install-cli:
	@command -v arduino-cli >/dev/null || { \
		echo "arduino-cli not found; install it first (see teensy/Makefile install-cli)"; \
		exit 1; \
	}
	arduino-cli config add board_manager.additional_urls $(RP2040_URL) || true
	arduino-cli core update-index
	arduino-cli core install rp2040:rp2040@$(CORE_VERSION)
	arduino-cli lib install RP2040PIO_CAN@$(LIB_VERSION)
	@command -v picocom >/dev/null || echo "Optional: sudo apt install -y picocom  (for make monitor)"

# Stage a soc_bridge/ sketch under bin/: copy all of src/, but emit .c as .cpp so Arduino
# compiles them as C++ (Arduino.h / Wire / RP2040PIO_CAN require it). Sources on disk stay .c.
# The .ino lives at the sketch root (Arduino requirement), so rewrite its include to src/...
sketch-dir: clean qdefs
	@test -f "$(SKETCH_SRC)" || { echo "Missing sketch: $(SKETCH_SRC)"; exit 1; }
	@mkdir -p "$(BIN_DIR)"
	@rm -rf "$(SKETCH_DIR)"
	@mkdir -p "$(SKETCH_DIR)/src"
	@sed 's|#include "soc_helper.h"|#include "src/soc_helper.h"|' \
		"$(SKETCH_SRC)" > "$(SKETCH_DIR)/$(SKETCH_NAME).ino"
	@cd "$(MAKEFILE_DIR)/src" && find . -type f ! -name '*.ino' ! -name 'emulated.h' | while read -r f; do \
		dest="$(SKETCH_DIR)/src/$${f}"; \
		mkdir -p "$$(dirname "$$dest")"; \
		case "$$f" in \
			*.c) cp "$$f" "$${dest%.c}.cpp" ;; \
			*)   cp "$$f" "$$dest" ;; \
		esac; \
	done

# Compile the soc_bridge firmware. -DCOMPILE_FOR_RP2040 selects real hardware headers.
# Arduino-Pico uses build.extra_flags (not teensy's build.flags.defs).
compile: sketch-dir
	arduino-cli compile --fqbn $(FQBN) --clean --output-dir "$(BIN_DIR)" \
		--build-property "build.extra_flags=-DCOMPILE_FOR_RP2040 -DNDEBUG" \
		"$(SKETCH_DIR)"

# Resolve a Waveshare RP2040 serial node to a real /dev/tty* path.
# arduino-cli does not accept /dev/serial/by-id symlinks.
define RESOLVE_SERIAL_PORT
	if [ -n "$(PORT)" ]; then \
		readlink -f "$(PORT)" 2>/dev/null || echo "$(PORT)"; \
	else \
		for link in /dev/serial/by-id/usb-Waveshare* \
		            /dev/serial/by-id/usb-*RP2040* \
		            /dev/serial/by-id/usb-Raspberry_Pi_Pico*; do \
			[ -e "$$link" ] || continue; \
			readlink -f "$$link"; \
			exit 0; \
		done; \
		arduino-cli board list 2>/dev/null | awk \
			'/waveshare_rp2040_zero/ { print $$1; exit }'; \
	fi
endef

# Prefer live serial CDC; otherwise flash via UF2 when the board is in BOOTSEL.
flash:
	@PORT_RESOLVED=$$($(RESOLVE_SERIAL_PORT)); \
	if [ -n "$$PORT_RESOLVED" ] && [ -e "$$PORT_RESOLVED" ]; then \
		echo "Uploading via serial $$PORT_RESOLVED ..."; \
		arduino-cli upload --fqbn $(FQBN) -p "$$PORT_RESOLVED" -l serial \
			--input-dir "$(BIN_DIR)" "$(SKETCH_DIR)"; \
	elif [ -d "/media/$$USER/RPI-RP2" ] || [ -d "/run/media/$$USER/RPI-RP2" ] || \
	     arduino-cli board list 2>/dev/null | grep -q 'UF2_Board[[:space:]]\+uf2conv'; then \
		echo "Uploading via UF2 bootloader (BOOTSEL / RPI-RP2) ..."; \
		arduino-cli upload --fqbn $(FQBN) -p UF2_Board -l uf2conv \
			--input-dir "$(BIN_DIR)" "$(SKETCH_DIR)"; \
	else \
		echo "No RP2040 found (no serial CDC and no RPI-RP2 UF2 volume)."; \
		echo "Plug in the board, or hold BOOT while plugging in, then retry."; \
		arduino-cli board list || true; \
		exit 1; \
	fi

# Compile and flash the SOC CAN bridge for Waveshare RP2040-Zero.
rp2040: compile flash
buildmon: compile flash monitor

monitor:
	@trap 'exit 0' INT TERM; \
	while true; do \
		PORT_RESOLVED=$$($(RESOLVE_SERIAL_PORT)); \
		if [ -z "$$PORT_RESOLVED" ] || [ ! -e "$$PORT_RESOLVED" ]; then \
			for d in /dev/ttyACM*; do \
				[ -e "$$d" ] || continue; \
				if udevadm info -q property -n "$$d" 2>/dev/null | grep -qiE 'ID_MODEL=.*(RP2040|Pico|Waveshare)|ID_VENDOR_ID=2e8a'; then \
					PORT_RESOLVED=$$d; \
					break; \
				fi; \
			done; \
		fi; \
		if [ -n "$$PORT_RESOLVED" ] && [ -e "$$PORT_RESOLVED" ]; then \
			echo "Connecting to $$PORT_RESOLVED..."; \
			picocom --nolock -b $(BAUD) "$$PORT_RESOLVED"; \
			echo "Disconnected."; \
		fi; \
		sleep 2; \
	done

clean:
	rm -rf "$(BIN_DIR)"
