#pragma once

#include "Arduino.h"

struct FaultStatusRegisters {
  uint32_t HFSR;
  uint32_t CFSR;
  uint32_t MMFAR;
  uint32_t BFAR;

  uint32_t DFSR;
  uint32_t AFSR;
  uint32_t SHCSR;
};

enum class FaultType { HardFault, MemManage, BusFault, UsageFault };

struct FlashDump {
  FaultType type;
  uint32_t stacked_registers[8];
  FaultStatusRegisters fsr;
};

struct FlashDumpInfo {
  uint8_t _zero; // Used to check that the storage area has been initialized
  uint32_t n_dumps;
};

constexpr uint32_t FLASH_DUMP_ADDR_BASE = 0;
constexpr uint32_t FLASH_DUMP_ADDR_INFO = FLASH_DUMP_ADDR_BASE + 0;
constexpr uint32_t FLASH_DUMP_ADDR_DUMPS =
    FLASH_DUMP_ADDR_INFO + sizeof(FlashDump);

void flash_dump_write_fault(FaultType fault_type, const uint32_t *stack,
                            const FaultStatusRegisters *fsr);
/**
 * Read the FlashDumpInfo (and create it if it hasn't been initialized yet).
 */
const FlashDumpInfo *flash_dump_get_info();
const FlashDump *flash_dump_get_fault(uint32_t n);

void uart_wait_for_txrdy();
size_t uart_write(uint8_t c);
size_t uart_print(const char *str);
size_t uart_print_hex(uint32_t x);

void print_dumped_faults(bool in_irq = false);
void print_stacked_registers(const uint32_t *stack, bool in_irq = false);
void print_fault_registers(const FaultStatusRegisters *fsr,
                           bool in_irq = false);

FaultStatusRegisters get_current_fsr();

const char *get_fault_type_name(FaultType type);
void fault_handler(uint32_t *stack_addr, FaultType fault_type, const int *leds,
                   unsigned n_leds);

void inline busy_wait(size_t iterations) {
  for (size_t i = 0; i < iterations; i++) {
    // Does nothing, but ensures the compiler doesn't optimize the loop away.
    __ASM("" ::: "memory");
  }
}