PROJECT       := OriginCar
BUILD         ?= debug
FRAMEWORK_DIR ?= ../../stm32/stm32f407-gcc-framework
FRAMEWORK_DIR := $(abspath $(FRAMEWORK_DIR))

CC      := arm-none-eabi-gcc
OBJCOPY := arm-none-eabi-objcopy
SIZE    := arm-none-eabi-size
NM      := arm-none-eabi-nm
OPENOCD := openocd

MCU := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
DEFS := -DSTM32F40_41xxx -DUSE_STDPERIPH_DRIVER -DARM_MATH_CM4 \
        -D__FPU_PRESENT=1 -D__FPU_USED=1 -DHSE_VALUE=8000000U
INCLUDES := \
  -IGCC_COMPAT/include \
  -IUSER -ICORE -IFWLIB/inc \
  -ISYSTEM/delay -ISYSTEM/sys -ISYSTEM/usart \
  -IHARDWARE -IHARDWARE/MPU6050 -IBALANCE \
  -IFreeRTOS/include -IFreeRTOS/portable/GCC/ARM_CM4F

APP_SRC := \
  USER/main.c USER/stm32f4xx_it.c USER/system_stm32f4xx.c \
  HARDWARE/adc.c HARDWARE/can.c HARDWARE/encoder.c HARDWARE/key.c \
  HARDWARE/motor.c HARDWARE/oled.c HARDWARE/pstwo.c HARDWARE/timer.c \
  HARDWARE/usartx.c HARDWARE/MPU6050/MPU6050.c HARDWARE/MPU6050/I2C.c \
  SYSTEM/delay/delay.c SYSTEM/usart/usart.c \
  BALANCE/system.c BALANCE/balance.c BALANCE/robot_select_init.c BALANCE/show.c

FWLIB_SRC := \
  FWLIB/src/misc.c FWLIB/src/stm32f4xx_gpio.c FWLIB/src/stm32f4xx_rcc.c \
  FWLIB/src/stm32f4xx_syscfg.c FWLIB/src/stm32f4xx_usart.c \
  FWLIB/src/stm32f4xx_adc.c FWLIB/src/stm32f4xx_can.c \
  FWLIB/src/stm32f4xx_tim.c FWLIB/src/stm32f4xx_pwr.c \
  FWLIB/src/stm32f4xx_dma.c

FREERTOS_SRC := \
  FreeRTOS/croutine.c FreeRTOS/event_groups.c FreeRTOS/list.c \
  FreeRTOS/queue.c FreeRTOS/tasks.c FreeRTOS/timers.c \
  FreeRTOS/portable/GCC/ARM_CM4F/port.c \
  FreeRTOS/portable/MemMang/heap_4.c

C_SRC := $(APP_SRC) $(FWLIB_SRC) $(FREERTOS_SRC) \
         GCC_COMPAT/runtime.c GCC_COMPAT/sys_gcc.c
UPPER_C_SRC := HARDWARE/LED.C
BUILD_DIR := build/$(BUILD)
C_OBJS := $(addprefix $(BUILD_DIR)/,$(C_SRC:.c=.o))
UPPER_C_OBJS := $(addprefix $(BUILD_DIR)/,$(UPPER_C_SRC:.C=.o))
STARTUP_OBJ := $(BUILD_DIR)/startup_stm32f407xx_gcc.o
OBJS := $(C_OBJS) $(UPPER_C_OBJS) $(STARTUP_OBJ)
DEPS := $(OBJS:.o=.d)

ifeq ($(BUILD),release)
  OPT_FLAGS := -Os -g0 -DNDEBUG
else
  OPT_FLAGS := -O0 -g3 -DDEBUG -fno-omit-frame-pointer
endif

CFLAGS := $(MCU) $(DEFS) $(INCLUDES) $(OPT_FLAGS) \
  -std=gnu11 -ffreestanding -ffunction-sections -fdata-sections -fno-common \
  -Wall -Wextra -Wshadow -Wno-unused-parameter -Wno-sign-compare \
  -MMD -MP
ASFLAGS := $(MCU) $(DEFS) $(OPT_FLAGS) -x assembler-with-cpp -Wa,--gdwarf-2
LDSCRIPT := $(FRAMEWORK_DIR)/STM32F407VGTX_FLASH.ld
LDFLAGS := $(MCU) -nostdlib -T$(LDSCRIPT) \
  -Wl,-Map=$(BUILD_DIR)/$(PROJECT).map,--cref,--gc-sections \
  -Wl,--start-group -lgcc -Wl,--end-group

ELF := $(BUILD_DIR)/$(PROJECT).elf
HEX := $(BUILD_DIR)/$(PROJECT).hex
BIN := $(BUILD_DIR)/$(PROJECT).bin

.PHONY: all debug release clean flash debug-server linkscope-info check-tools

all: check-tools $(ELF) $(HEX) $(BIN)
	$(SIZE) $(ELF)

debug:
	$(MAKE) BUILD=debug all

release:
	$(MAKE) BUILD=release all

check-tools:
	@command -v $(CC) >/dev/null || { echo "Missing $(CC)"; exit 1; }
	@command -v $(OPENOCD) >/dev/null || { echo "Missing $(OPENOCD)"; exit 1; }
	@test -f $(LDSCRIPT) || { echo "Framework not found: $(FRAMEWORK_DIR)"; exit 1; }

$(ELF): $(OBJS)
	@mkdir -p $(dir $@)
	$(CC) $(OBJS) $(LDFLAGS) -o $@

$(HEX): $(ELF)
	$(OBJCOPY) -O ihex $< $@

$(BIN): $(ELF)
	$(OBJCOPY) -O binary $< $@

$(BUILD_DIR)/%.o: %.c
	@mkdir -p $(dir $@)
	$(CC) $(CFLAGS) -c $< -o $@

$(BUILD_DIR)/%.o: %.C
	@mkdir -p $(dir $@)
	$(CC) $(CFLAGS) -x c -c $< -o $@

$(STARTUP_OBJ): $(FRAMEWORK_DIR)/CORE/startup_stm32f407xx_gcc.s
	@mkdir -p $(dir $@)
	$(CC) $(ASFLAGS) -c $< -o $@

flash: all
	$(OPENOCD) -f openocd.cfg -c "program $(ELF) verify reset exit"

debug-server:
	$(OPENOCD) -f openocd.cfg

linkscope-info: $(ELF)
	@echo "ELF: $(abspath $(ELF))"
	@echo "GDB server: localhost:3333 (run 'make debug-server')"
	@echo "Example observable globals:"
	@$(NM) -S --size-sort $(ELF) | grep -E ' (Car_Mode|RC_Velocity|Velocity_KP|Velocity_KI|Move_X|Move_Y|Move_Z)$$' || true

clean:
	rm -rf build

-include $(DEPS)
