/*
 * can_schema.h
 *
 *  Created on: Sep 16, 2025
 *      Author: herli
 */

#ifndef INC_CAN_SCHEMA_H_
#define INC_CAN_SCHEMA_H_

#ifndef CAN_SCHEMA_H
#define CAN_SCHEMA_H

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

typedef enum {
    CAN_DIR_RX = 0,   // incoming from bus -> decode into RAM
    CAN_DIR_TX = 1    // outgoing to bus   -> encode from RAM
} CanDirection;

typedef enum {
    CAN_ENDIAN_INTEL = 0,    // DBC "Intel" (little-endian bit numbering): bit 0 = LSB of byte 0
    CAN_ENDIAN_MOTOROLA = 1  // DBC "Motorola" (big-endian bit numbering)
} CanEndian;

typedef enum {
    CAN_SYM_UX = 0,   // unsigned raw
    CAN_SYM_SX = 1,   // signed two's complement raw
    CAN_SYM_FLOAT = 2 // (kept for future use)
} CanRawType;

typedef enum {
    NOMINAL = 0,
    INVERTED = 1
} CanInversion;

/* Shared scale (optional): if a symbol sets factor==0 && offset==0 and scale!=NULL,
 * the effective factor/offset comes from this struct. If symbol.factor/offset is non-zero,
 * it overrides the scale.
 */
typedef struct CanScaleDef {
    const char *unit;  // informational, may be NULL
    float       factor;
    float       offset;
} CanScaleDef;

typedef enum {
    CAN_STORE_FLOAT = 0,   // ptr -> float (uses factor/offset)
    CAN_STORE_U32,         // ptr -> uint32_t (raw integer)
    CAN_STORE_S32,         // ptr -> int32_t  (raw integer)
    CAN_STORE_U16,         // ptr -> uint16_t (raw integer)
    CAN_STORE_S16,         // ptr -> int16_t  (raw integer)
    CAN_STORE_U8,          // ptr -> uint8_t  (raw integer)
    CAN_STORE_S8           // ptr -> int8_t   (raw integer)
} CanStorage;

struct CanMessageDef;  // fwd

// User-provided TX function that actually hands frames to HAL
typedef bool (*CanTxFn)(uint16_t id, const uint8_t data[8], uint8_t dlc);

// Optional per-message RX handler (called AFTER any symbol decode)
typedef void (*CanRxHandler)(
    const struct CanMessageDef *msg,
    const uint8_t in_data[8],
    CanTxFn tx
);


typedef struct {
    const char    *name;
    uint8_t        start_bit, bit_len;
    CanEndian      endian;
    CanRawType     raw_type;   // UX / SX (bit interpretation)
    float          factor, offset;
    void          *ptr;
    const CanScaleDef *scale;
    CanStorage     storage;    // NEW: how to read/write *ptr
    CanInversion inverted;

} CanSymbolDef;

typedef struct CanMessageDef {
    uint16_t            can_id;        // 11-bit standard ID
    CanDirection        dir;           // RX or TX
    uint8_t             dlc;           // 0..8
    const char         *name;          // message name
    const CanSymbolDef *symbols;       // array of symbols (NULL if template-only)
    uint8_t             symbol_count;  // number of symbols

    // Optional template payload (used for TX): copied first, then symbols packed on top.
    const uint8_t      *tx_template;   // 0..8 bytes in flash (NULL if none)
    uint8_t             send_on_boot;  // if 1 and TX, manager will send at init

    // Optional RX handler (for custom behaviors like address-based replies)
    CanRxHandler        rx_handler;
} CanMessageDef;

typedef struct {
    uint16_t           addr;     // request address
    void              *ptr;      // pointer to physical value (usually float*)
    CanRawType         raw_type; // how to quantize (unsigned/signed)
    const CanScaleDef *scale;    // scaling used for inverse transform
    CanStorage         storage;   // NEW: how to read ptr (FLOAT/U16/U32/…)
} CanAddressEntry;

/* Registry access (implemented in can_db.c) */
const CanMessageDef* can_db_find(uint16_t can_id, CanDirection dir);
const CanMessageDef* can_db_all(size_t *count);

/* Bit-pack engine (implemented in can_encode.c) */
void can_encode_message(const CanMessageDef *msg, uint8_t out_data[8]);
void can_decode_message(const CanMessageDef *msg, const uint8_t in_data[8]);

#endif /* CAN_SCHEMA_H */

#endif /* INC_CAN_SCHEMA_H_ */
