/**
 * @file adc.hpp
 * @brief ADC wrapper component header - Wrapper for ESP-IDF ADC functionality
 * @author Mahmoud Elmohtady
 * @company Nabd solutions - ASF
 * @copyright Copyright (c) 2025
 */

#ifndef ADC_HPP
#define ADC_HPP

#include <cstdint>
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_err.h"

/**
 * @brief ADC unit enumeration
 */
enum class AdcUnit
{
    UNIT_1 = ADC_UNIT_1,
    UNIT_2 = ADC_UNIT_2
};

/**
 * @brief ADC channel enumeration
 */
enum class AdcChannel
{
    CHANNEL_0 = ADC_CHANNEL_0,
    CHANNEL_1 = ADC_CHANNEL_1,
    CHANNEL_2 = ADC_CHANNEL_2,
    CHANNEL_3 = ADC_CHANNEL_3,
    CHANNEL_4 = ADC_CHANNEL_4,
    CHANNEL_5 = ADC_CHANNEL_5,
    CHANNEL_6 = ADC_CHANNEL_6,
    CHANNEL_7 = ADC_CHANNEL_7,
    CHANNEL_8 = ADC_CHANNEL_8,
    CHANNEL_9 = ADC_CHANNEL_9
};

/**
 * @brief ADC attenuation enumeration
 */
enum class AdcAttenuation
{
    ATTEN_0dB = ADC_ATTEN_DB_0,      ///< 0dB attenuation (0-950mV)
    ATTEN_2_5dB = ADC_ATTEN_DB_2_5,  ///< 2.5dB attenuation (0-1250mV)
    ATTEN_6dB = ADC_ATTEN_DB_6,      ///< 6dB attenuation (0-1750mV)
    ATTEN_11dB = ADC_ATTEN_DB_11     ///< 11dB attenuation (0-3100mV)
};

/**
 * @brief ADC bitwidth enumeration
 */
enum class AdcBitwidth
{
    WIDTH_9BIT = ADC_BITWIDTH_9,
    WIDTH_10BIT = ADC_BITWIDTH_10,
    WIDTH_11BIT = ADC_BITWIDTH_11,
    WIDTH_12BIT = ADC_BITWIDTH_12,
    WIDTH_13BIT = ADC_BITWIDTH_13
};

/**
 * @brief ADC channel configuration structure
 */
struct AdcChannelConfig
{
    AdcUnit unit;              ///< ADC unit
    AdcChannel channel;        ///< ADC channel
    AdcAttenuation atten;      ///< Attenuation level
    AdcBitwidth bitwidth;      ///< ADC resolution
};

/**
 * @brief ADC wrapper class
 * 
 * Provides a C++ wrapper for ESP-IDF ADC functionality
 * with simplified interface and error handling.
 * This class encapsulates ESP-IDF ADC oneshot driver functions in an object-oriented interface.
 */
class Adc
{
public:
    /**
     * @brief Constructor
     * @details Initializes the ADC wrapper instance
     */
    Adc();

    /**
     * @brief Destructor
     * @details Cleans up resources and deinitializes ADC units
     */
    ~Adc();

    /**
     * @brief Initialize ADC component
     * @return true if initialized successfully, false otherwise
     */
    bool initialize();

    /**
     * @brief Initialize ADC unit
     * @param unit ADC unit to initialize
     * @param bitwidth ADC resolution
     * @return true if initialized successfully, false otherwise
     */
    bool initializeUnit(AdcUnit unit, AdcBitwidth bitwidth);

    /**
     * @brief Deinitialize ADC unit
     * @param unit ADC unit to deinitialize
     * @return true if deinitialized successfully, false otherwise
     */
    bool deinitializeUnit(AdcUnit unit);

    /**
     * @brief Configure ADC channel
     * @param config Channel configuration
     * @return true if configured successfully, false otherwise
     * @note Unit must be initialized before configuring channels
     */
    bool configureChannel(const AdcChannelConfig& config);

    /**
     * @brief Read voltage from a channel
     * @param unit ADC unit
     * @param channel ADC channel to read
     * @return Voltage in millivolts, or -1 on error
     * @note Channel must be configured before reading
     */
    int32_t readVoltage(AdcUnit unit, AdcChannel channel);

