#ifndef _ADC_DRIVER_HPP_
#define _ADC_DRIVER_HPP_ 1

#include "hardware/adc.h"
#include "hardware/irq.h"
#include "pico/multicore.h"
#include "hardware/clocks.h"

#include "adrs2040U_i2c.h"

#define ADC_BUFF_SIZE   0x100

// MUTEX
auto_init_mutex(adcbuff_mutex);

static volatile int read_p;
static volatile int write_p;
static volatile uint16_t adc_buffer[ADC_BUFF_SIZE];

static void adc_interrupt(void)
{
    while(! adc_fifo_is_empty()) {
        mutex_enter_blocking(&adcbuff_mutex);
        adc_buffer[write_p] = adc_fifo_get() & 0xFFF;
        write_p = (write_p + 1) & (ADC_BUFF_SIZE - 1);
        mutex_exit(&adcbuff_mutex);
    }
};

class ADC_Driver {
    private:
        int sample_rate = 1000;     // サンプリングレート
        uint32_t adc_clk;           // ADCクロック
        uint8_t adc_pin;            // 使用するADCピン番号
        uint8_t adc_channel;        // ADCチャンネル番号

    public:
        ADC_Driver(uint8_t p = 26, uint8_t c = 0) : adc_pin(p),  adc_channel(c) 
        {
            read_p = write_p = 0;
            sample_rate = 1000;
            adc_init();
            adc_gpio_init(adc_pin);
            adc_select_input(adc_channel);
            adc_fifo_setup(true, false, 1, true, false);
            // ADCサンプリングクロック設定
            adc_clk = clock_get_hz(clk_adc);
            adc_set_clkdiv(adc_clk/sample_rate);
            // ADC割り込み
	        irq_set_exclusive_handler(ADC_IRQ_FIFO, adc_interrupt);
	        irq_set_enabled(ADC_IRQ_FIFO, true);
            run(false);
        };

        int get_sample_rate(void)
        {
            return sample_rate;
        };

        void set_sample_rate(int rate)
        {
            sample_rate = rate;
            adc_set_clkdiv(adc_clk/sample_rate);
        };

        bool is_empty(void)
        {
            return (read_p == write_p);
        };

        int get_value(void)
        {
            int wp, rv;

            mutex_enter_blocking(&adcbuff_mutex);
            wp = write_p;
            if(read_p != wp) {
                rv = adc_buffer[read_p];
                read_p = (read_p + 1) & (ADC_BUFF_SIZE - 1);
            }
            else
                rv = -1;
            mutex_exit(&adcbuff_mutex);

            return rv;
        };

        uint8_t count(void)
        {
            uint8_t r;
            mutex_enter_blocking(&adcbuff_mutex);
            r = write_p - read_p;
            mutex_exit(&adcbuff_mutex);

            return r;
        }

        void run(bool r)
        {
            adc_irq_set_enabled(r);
            adc_run(r);

            if(r == false) {
                read_p = write_p = 0;
            }
        };
};
#endif