#ifndef ADBMS_DRIVER_H
#define ADBMS_DRIVER_H

#include "config_ADBMS6830.h"
#include <stdint.h>

#define ERROR_TIME_THRESH 150 // ms

typedef enum : uint16_t {
    ADBMS_NO_ERROR = 0,
    ADBMS_OVERTEMP,
    ADBMS_UNDERTEMP,
    ADBMS_OVERVOLT,
    ADBMS_UNDERVOLT,
    ADBMS_OPENWIRE,
    ADBMS_INTERNAL_BMS_TIMEOUT,       // BMS went to sleep too many times
    ADBMS_INTERNAL_BMS_CHECKSUM_FAIL, // BMS CRC failed too many times
    ADBMS_INTERNAL_BMS_OVERTEMP,
    ADBMS_INTERNAL_BMS_FAULT,
    NUM_ERROR_KINDS
} ADBMS_Status;

static const char* ADBMS_Status_Names[NUM_ERROR_KINDS] = {
    "ADBMS_NO_ERROR",
    "ADBMS_OVERTEMP",
    "ADBMS_UNDERTEMP",
    "ADBMS_OVERVOLT",
    "ADBMS_UNDERVOLT",
    "ADBMS_OPENWIRE",
    "ADBMS_INTERNAL_BMS_TIMEOUT",
    "ADBMS_INTERNAL_BMS_CHECKSUM_FAIL",
    "ADBMS_INTERNAL_BMS_OVERTEMP",
    "ADBMS_INTERNAL_BMS_FAULT"
};

static inline const char* ADBMS_Status_ToString(ADBMS_Status status) {
    return (status < NUM_ERROR_KINDS) ? ADBMS_Status_Names[status] : "UNKNOWN";
}

typedef struct {
    ADBMS_Status status : 16;
    int16_t bms_id : 16; // the BMS that caused the error, zero if no error, -1 if unknown BMS
} ADBMS_DetailedStatus;

typedef struct {
    uint8_t data[4];
    uint32_t errors_since;
} SlaveErrorData;

struct ADBMS6830_Internal_Status {
    uint16_t CS_FLT : 16; // ADC fault - mismatch between S- and C-ADC
    uint16_t : 3;
    uint16_t CCTS : 13;   // Conversion counter
    uint16_t SMED : 1;    // S-ADC multiple trim error (uncorrectable)
    uint16_t SED : 1;     // S-ADC single trim error (correctable)
    uint16_t CMED : 1;    // C-ADC multiple trim error (uncorrectable)
    uint16_t CED : 1;     // C-ADC single trim error (correctable)
    uint16_t VD_UV : 1;   // 3V digital supply undervoltage
    uint16_t VD_OV : 1;   // 3V digital supply overvoltage
    uint16_t VA_UV : 1;   // 5V analog supply undervoltage
    uint16_t VA_OV : 1;   // 5V analog supply overvoltage
    uint16_t OSCCHK : 1;  // Oscillator check
    uint16_t TMODCHK : 1; // Test mode check
    uint16_t THSD : 1;    // Thermal shutdown
    uint16_t SLEEP : 1;   // Sleep mode previously entered
    uint16_t SPIFLT : 1;  // SPI fault
    uint16_t COMPARE : 1; // Comparasion between S- and C-ADC active
    uint16_t VDE : 1;     // Supply voltage error
    uint16_t VDEL : 1;    // Latent supply voltage error
};

#define MAXIMUM_CELL_VOLTAGES 16
#define MAXIMUM_AUX_VOLTAGES  10
#define MAXIMUM_GPIO          10

typedef struct {
    int16_t cellVoltages[MAXIMUM_CELL_VOLTAGES];
    int16_t auxVoltages[MAXIMUM_AUX_VOLTAGES];
    uint64_t bmsID;

    struct ADBMS6830_Internal_Status status;
    int16_t internalDieTemp;
    uint16_t analogSupplyVoltage;
    uint16_t digitalSupplyVoltage;
    uint16_t sumOfCellMeasurements;
    uint16_t refVoltage;

    uint32_t overVoltage;  // Bitfield of overvoltage flags
    uint32_t underVoltage; //      "      undervoltage  "

} Cell_Module;

extern uint32_t error_sources; // Bitfield of error sources
extern SlaveErrorData error_data[NUM_ERROR_KINDS];
extern Cell_Module modules[N_BMS];

[[gnu::nonnull]] ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi);

ADBMS_DetailedStatus AMS_Idle_Loop();

#undef MAXIMUM_CELL_VOLTAGES
#undef MAXIMUM_AUX_VOLTAGES
#undef MAXIMUM_GPIO

#endif // ADBMS_DRIVER_H
