#include "configuration.h"

#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_ADS1X15.h>)

#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "ADS1X15Sensor.h"
#include "TelemetrySensor.h"
#include <Adafruit_ADS1X15.h>

ADS1X15Sensor::ADS1X15Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_ADS1X15, "ADS1X15") {}

bool ADS1X15Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
    LOG_INFO("Init sensor: %s (address: 0x%x)", sensorName, dev->address.address);

    _bus = bus;

    _address = dev->address.address;
    _deviceType = dev->type;

#ifdef ADS1X15_I2C_CLOCK_SPEED
    _port = dev->address.port;
    reClockI2C.setup(_bus, _port);
    ReClockI2CGuard clockGuard(reClockI2C, ADS1X15_I2C_CLOCK_SPEED);
#endif /* ADS1X15_I2C_CLOCK_SPEED */

    status = ads1x15.begin(_address, _bus);

    initI2CSensor();

    return status;
}

struct _ADS1X15Measurement ADS1X15Sensor::getMeasurement(uint8_t ch)
{
    struct _ADS1X15Measurement measurement;

    // Reset gain
    ads1x15.setGain(GAIN_TWOTHIRDS);
    double voltage_range = 6.144;

    // Get value with full range
    uint16_t value = ads1x15.readADC_SingleEnded(ch);

    // Dynamic gain, to increase resolution of low voltage values
    // If value is under 4.096v increase the gain depending on voltage
    if (value < 21845) {
        if (value > 10922) {

            // 1x gain, 4.096V
            ads1x15.setGain(GAIN_ONE);
            voltage_range = 4.096;

        } else if (value > 5461) {

            // 2x gain, 2.048V
            ads1x15.setGain(GAIN_TWO);
            voltage_range = 2.048;

        } else if (value > 2730) {

            // 4x gain, 1.024V
            ads1x15.setGain(GAIN_FOUR);
            voltage_range = 1.024;

        } else if (value > 1365) {

            // 8x gain, 0.25V
            ads1x15.setGain(GAIN_EIGHT);
            voltage_range = 0.512;

        } else {

            // 16x gain, 0.125V
            ads1x15.setGain(GAIN_SIXTEEN);
            voltage_range = 0.256;
        }

        // Get the value again
        value = ads1x15.readADC_SingleEnded(ch);
    }

    measurement.voltage = (float)value / 32768 * voltage_range;

    return measurement;
}

struct _ADS1X15Measurements ADS1X15Sensor::getMeasurements()
{
    struct _ADS1X15Measurements measurements;

    // ADS1X15 has 4 channels starting from 0
    for (int i = 0; i < 4; i++) {
        measurements.measurements[i] = getMeasurement(i);
    }

    return measurements;
}

bool ADS1X15Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
    // Done here and not in getMeasurements to avoid the back-and-forth 4-8 times one after the other
#ifdef ADS1X15_I2C_CLOCK_SPEED
    ReClockI2CGuard clockGuard(reClockI2C, ADS1X15_I2C_CLOCK_SPEED);
#endif /* ADS1X15_I2C_CLOCK_SPEED */

    struct _ADS1X15Measurements m = getMeasurements();

    switch (_deviceType) {
    case ScanI2C::DeviceType::ADS1X15: {
        measurement->variant.environment_metrics.has_adc_voltage_ch0 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch1 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch2 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch3 = true;

        measurement->variant.environment_metrics.adc_voltage_ch0 = m.measurements[0].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch1 = m.measurements[1].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch2 = m.measurements[2].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch3 = m.measurements[3].voltage;

        LOG_DEBUG(
            "Got %s readings: adc_voltage_ch0=%f, adc_voltage_ch1=%f, adc_voltage_ch2=%f, adc_voltage_ch3=%f", sensorName,
            measurement->variant.environment_metrics.adc_voltage_ch0, measurement->variant.environment_metrics.adc_voltage_ch1,
            measurement->variant.environment_metrics.adc_voltage_ch2, measurement->variant.environment_metrics.adc_voltage_ch3);

        break;
    }
    case ScanI2C::DeviceType::ADS1X15_ALT: {
        measurement->variant.environment_metrics.has_adc_voltage_ch4 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch5 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch6 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch7 = true;

        measurement->variant.environment_metrics.adc_voltage_ch4 = m.measurements[0].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch5 = m.measurements[1].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch6 = m.measurements[2].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch7 = m.measurements[3].voltage;

        LOG_DEBUG(
            "Got %s readings: adc_voltage_ch4=%f, adc_voltage_ch5=%f, adc_voltage_ch6=%f, adc_voltage_ch7=%f", sensorName,
            measurement->variant.environment_metrics.adc_voltage_ch4, measurement->variant.environment_metrics.adc_voltage_ch5,
            measurement->variant.environment_metrics.adc_voltage_ch6, measurement->variant.environment_metrics.adc_voltage_ch7);

        break;
    }
    default: {
        measurement->variant.environment_metrics.has_adc_voltage_ch0 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch1 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch2 = true;
        measurement->variant.environment_metrics.has_adc_voltage_ch3 = true;

        measurement->variant.environment_metrics.adc_voltage_ch0 = m.measurements[0].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch1 = m.measurements[1].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch2 = m.measurements[2].voltage;
        measurement->variant.environment_metrics.adc_voltage_ch3 = m.measurements[3].voltage;

        LOG_DEBUG(
            "Got %s readings: adc_voltage_ch0=%f, adc_voltage_ch1=%f, adc_voltage_ch2=%f, adc_voltage_ch3=%f", sensorName,
            measurement->variant.environment_metrics.adc_voltage_ch0, measurement->variant.environment_metrics.adc_voltage_ch1,
            measurement->variant.environment_metrics.adc_voltage_ch2, measurement->variant.environment_metrics.adc_voltage_ch3);

        break;
    }
    }
    return true;
}

#endif