#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include <esp_timer.h>
//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE

#include "esp_log.h"
#include "driver/gpio.h"
#include "DHT.h"

int DHTgpio = GPIO_NUM_4;
float humidity = 0.;
float temperature = 0.;
unsigned long lastReadTime;

static const char *TAG = "DHT";

float getHumidity() { return humidity; }
float getTemperature() { return temperature; }

void DHT_task(void *pvParameter)
{
    //setDHTgpio(GPIO_NUM_4);
    //ESP_LOGI(TAG, "Starting DHT Task\n\n");
    printf("Starting DHT Task\n");

    while (1)
    {
        printf("=== Reading DHT ===\n");
        int ret = readDHT();
        errorHandler(ret);
        printf("Hum: %.1f Tmp: %.1f\n", getHumidity(), getTemperature());

        // -- wait at least 3 sec before reading again ------------
        // The interval of whole process must be beyond 2 seconds !!
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void app_main() {
  //xTaskCreate(&DHT_task, "DHT_task", 2048, NULL, 5, NULL);
  while (true) {

        printf("=== Reading DHT ===\n");
        int ret = readDHT();
        errorHandler(ret);
        printf("Hum: %.1f Tmp: %.1f\n", getHumidity(), getTemperature());

    vTaskDelay(2000 / portTICK_PERIOD_MS);
  }
}

void errorHandler(int response)
{
  printf("errorHandler\n");
    switch (response)
    {

    case DHT_TIMEOUT_ERROR:
        printf("Sensor Timeout\n");
        ESP_LOGE(TAG, "Sensor Timeout\n");
        break;

    case DHT_CHECKSUM_ERROR:
        printf("CheckSum error\n");
        ESP_LOGE(TAG, "CheckSum error\n");
        break;

    case DHT_OK:

        printf("DHT_OK\n");
        break;

    default:
        printf("Unknown error\n");
        ESP_LOGE(TAG, "Unknown error\n");
    }
}


int getSignalLevel(int usTimeOut, bool state)
{

    int uSec = 0;
    while (gpio_get_level(DHTgpio) == state)
    {

        if (uSec > usTimeOut)
            return -1;

        ++uSec;
        esp_rom_delay_us(1); // uSec delay
    }

    return uSec;
}
#define MAXdhtData 5 // to complete 40 = 5*8 Bits

int readDHT()
{
    int64_t startTime = esp_timer_get_time();

    if (lastReadTime && (unsigned long)(startTime - lastReadTime) < 2000000)
	  {
      printf("Too fast\n");
		  return DHT_CHECKSUM_ERROR;
	  }
	  lastReadTime = startTime;

    int uSec = 0;

    uint8_t dhtData[MAXdhtData];
    uint8_t byteInx = 0;
    uint8_t bitInx = 7;

	  uint16_t rawHumidity = 0;
	  uint16_t rawTemperature = 0;
	  uint16_t data = 0;


    for (int k = 0; k < MAXdhtData; k++)
        dhtData[k] = 0;

    // == Send start signal to DHT sensor ===========

    gpio_set_direction(DHTgpio, GPIO_MODE_OUTPUT);

    // pull down for 3 ms for a smooth and nice wake up
    gpio_set_level(DHTgpio, 0);
    esp_rom_delay_us(18000);

    // pull up for 25 us for a gentile asking for data

    gpio_set_direction(DHTgpio, GPIO_MODE_INPUT); // change to input mode
    gpio_set_level(DHTgpio, 1);
    esp_rom_delay_us(25);

    portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
    portENTER_CRITICAL(&mux);
    for (int8_t i = -3; i < 2 * 40; i++)
	{
		//byte age;
    uint8_t age = 0;
    startTime = esp_timer_get_time();
		do
		{
			age = (unsigned long)(esp_timer_get_time() - startTime);
			if (age > 90)
			{
				//error = ERROR_TIMEOUT;

				portEXIT_CRITICAL(&mux);

        printf("sad\n");
				return DHT_TIMEOUT_ERROR;
			}
		} while (gpio_get_level(DHTgpio) == (i & 1) ? 1 : 0);

		if (i >= 0 && (i & 1))
		{
			// Now we are being fed our 40 bits
			data <<= 1;

			// A zero max 30 usecs, a one at least 68 usecs.
			if (age > 30)
			{
				data |= 1; // we got a one
			}
		}

		switch (i)
		{
		case 31:
			rawHumidity = data;
			break;
		case 63:
			rawTemperature = data;
			data = 0;
			break;
		}
	}

portEXIT_CRITICAL(&mux);

if ((uint8_t)((uint16_t)(rawHumidity + (rawHumidity >> 8) + rawTemperature + (rawTemperature >> 8))) != data) {
    return DHT_CHECKSUM_ERROR;
}
  humidity = rawHumidity * 0.1;
		if (rawTemperature & 0x8000)
		{
			rawTemperature = -(int16_t)(rawTemperature & 0x7FFF);
		}
		temperature = ((int16_t)rawTemperature) * 0.1;
    return DHT_OK;
}