#pragma once

#include "soc/soc_caps.h"
#include "hal/adc_types.h"
#include "hal/adc_ll.h"
#include "esp_err.h"

#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
#include "soc/gdma_struct.h"
#include "hal/gdma_ll.h"
#include "hal/dma_types.h"
#include "hal/adc_ll.h"
#include "hal/dma_types.h"
#include "esp_err.h"

//For ADC module, each conversion contains 4 bytes
#define ADC_HAL_DATA_LEN_PER_CONV 4

/**
 * @brief Enum for DMA descriptor status
 */
typedef enum adc_hal_dma_desc_status_t {
    ADC_HAL_DMA_DESC_VALID   = 0,            ///< This DMA descriptor is written by HW already
    ADC_HAL_DMA_DESC_WAITING = 1,            ///< This DMA descriptor is not written by HW yet
    ADC_HAL_DMA_DESC_NULL    = 2             ///< This DMA descriptor is NULL
} adc_hal_dma_desc_status_t;

/**
 * @brief Configuration of the HAL
 */
typedef struct adc_hal_config_t {
    uint32_t            desc_max_num;       ///< Number of the descriptors linked once
    uint32_t            dma_chan;           ///< DMA channel to be used
    uint32_t            eof_num;            ///< Bytes between 2 in_suc_eof interrupts
} adc_hal_config_t;

/**
 * @brief Context of the HAL
 */
typedef struct adc_hal_context_t {
    /**< this needs to be malloced by the driver layer first */
    dma_descriptor_t    *rx_desc;           ///< DMA descriptors

    /**< these will be assigned by hal layer itself */
    gdma_dev_t          *dev;               ///< GDMA address
    dma_descriptor_t    desc_dummy_head;    ///< Dummy DMA descriptor for ``cur_desc_ptr`` to start
    dma_descriptor_t    *cur_desc_ptr;      ///< Pointer to the current descriptor

    /**< these need to be configured by `adc_hal_config_t` via driver layer*/
    uint32_t            desc_max_num;       ///< Number of the descriptors linked once
    uint32_t            dma_chan;           ///< DMA channel to be used
    uint32_t            eof_num;            ///< Words between 2 in_suc_eof interrupts
} adc_hal_context_t;
#endif

/*---------------------------------------------------------------
                    Common setting
---------------------------------------------------------------*/
/**
 * ADC module initialization.
 */
void adc_hal_init(void);

/**
 * Set ADC module power management.
 *
 * @prarm manage Set ADC power status.
 */
#define adc_hal_set_power_manage(manage) adc_ll_set_power_manage(manage)

/**
 * ADC module clock division factor setting. ADC clock devided from APB clock.
 *
 * @prarm div Division factor.
 */
#define adc_hal_digi_set_clk_div(div) adc_ll_digi_set_clk_div(div)

#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
/**
 * ADC SAR clock division factor setting. ADC SAR clock devided from `RTC_FAST_CLK`.
 *
 * @prarm div Division factor.
 */
#define adc_hal_set_sar_clk_div(adc_n, div) adc_ll_set_sar_clk_div(adc_n, div)

/**
 * Set ADC module controller.
 * There are five SAR ADC controllers:
 * Two digital controller: Continuous conversion mode (DMA). High performance with multiple channel scan modes;
 * Two RTC controller: Single conversion modes (Polling). For low power purpose working during deep sleep;
 * the other is dedicated for Power detect (PWDET / PKDET), Only support ADC2.
 *
 * @prarm adc_n ADC unit.
 * @prarm ctrl ADC controller.
 */
#define adc_hal_set_controller(adc_n, ctrl) adc_ll_set_controller(adc_n, ctrl)
#endif  //#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2

#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
/**
 * Get the attenuation of a particular channel on ADCn.
 *
 * @param adc_n ADC unit.
 * @param channel ADCn channel number.
 * @return atten The attenuation option.
 */
#define adc_hal_get_atten(adc_n, channel) adc_ll_get_atten(adc_n, channel)
#endif

#if CONFIG_IDF_TARGET_ESP32
/**
 * Close ADC AMP module if don't use it for power save.
 */
#define adc_hal_amp_disable() adc_ll_amp_disable()
#endif

#if SOC_ADC_ARBITER_SUPPORTED
//No ADC2 controller arbiter on ESP32
/**
 * Config ADC2 module arbiter.
 * The arbiter is to improve the use efficiency of ADC2. After the control right is robbed by the high priority,
 * the low priority controller will read the invalid ADC2 data, and the validity of the data can be judged by the flag bit in the data.
 *
 * @note Only ADC2 support arbiter.
 * @note The arbiter's working clock is APB_CLK. When the APB_CLK clock drops below 8 MHz, the arbiter must be in shield mode.
 * @note Default priority: Wi-Fi > RTC > Digital;
 *
 * @param config Refer to ``adc_arbiter_t``.
 */