    /**
     * @brief Read raw value from a channel
     * @param unit ADC unit
     * @param channel ADC channel to read
     * @return Raw ADC value, or -1 on error
     * @note Channel must be configured before reading
     */
    int32_t readRaw(AdcUnit unit, AdcChannel channel);

    /**
     * @brief Read multiple samples and return average
     * @param unit ADC unit
     * @param channel ADC channel to read
     * @param samples Number of samples to average
     * @return Average voltage in millivolts, or -1 on error
     */
    int32_t readVoltageAverage(AdcUnit unit, AdcChannel channel, uint32_t samples);

    /**
     * @brief Read multiple raw samples and return average
     * @param unit ADC unit
     * @param channel ADC channel to read
     * @param samples Number of samples to average
     * @return Average raw ADC value, or -1 on error
     */
    int32_t readRawAverage(AdcUnit unit, AdcChannel channel, uint32_t samples);

    /**
     * @brief Check if ADC unit is initialized
     * @param unit ADC unit to check
     * @return true if initialized, false otherwise
     */
    bool isUnitInitialized(AdcUnit unit) const;

    /**
     * @brief Check if channel is configured
     * @param unit ADC unit
     * @param channel ADC channel
     * @return true if configured, false otherwise
     */
    bool isChannelConfigured(AdcUnit unit, AdcChannel channel) const;

    /**
     * @brief Get maximum raw value for given bitwidth
     * @param bitwidth ADC bitwidth
     * @return Maximum raw value
     */
    static uint32_t getMaxRawValue(AdcBitwidth bitwidth);

    /**
     * @brief Get default channel configuration
     * @return Default ADC channel configuration
     */
    static AdcChannelConfig getDefaultChannelConfig();

private:
    adc_oneshot_unit_handle_t m_adcHandle_[2];     ///< ADC unit handles
    adc_cali_handle_t m_caliHandle_[2][10];        ///< Calibration handles [unit][channel]
    bool m_unitInitialized_[2];                    ///< Unit initialization status
    bool m_channelConfigured_[2][10];              ///< Channel configuration status [unit][channel]
    AdcBitwidth m_unitBitwidth_[2];                ///< Bitwidth for each unit
    
    /**
     * @brief Convert AdcUnit to ESP-IDF adc_unit_t
     * @param unit ADC unit
     * @return ESP-IDF adc_unit_t
     */
    adc_unit_t convertUnit(AdcUnit unit);
    
    /**
     * @brief Convert AdcChannel to ESP-IDF adc_channel_t
     * @param channel ADC channel
     * @return ESP-IDF adc_channel_t
     */
    adc_channel_t convertChannel(AdcChannel channel);
    
    /**
     * @brief Convert AdcAttenuation to ESP-IDF adc_atten_t
     * @param atten ADC attenuation
     * @return ESP-IDF adc_atten_t
     */
    adc_atten_t convertAttenuation(AdcAttenuation atten);
    
    /**
     * @brief Convert AdcBitwidth to ESP-IDF adc_bitwidth_t
     * @param bitwidth ADC bitwidth
     * @return ESP-IDF adc_bitwidth_t
     */
    adc_bitwidth_t convertBitwidth(AdcBitwidth bitwidth);
    
    /**
     * @brief Initialize calibration for channel
     * @param unit ADC unit
     * @param channel ADC channel
     * @param atten Attenuation level
     * @return true if calibration initialized, false otherwise
     */
    bool initializeCalibration(AdcUnit unit, AdcChannel channel, AdcAttenuation atten);
    
    /**
     * @brief Deinitialize calibration for channel
     * @param unit ADC unit
     * @param channel ADC channel
     * @return true if calibration deinitialized, false otherwise
     */
    bool deinitializeCalibration(AdcUnit unit, AdcChannel channel);
    
    /**
     * @brief Get unit index from AdcUnit
     * @param unit ADC unit
     * @return Unit index (0 or 1)
     */
    uint8_t getUnitIndex(AdcUnit unit) const;
    
    /**
     * @brief Get channel index from AdcChannel
     * @param channel ADC channel
     * @return Channel index (0-9)
     */
    uint8_t getChannelIndex(AdcChannel channel) const;
};

#endif // ADC_HPP

