#include<Wire.h>
#include<Arduino.h>

typedef enum
{
    LP8_STATE_ERROR = -1,
    LP8_STATE_INITIALIZE = 0,
    LP8_STATE_READY = 1,
    LP8_STATE_PRECHARGE = 2,
    LP8_STATE_CHARGE = 3,
    LP8_STATE_BOOT = 4,
    LP8_STATE_BOOT_READ = 5,
    LP8_STATE_MEASURE = 6,
    LP8_STATE_MEASURE_READ = 7

} lp8_state_t;

typedef enum
{
    LP8_ERROR_CLEAR = 88,
    LP8_ERROR_INITIALIZE = 0,
    LP8_ERROR_PRECHARGE = 1,

    LP8_ERROR_CHARGE_CHARGE_ENABLE = 2,
    LP8_ERROR_CHARGE_DEVICE_ENABLE = 3,

    LP8_ERROR_BOOT_SIGNAL_READY = 4,
    LP8_ERROR_BOOT_TIMEOUT = 5,
    LP8_ERROR_BOOT_UART_ENABLE = 6,
    LP8_ERROR_BOOT_UART_WRITE = 7,

    LP8_ERROR_BOOT_READ_UART_ENABLE = 8,
    LP8_ERROR_BOOT_READ_DEVICE_ADDRESS = 9,
    LP8_ERROR_BOOT_READ_COMMAND = 10,
    LP8_ERROR_BOOT_READ_CRC = 11,
    LP8_ERROR_BOOT_READ_TIMEOUT = 12,

    LP8_ERROR_MEASURE_SIGNAL_RDY = 13,
    LP8_ERROR_MEASURE_SIGNAL_RDY_TIMEOUT = 14,
    LP8_ERROR_MEASURE_UART_ENABLE = 15,
    LP8_ERROR_MEASURE_UART_WRITE = 16,

    LP8_ERROR_MEASURE_READ_UART_ENABLE = 17,
    LP8_ERROR_MEASURE_READ_DEVICE_ENABLE = 18,
    LP8_ERROR_MEASURE_READ_DEVICE_ADDRESS = 19,
    LP8_ERROR_MEASURE_READ_COMMAND = 20,
    LP8_ERROR_MEASURE_READ_CRC = 21,
    LP8_ERROR_MEASURE_READ_CALIBRATION_RUN = 22,
    LP8_ERROR_MEASURE_READ_OOR = 23,
    LP8_ERROR_MEASURE_READ_STATUS1 = 24,
    LP8_ERROR_MEASURE_READ_STATUS1_ADC = 25,
    LP8_ERROR_MEASURE_READ_STATUS1_VCAP1 = 26,
    LP8_ERROR_MEASURE_READ_STATUS1_VCAP2 = 27,
    LP8_ERROR_MEASURE_READ_TIMEOUT = 28

} lp8_error_t;

typedef enum
{
    //Background calibration using unfiltered data
    LP8_CALIBRATION_BACKGROUND_UNFILTERED = 0x50,

    //Background calibration using filtered data
    LP8_CALIBRATION_BACKGROUND_FILTERED = 0x51,

    //Background calibration using unfiltered data + reset filters
    LP8_CALIBRATION_BACKGROUND_UNFILTERED_RF = 0x52,

    //Background calibration using filtered data + reset filters
    LP8_CALIBRATION_BACKGROUND_FILTERED_RF = 0x53,

    //ABC (based on filtered data)
    LP8_CALIBRATION_ABC = 0x70,

    //ABC (based on filtered data) + reset filters
    LP8_CALIBRATION_ABC_RF = 0x72

} lp8_calibration_t;

struct lp8_t {
    lp8_state_t state;
    lp8_error_t error;
    bool first_measurement_done;
    lp8_calibration_t calibration;
    bool calibration_run;
    uint8_t rx_buffer[49];
    uint8_t tx_buffer[33];
    uint8_t sensor_state[23];
    int16_t concentration;
    bool valid;
    uint16_t pressure;
};

bool init_co2_module(long baudrate = 0);
bool reset_module();
uint8_t get_ready_state();
uint16_t calculate_crc16(uint8_t *buffer, uint8_t length);
bool tca_set_port(uint8_t port);
bool tca_set_direction(uint8_t dir);
int16_t get_co2_concentration(uint16_t pressure = 10124);