void adc_hal_arbiter_config(adc_arbiter_t *config);
#endif  //#if SOC_ADC_ARBITER_SUPPORTED

/*---------------------------------------------------------------
                    PWDET(Power detect) controller setting
---------------------------------------------------------------*/

/**
 * Set adc cct for PWDET controller.
 *
 * @note Capacitor tuning of the PA power monitor. cct set to the same value with PHY.
 * @prarm cct Range: 0 ~ 7.
 */
#define adc_hal_pwdet_set_cct(cct) adc_ll_pwdet_set_cct(cct)

/**
 * Get adc cct for PWDET controller.
 *
 * @note Capacitor tuning of the PA power monitor. cct set to the same value with PHY.
 * @return cct Range: 0 ~ 7.
 */
#define adc_hal_pwdet_get_cct() adc_ll_pwdet_get_cct()

/*---------------------------------------------------------------
                    RTC controller setting
---------------------------------------------------------------*/
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
/**
 * Set adc output data format for RTC controller.
 *
 * @prarm adc_n ADC unit.
 * @prarm bits Output data bits width option.
 */
#define adc_hal_rtc_set_output_format(adc_n, bits) adc_ll_rtc_set_output_format(adc_n, bits)

/**
 * ADC module output data invert or not.
 *
 * @prarm adc_n ADC unit.
 */
#define adc_hal_rtc_output_invert(adc_n, inv_en) adc_ll_rtc_output_invert(adc_n, inv_en)
#endif  //#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2

/**
 *  Enable/disable the output of ADCn's internal reference voltage to one of ADC2's channels.
 *
 *  This function routes the internal reference voltage of ADCn to one of
 *  ADC2's channels. This reference voltage can then be manually measured
 *  for calibration purposes.
 *
 *  @note  ESP32 only supports output of ADC2's internal reference voltage.
 *  @param[in]  adc ADC unit select
 *  @param[in]  channel ADC2 channel number
 *  @param[in]  en Enable/disable the reference voltage output
 */
#define adc_hal_vref_output(adc, channel, en) adc_ll_vref_output(adc, channel, en)

/*---------------------------------------------------------------
                    Digital controller setting
---------------------------------------------------------------*/
/**
 * Digital controller deinitialization.
 */
void adc_hal_digi_deinit(void);

/**
 * Setting the digital controller.
 *
 * @param cfg Pointer to digital controller paramter.
 */
void adc_hal_digi_controller_config(const adc_digi_config_t *cfg);

/**
 * Reset the pattern table pointer, then take the measurement rule from table header in next measurement.
 *
 * @param adc_n ADC unit.
 */
#define adc_hal_digi_clear_pattern_table(adc_n) adc_ll_digi_clear_pattern_table(adc_n)

/*---------------------------------------------------------------
                    ADC Single Read
---------------------------------------------------------------*/
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
/**
 * Set the attenuation of a particular channel on ADCn.
 *
 * @note For any given channel, this function must be called before the first time conversion.
 *
 * The default ADC full-scale voltage is 1.1V. To read higher voltages (up to the pin maximum voltage,
 * usually 3.3V) requires setting >0dB signal attenuation for that ADC channel.
 *
 * When VDD_A is 3.3V:
 *
 * - 0dB attenuaton (ADC_ATTEN_DB_0) gives full-scale voltage 1.1V
 * - 2.5dB attenuation (ADC_ATTEN_DB_2_5) gives full-scale voltage 1.5V
 * - 6dB attenuation (ADC_ATTEN_DB_6) gives full-scale voltage 2.2V
 * - 11dB attenuation (ADC_ATTEN_DB_11) gives full-scale voltage 3.9V (see note below)
 *
 * @note The full-scale voltage is the voltage corresponding to a maximum reading (depending on ADC1 configured
 * bit width, this value is: 4095 for 12-bits, 2047 for 11-bits, 1023 for 10-bits, 511 for 9 bits.)
 *
 * @note At 11dB attenuation the maximum voltage is limited by VDD_A, not the full scale voltage.
 *
 * Due to ADC characteristics, most accurate results are obtained within the following approximate voltage ranges:
 *
 * - 0dB attenuaton (ADC_ATTEN_DB_0) between 100 and 950mV
 * - 2.5dB attenuation (ADC_ATTEN_DB_2_5) between 100 and 1250mV
 * - 6dB attenuation (ADC_ATTEN_DB_6) between 150 to 1750mV
 * - 11dB attenuation (ADC_ATTEN_DB_11) between 150 to 2450mV
 *
 * For maximum accuracy, use the ADC calibration APIs and measure voltages within these recommended ranges.
 *
 * @param adc_n   ADC unit.
 * @param channel ADCn channel number.
 * @param atten   ADC attenuation. See ``adc_atten_t``
 */
#define adc_hal_set_atten(adc_n, channel, atten) adc_ll_set_atten(adc_n, channel, atten)

