#ifndef PROTOCOL_H
#define PROTOCOL_H

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

/* Commands: Client -> Firmware (0x1x, 0x2x, 0x3x) */
#define CMD_SET_SWEEP     0x10
#define CMD_SET_RTIA      0x11
#define CMD_SET_RCAL      0x12
#define CMD_START_SWEEP   0x13
#define CMD_GET_CONFIG    0x14
#define CMD_SET_ELECTRODE 0x15
#define CMD_START_LSV     0x20
#define CMD_START_AMP     0x21
#define CMD_STOP_AMP      0x22

#define CMD_GET_TEMP      0x17
#define CMD_START_CL      0x23
#define CMD_START_PH      0x24
#define CMD_START_CLEAN   0x25
#define CMD_OPEN_CAL      0x26
#define CMD_CLEAR_OPEN_CAL 0x27
#define CMD_SET_CELL_K    0x28
#define CMD_GET_CELL_K    0x29
#define CMD_START_REFS    0x30
#define CMD_GET_REFS      0x31
#define CMD_CLEAR_REFS    0x32
#define CMD_SET_CL_FACTOR 0x33
#define CMD_GET_CL_FACTOR 0x34
#define CMD_SET_PH_CAL    0x35
#define CMD_GET_PH_CAL    0x36

/* Session sync commands (0x4x) */
#define CMD_SESSION_CREATE  0x40
#define CMD_SESSION_SWITCH  0x41
#define CMD_SESSION_LIST    0x42
#define CMD_SESSION_RENAME  0x43
#define CMD_HEARTBEAT       0x44

/* Responses: Firmware -> Client (0x0x, 0x2x) */
#define RSP_SWEEP_START 0x01
#define RSP_DATA_POINT  0x02
#define RSP_SWEEP_END   0x03
#define RSP_CONFIG      0x04
#define RSP_LSV_START   0x05
#define RSP_LSV_POINT   0x06
#define RSP_LSV_END     0x07
#define RSP_AMP_START   0x08
#define RSP_AMP_POINT   0x09
#define RSP_AMP_END     0x0A
#define RSP_CL_START    0x0B
#define RSP_CL_POINT    0x0C
#define RSP_CL_RESULT   0x0D
#define RSP_CL_END      0x0E
#define RSP_PH_RESULT   0x0F
#define RSP_TEMP        0x10
#define RSP_CELL_K      0x11
#define RSP_REF_FRAME   0x20
#define RSP_REF_LP_RANGE 0x21
#define RSP_REFS_DONE   0x22
#define RSP_REF_STATUS  0x23
#define RSP_CL_FACTOR   0x24
#define RSP_PH_CAL      0x25
#define RSP_KEEPALIVE   0x50

/* Session sync responses (0x4x) */
#define RSP_SESSION_CREATED  0x40
#define RSP_SESSION_SWITCHED 0x41
#define RSP_SESSION_LIST     0x42
#define RSP_SESSION_RENAMED  0x43
#define RSP_CLIENT_LIST      0x44

/* Session limits */
#define MAX_SESSIONS      8
#define MAX_SESSION_NAME  32

typedef struct {
    uint8_t type;
    union {
        struct { float freq_start, freq_stop; uint16_t ppd; } sweep;
        uint8_t rtia;
        uint8_t rcal;
        uint8_t electrode;
        struct { float v_start, v_stop, scan_rate; uint8_t lp_rtia; uint16_t num_points; } lsv;
        struct { float v_hold, interval_ms, duration_s; uint8_t lp_rtia; } amp;
        struct { float v_cond, t_cond_ms, v_free, v_total, t_dep_ms, t_meas_ms; uint8_t lp_rtia; } cl;
        struct { float stabilize_s; } ph;
        struct { float v_mv; float duration_s; } clean;
        float cell_k;
        float cl_factor;
        struct { float slope; float offset; } ph_cal;
        struct { uint8_t name_len; char name[MAX_SESSION_NAME]; } session_create;
        struct { uint8_t id; } session_switch;
        struct { uint8_t id; uint8_t name_len; char name[MAX_SESSION_NAME]; } session_rename;
    };
} Command;

typedef struct {
    uint8_t id;
    char name[MAX_SESSION_NAME + 1];
    uint8_t active;
} Session;

int  protocol_init(void);
int  protocol_recv_command(Command *cmd, uint32_t timeout_ms);
void protocol_push_command(const Command *cmd);

/* 7-bit encode/decode helpers */
void     encode_u32(uint32_t val, uint8_t *out);
uint32_t decode_u32(const uint8_t *d);
float    decode_float(const uint8_t *d);
uint16_t decode_u16(const uint8_t *d);

/* outbound: EIS */
int send_sweep_start(uint32_t num_points, float freq_start, float freq_stop,
                     uint32_t ts_ms, uint16_t meas_id);
int send_eis_point(uint16_t index, const EISPoint *pt);
int send_sweep_end(void);
int send_config(const EISConfig *cfg);

/* outbound: LSV */
int send_lsv_start(uint32_t num_points, float v_start, float v_stop,
                   uint32_t ts_ms, uint16_t meas_id);
int send_lsv_point(uint16_t index, float v_mv, float i_ua);
int send_lsv_end(void);

/* outbound: Amperometry */
int send_amp_start(float v_hold, uint32_t ts_ms, uint16_t meas_id);
int send_amp_point(uint16_t index, float t_ms, float i_ua);
int send_amp_end(void);

/* outbound: Chlorine */
int send_cl_start(uint32_t num_points, uint32_t ts_ms, uint16_t meas_id);
int send_cl_point(uint16_t index, float t_ms, float i_ua, uint8_t phase);
int send_cl_result(float i_free_ua, float i_total_ua);
int send_cl_end(void);

/* outbound: pH */
int send_ph_result(float v_ocp_mv, float ph, float temp_c,
                   uint32_t ts_ms, uint16_t meas_id);

/* outbound: temperature */
int send_temp(float temp_c);

/* outbound: cell constant */
int send_cell_k(float k);

/* outbound: chlorine factor */
int send_cl_factor(float f);

/* outbound: pH calibration */
int send_ph_cal(float slope, float offset);

/* outbound: reference collection */
int send_ref_frame(uint8_t mode, uint8_t rtia_idx);
int send_ref_lp_range(uint8_t mode, uint8_t low_idx, uint8_t high_idx);
int send_refs_done(void);
int send_ref_status(uint8_t has_refs);

/* keepalive (sent during long blocking ops) */
int send_keepalive(void);

/* session management */
const Session *session_get_all(uint8_t *count);
uint8_t session_get_current(void);
uint8_t session_create(const char *name, uint8_t name_len);
int     session_switch(uint8_t id);
int     session_rename(uint8_t id, const char *name, uint8_t name_len);

/* session sync responses */
int send_session_created(uint8_t id, const char *name, uint8_t name_len);
int send_session_switched(uint8_t id);
int send_session_list(void);
int send_session_renamed(uint8_t id, const char *name, uint8_t name_len);
int send_client_list(uint8_t count);

#endif
