#include "mcp3550.h"
#include "mcp23018.h"
#include "driver/gpio.h"
#include <esp_timer.h>


#define TAG_MICS "MICS_ADC"
#define MCP3550_SPS 3.75f
#define MCP3550_CONVERSION_MS ((int)(1000.0f / MCP3550_SPS)) // ~267ms
#define MCP3550_TIMEOUT_MS 400

#define ADC_COUNT 4
const uint8_t adc_cs_pins[ADC_COUNT] = {
    MCP_CS_ADC_NH3, MCP_CS_ADC_CO, MCP_CS_ADC_NO2, MCP_CS_ADC_UVC
};


spi_device_handle_t mcp3550_handle;

void mcp3550_spi_init()
{
    // spi_device_interface_config_t devcfg = {
    //     .clock_speed_hz = 100000,
    //     .mode = 0,
    //     .spics_io_num = -1, // We handle CS manually
    //     .queue_size = 1,
    // };

    // ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_add_device(SPI2_HOST, &devcfg, &mcp3550_handle));

    // Set MISO pin for input (needed for polling)
    gpio_set_direction(MCP3550_MISO_GPIO, GPIO_MODE_INPUT);
}


// int32_t mcp3550_read(uint8_t cs_pin)
// {
//     uint8_t rx_buf[4] = {0}; // 25 bits fits in 4 bytes

//     int64_t start = esp_timer_get_time(); // in microseconds
//     while (true) {
//         mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW
//         vTaskDelay(pdMS_TO_TICKS(30)); // Wait before retrying
//         mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH

//         vTaskDelay(pdMS_TO_TICKS(200)); // Wait before retrying

//         mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW
//         esp_rom_delay_us(1000); // Wait before retrying

//         spi_transaction_t trans = {
//             .length = 25,
//             .rx_buffer = rx_buf,
//         };

//         esp_err_t err = spi_device_transmit(mcp3550_handle, &trans);

//         mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH

//         if (err != ESP_OK) {
//             ESP_LOGE(TAG_MICS, "SPI transmit failed on CS pin %u", cs_pin);
//             return INT32_MIN;
//         }

//         bool dr = (rx_buf[0] & 0x80) != 0;
//         if (!dr) break;

//         if ((esp_timer_get_time() - start) > (MCP3550_TIMEOUT_MS * 1000)) {
//             ESP_LOGW(TAG_MICS, "Timeout waiting for DR=0 on CS pin %u", cs_pin);
//             return INT32_MIN;
//         }

//         vTaskDelay(pdMS_TO_TICKS(10)); // Wait before retrying
//     }

//     // Combine 22-bit result (drop DR + status bits)
//     uint32_t raw = ((rx_buf[0] & 0x3F) << 16) | (rx_buf[1] << 8) | rx_buf[2];

//     // Sign-extend 22-bit value
//     int32_t value = raw;
//     if (value & (1 << 21)) value |= 0xFFC00000;

//     return value;
// }

// int32_t mcp3550_read(uint8_t cs_pin)
// {
//     uint8_t rx_buf[4] = {0};
//     uint32_t timeout_us = MCP3550_TIMEOUT_MS * 1000;
//     int64_t start = esp_timer_get_time();

//     // Start conversion
//     mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW

//     // Wait until MISO/SDO goes LOW = DR ready
//     while (gpio_get_level(MCP3550_MISO_GPIO)) {
//         if ((esp_timer_get_time() - start) > timeout_us) {
//             ESP_LOGW(TAG_MICS, "Timeout waiting for MISO=0 on CS %u", cs_pin);
//             mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH
//             return INT32_MIN;
//         }
//         esp_rom_delay_us(10); // micro delay
//     }

//     // Data is ready, do full SPI read
//     spi_transaction_t trans = {
//         .length = 25,  // 25 bits
//         .rx_buffer = rx_buf,
//     };
//     esp_err_t err = spi_device_transmit(mcp3550_handle, &trans);

//     mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH to start next conversion

//     if (err != ESP_OK) {
//         ESP_LOGE(TAG_MICS, "SPI transmit failed");
//         return INT32_MIN;
//     }

//     // Extract 22-bit result
//     uint32_t raw = ((rx_buf[0] & 0x3F) << 16) | (rx_buf[1] << 8) | rx_buf[2];

//     // Sign-extend 22-bit value
//     int32_t value = raw;
//     if (value & (1 << 21)) value |= 0xFFC00000;

//     return value;
// }

int32_t mcp3550_read(uint8_t cs_pin)
{
    uint32_t data = 0;
    int64_t start = esp_timer_get_time();
    uint32_t timeout_us = MCP3550_TIMEOUT_MS * 1000;

    ESP_LOGI(TAG_MCP, "Starting read from ADC CS %d", cs_pin);

    // CS LOW
    mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0);

    vTaskDelay(pdMS_TO_TICKS(10));

    // Wait for DR (MISO LOW)
    while (gpio_get_level(MCP3550_MISO_GPIO)) {
        if ((esp_timer_get_time() - start) > timeout_us) {
            ESP_LOGW(TAG_MICS, "Timeout waiting for MISO=0 on CS %u", cs_pin);
            mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH
            return INT32_MIN;
        }
        vTaskDelay(pdMS_TO_TICKS(10)); // Wait 1 tick (e.g., 1ms)
    }

    // Clock out 25 bits
    for (int i = 0; i < 25; i++) {
        gpio_set_level(MCP3550_SCK_GPIO, 1);
        esp_rom_delay_us(5); // small delay to simulate clock high
        data = (data << 1) | gpio_get_level(MCP3550_MISO_GPIO);
        gpio_set_level(MCP3550_SCK_GPIO, 0);
        esp_rom_delay_us(5); // small delay to simulate clock low
    }

    mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH

    // Extract 22-bit value (bits [23:2])
    uint32_t raw = (data >> 2) & 0x3FFFFF;

    // Sign-extend 22-bit value
    int32_t value = raw;
    if (value & (1 << 21)) value |= 0xFFC00000;

    return value;
}



float mcp3550_to_voltage(int32_t value, float vref)
{
    return ((float)value / (1 << 21)) * vref;
}

mics_adc_data_t mcp3550_read_all(float vref)
{
    mics_adc_data_t data;
    int32_t raw[ADC_COUNT];
    float volts[ADC_COUNT];

    for (int i = 0; i < ADC_COUNT; i++) {
        raw[i] = mcp3550_read(adc_cs_pins[i]);
        vTaskDelay(pdMS_TO_TICKS(10)); // Wait before retrying
        volts[i] = mcp3550_to_voltage(raw[i], vref);
    }

    data.raw_nh3 = raw[0]; data.volts_nh3 = volts[0];
    data.raw_co  = raw[1]; data.volts_co  = volts[1];
    data.raw_no2 = raw[2]; data.volts_no2 = volts[2];
    data.raw_uvc = raw[3]; data.volts_uvc = volts[3];

    return data;
}

void log_mics_adc_values(mics_adc_data_t *data)
{
    ESP_LOGI(TAG_MICS,
             "ADC Readings:\n"
             "  NH3  -> Raw: %ld, Voltage: %f V\n"
             "  CO   -> Raw: %ld, Voltage: %f V\n"
             "  NO2  -> Raw: %ld, Voltage: %f V\n"
             "  UVC  -> Raw: %ld, Voltage: %f V",
             data->raw_nh3, data->volts_nh3,
             data->raw_co,  data->volts_co,
             data->raw_no2, data->volts_no2,
             data->raw_uvc, data->volts_uvc);
}