#else // CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
/**
 * Set the attenuation for ADC to single read
 *
 * @note All ADC units and channels will share the setting. So PLEASE DO save your attenuations and reset them by calling this API again in your driver
 *
 * @param adc_n    Not used, leave here for chip version compatibility
 * @param channel  Not used, leave here for chip version compatibility
 * @param atten    ADC attenuation. See ``adc_atten_t``
 */
#define adc_hal_set_atten(adc_n, channel, atten) adc_ll_onetime_set_atten(atten)
#endif

/**
 * Start an ADC conversion and get the converted value.
 *
 * @note It may be block to wait conversion finish.
 *
 * @param      adc_n   ADC unit.
 * @param      channel ADC channel number.
 * @param[out] out_raw ADC converted result
 *
 * @return
 *      - ESP_OK:                The value is valid.
 *      - ESP_ERR_INVALID_STATE: The value is invalid.
 */
esp_err_t adc_hal_convert(adc_ll_num_t adc_n, int channel, int *out_raw);

/*---------------------------------------------------------------
                    ADC calibration setting
---------------------------------------------------------------*/
#if SOC_ADC_CALIBRATION_V1_SUPPORTED
// ESP32-S2, C3 and H2 support HW offset calibration.

/**
 * @brief Initialize default parameter for the calibration block.
 *
 * @param adc_n ADC index numer
 */
void adc_hal_calibration_init(adc_ll_num_t adc_n);

/**
 * Set the calibration result (initial data) to ADC.
 *
 * @note  Different ADC units and different attenuation options use different calibration data (initial data).
 *
 * @param adc_n ADC index number.
 * @param param the calibration parameter to configure
 */
void adc_hal_set_calibration_param(adc_ll_num_t adc_n, uint32_t param);

/**
 * Calibrate the ADC using internal connections.
 *
 * @note  Different ADC units and different attenuation options use different calibration data (initial data).
 *
 * @param adc_n ADC index number.
 * @param channel adc channel number.
 * @param atten The attenuation for the channel
 * @param internal_gnd true:  Disconnect from the IO port and use the internal GND as the calibration voltage.
 *                     false: Use IO external voltage as calibration voltage.
 *
 * @return
 *      - The calibration result (initial data) to ADC, use `adc_hal_set_calibration_param` to set.
 */
uint32_t adc_hal_self_calibration(adc_ll_num_t adc_n, adc_channel_t channel, adc_atten_t atten, bool internal_gnd);

#endif //SOC_ADC_CALIBRATION_V1_SUPPORTED

#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
/*---------------------------------------------------------------
                    DMA setting
---------------------------------------------------------------*/
/**
 * @brief Initialize the hal context
 *
 * @param hal    Context of the HAL
 * @param config Configuration of the HAL
 */
void adc_hal_context_config(adc_hal_context_t *hal, const adc_hal_config_t *config);

/**
 * @brief Initialize the HW
 *
 * @param hal Context of the HAL
 */
void adc_hal_digi_init(adc_hal_context_t *hal);

/**
 * @brief Reset ADC / DMA fifo
 *
 * @param hal Context of the HAL
 */
void adc_hal_fifo_reset(adc_hal_context_t *hal);

/**
 * @brief Start DMA
 *
 * @param hal      Context of the HAL
 * @param data_buf Pointer to the data buffer, the length should be multiple of ``desc_max_num`` and ``eof_num`` in ``adc_hal_context_t``
 */
void adc_hal_digi_rxdma_start(adc_hal_context_t *hal, uint8_t *data_buf);

/**
 * @brief Start ADC
 *
 * @param hal Context of the HAL
 */
void adc_hal_digi_start(adc_hal_context_t *hal);

/**
 * @brief Get the ADC reading result
 *
 * @param      hal           Context of the HAL
 * @param      eof_desc_addr The last descriptor that is finished by HW. Should be got from DMA
 * @param[out] cur_desc      The descriptor with ADC reading result (from the 1st one to the last one (``eof_desc_addr``))
 *
 * @return                   See ``adc_hal_dma_desc_status_t``
 */
adc_hal_dma_desc_status_t adc_hal_get_reading_result(adc_hal_context_t *hal, const intptr_t eof_desc_addr, dma_descriptor_t **cur_desc);

/**
 * @brief Stop DMA
 *
 * @param hal Context of the HAL
 */
void adc_hal_digi_rxdma_stop(adc_hal_context_t *hal);

/**
 * @brief Clear interrupt
 *
 * @param hal  Context of the HAL
 * @param mask mask of the interrupt
 */
void adc_hal_digi_clr_intr(adc_hal_context_t *hal, uint32_t mask);

/**
 * @brief Enable interrupt
 *
 * @param hal  Context of the HAL
 * @param mask mask of the interrupt
 */
void adc_hal_digi_dis_intr(adc_hal_context_t *hal, uint32_t mask);

/**
 * @brief Stop ADC
 *
 * @param hal Context of the HAL
 */
void adc_hal_digi_stop(adc_hal_context_t *hal);

#endif  //#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
