#pragma once

#include <zephyr/zephyr.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/adc.h>


#define WAIT_TIME_BEFORE_SAMPLE_US  	280 
#define WAIT_TIME_FOR_MEASUREMENT_US 	40
#define WAIT_TIME_PERIOD                9680

#define LED0_NODE DT_ALIAS(irled)
#define ADC_NODE DT_NODELABEL(adc)

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct device *adc_dev = DEVICE_DT_GET(ADC_NODE);

#define ADC_RESOLUTION		12
#define ADC_CHANNEL			0
#define ADC_PORT			SAADC_CH_PSELP_PSELP_AnalogInput2	//AIN2 P0.04
#define ADC_REFERENCE		ADC_REF_INTERNAL					//0.6V
#define ADC_GAIN			ADC_GAIN_1_2						//ADC_REFERENCE*2

struct adc_channel_cfg channel_cfg = {
	.gain = ADC_GAIN,
	.reference = ADC_REFERENCE,
	.acquisition_time = ADC_ACQ_TIME_DEFAULT,
	.channel_id = ADC_CHANNEL,
#ifdef CONFIG_ADC_NRFX_SAADC
	.input_positive = ADC_PORT
#endif
};


int16_t sample_buffer[1];
struct adc_sequence sequence = {
	/* individual channels will be added below */
	.channels = BIT(ADC_CHANNEL),
	.buffer = sample_buffer,
	/* buffer size in bytes, not number of samples */
	.buffer_size = sizeof(sample_buffer),
	.resolution = ADC_RESOLUTION
};


void dust_sensor_init(void){
    int ret;

	if (!device_is_ready(led.port)) {
		return;
	}

	if (!device_is_ready(adc_dev)) {
		return;
	}

	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
	if (ret < 0) {
		return;
	}

	ret = adc_channel_setup(adc_dev, &channel_cfg);
	if (ret != 0) {
		printk("ADC adc_channel_setup failed with error %d \n", ret);
		return;
	}
}

double measure_dust_single(void){
    int ret;

    // TURN ON LED
	ret = gpio_pin_set_dt(&led, 1);
	if (ret < 0) {
		return;
	}

	// WAIT BEFORE MEASUREMENT
	k_usleep(WAIT_TIME_BEFORE_SAMPLE_US);

	// MEASURE SAMPLE
	ret = adc_read(adc_dev, &sequence);
	if (ret != 0) {
		printk("ADC reading failed with error %d \n", ret);
		return;
	}

	// WAIT FOR MEASUREMENT
	k_usleep(WAIT_TIME_FOR_MEASUREMENT_US);

	// CALCULATE VALUE
	int32_t mv_value = sample_buffer[0];
	//printk("ADC: %d ... ", mv_value);
	int32_t adc_vref = adc_ref_internal(adc_dev);
	adc_raw_to_millivolts(adc_vref, ADC_GAIN, ADC_RESOLUTION, &mv_value);
	//printk("U0: %d mV ... ", mv_value);
	double mv_val_real = mv_value > 0 ? (double)mv_value/0.2040816327 : 0;
	//printk("Ucc: %.2f mV \n", mv_val_real);

	// TURN OFF LED
	ret = gpio_pin_set_dt(&led, 0);
	if (ret < 0) {
		return;
	}

    k_usleep(WAIT_TIME_PERIOD);

    return mv_val_real;

}

double measure_dust(void){
    double measurement = 0;

    for (size_t i = 0; i < 10; i++)
    {
        measurement += measure_dust_single();
    }

    double average_measurement = measurement/10;
    double dust_level = 0.17 * average_measurement/1000 - 0.1;

    printk("Dust: %.2f mV ... %f ug/m3\n", average_measurement, dust_level);
	return dust_level;
}
