#ifndef FAULT_HANDLER_H
#define FAULT_HANDLER_H

#include <stdint.h>
#include <stdbool.h>

/*
 * fault_handler.h — STM32F7 fault detection and recovery (Issue #565)
 *
 * Features:
 *   - HardFault / BusFault / UsageFault / MemManage vector hooks with full
 *     Cortex-M7 register dump (R0-R3, LR, PC, xPSR, CFSR, HFSR, MMFAR, BFAR)
 *   - .noinit SRAM ring: fault frame captured and magic-tagged, survives
 *     NVIC_SystemReset(); persisted to flash on the subsequent boot
 *   - MPU Region 0 stack-guard (32 bytes at __stack_end, no-access) → MemManage
 *     fault detected as FAULT_STACK_OVF
 *   - Brownout detect via RCC_CSR_BORRSTF on boot → FAULT_BROWNOUT
 *   - Persistent fault log: last 8 entries × 64 bytes in flash sector 7
 *     at 0x08060000 (below the PID store at 0x0807FFC0)
 *   - JLINK_TLM_FAULT_LOG (0x85): 20-byte summary sent via JLink on boot
 *     and on JLINK_CMD_FAULT_LOG_GET (0x0C) request
 *   - LED blink codes on LED2 (PC14, active-low) for 10 s after recovery:
 *       HARDFAULT   = 3 fast blinks (100 ms)
 *       WATCHDOG    = 2 slow blinks (300 ms)
 *       BROWNOUT    = 1 long blink  (500 ms)
 *       STACK_OVF   = 4 fast blinks (100 ms)
 *       BUS_FAULT   = alternating 3+1
 *       USAGE_FAULT = 2 fast blinks
 *   - Auto-recovery: fault → .noinit capture → NVIC_SystemReset()
 *     On next boot fault_handler_init() re-runs safely: persists, prints, blinks
 *
 * Flash layout within sector 7 (0x08060000, 128 KB):
 *   Slot 0-7: 0x08060000 – 0x080601FF  (8 × 64 bytes = 512 bytes fault log)
 *   PID store: 0x0807FFC0 – 0x0807FFFF  (64 bytes, managed by pid_flash.c)
 */

/* ---- Fault types ---- */
typedef enum {
    FAULT_NONE        = 0x00,
    FAULT_HARDFAULT   = 0x01,  /* HardFault escalation */
    FAULT_WATCHDOG    = 0x02,  /* IWDG timeout reset */
    FAULT_BROWNOUT    = 0x03,  /* Brown-out reset (BOR) */
    FAULT_STACK_OVF   = 0x04,  /* MPU stack guard MemManage */
    FAULT_BUS_FAULT   = 0x05,  /* BusFault */
    FAULT_USAGE_FAULT = 0x06,  /* UsageFault */
    FAULT_MEM_FAULT   = 0x07,  /* MemManageFault (non-stack-guard) */
    FAULT_ASSERT      = 0x08,  /* Software assertion */
} FaultType;

/* ---- Flash fault log constants ---- */
#define FAULT_LOG_MAX_ENTRIES   8u
#define FAULT_LOG_MAGIC         0xFADE5A01u
#define FAULT_LOG_BASE_ADDR     0x08060000UL   /* start of flash sector 7 */
#define FAULT_LOG_ENTRY_SIZE    64u            /* bytes per entry */

/* ---- Flash fault log entry (64 bytes, packed) ---- */
typedef struct __attribute__((packed)) {
    uint32_t magic;         /* FAULT_LOG_MAGIC when valid */
    uint8_t  fault_type;    /* FaultType */
    uint8_t  reset_count;   /* lifetime reset counter */
    uint16_t _pad0;
    uint32_t timestamp_ms;  /* HAL_GetTick() at reset (0 if pre-tick) */
    uint32_t pc;            /* faulting instruction address */
    uint32_t lr;            /* link register at fault */
    uint32_t r0;
    uint32_t r1;
    uint32_t r2;
    uint32_t r3;
    uint32_t cfsr;          /* SCB->CFSR: combined fault status register */
    uint32_t hfsr;          /* SCB->HFSR: hard fault status register */
    uint32_t mmfar;         /* SCB->MMFAR: memory manage fault address */
    uint32_t bfar;          /* SCB->BFAR: bus fault address */
    uint32_t sp;            /* stack pointer value at fault */
    uint8_t  _pad1[4];      /* pad to 64 bytes */
} fault_log_entry_t;  /* 64 bytes */

/*
 * fault_handler_init() — call early in main(), before safety_init().
 * 1. Increments reset counter (.noinit SRAM).
 * 2. Checks .noinit SRAM for a pending fault capture; if found: persists to
 *    flash, prints CDC register dump, starts LED blink code.
 * 3. Detects brownout via RCC_CSR_BORRSTF; logs if detected.
 * 4. Clears RCC reset flags.
 * 5. Installs MPU Region 0 stack guard.
 * 6. Enables MemManage, BusFault, UsageFault (SCB->SHCSR).
 */
void fault_handler_init(void);

/*
 * fault_mpu_guard_init() — configure MPU Region 0 as a 32-byte no-access
 * guard at __stack_end (bottom of main stack).  Generates MemManage on
 * stack overflow.  Called automatically by fault_handler_init().
 */
void fault_mpu_guard_init(void);

/*
 * fault_get_last_type() — most recent fault type from flash log, or FAULT_NONE.
 */
FaultType fault_get_last_type(void);

/*
 * fault_log_read(idx, out) — read flash slot 0..7.
 * Returns false if slot empty or idx out of range.
 */
bool fault_log_read(uint8_t idx, fault_log_entry_t *out);

/*
 * fault_log_get_count() — number of valid (occupied) log slots, 0-8.
 */
uint8_t fault_log_get_count(void);

/*
 * fault_log_clear() — erase fault log, restore PID if previously saved.
 * Erases all of sector 7 (~1 s stall). Do not call while armed.
 */
void fault_log_clear(void);

/*
 * fault_assert(file, line) — software fault at runtime; captures return
 * address, writes SRAM magic, triggers NVIC_SystemReset().
 * Use via FAULT_ASSERT(cond) macro below.
 */
void fault_assert_impl(const char *file, int line);

#define FAULT_ASSERT(cond) \
    do { if (!(cond)) fault_assert_impl(__FILE__, __LINE__); } while (0)

/*
 * fault_led_tick(now_ms) — drive LED2 blink code from main loop (1 ms).
 * Self-disables after 10 s so it doesn't interfere with normal LED state.
 */
void fault_led_tick(uint32_t now_ms);

/* C-level fault dispatch (called from naked asm stubs; not for direct use) */
void fault_hard_c(uint32_t *frame);
void fault_mem_c(uint32_t *frame);
void fault_bus_c(uint32_t *frame);
void fault_usage_c(uint32_t *frame);

#endif /* FAULT_HANDLER_H */
