#include "dht.h"
#include "wifi.h"
#include "http.h"
#include "tm1637.h"
#include <cJSON.h>
#include <rom/ets_sys.h>

const gpio_num_t FRQ_CLK = GPIO_NUM_12;
const gpio_num_t FRQ_DTA = GPIO_NUM_14;
const gpio_num_t DHT_CLK = GPIO_NUM_18;
const gpio_num_t DHT_DTA = GPIO_NUM_19;
const gpio_num_t HTTP_CLK = GPIO_NUM_22;
const gpio_num_t HTTP_DTA = GPIO_NUM_23;

static int servoFreq = 0;
static int servoPulseState = 0;

static int indoorTempF = 0;
static int outdoorTempF = 0;

static tm1637_led_t *http_display;
static tm1637_led_t *dht_display;
static tm1637_led_t *freq_display;

static char http_buffer [512];

static void http_error()
{
    for(int i = 0; i < 4; ++i)
    {
        tm1637_set_segment_number(http_display, i, 14, false);
    }
    while(1)
    {
        for (int i = 0; i < 7 ; i++)
        {
            tm1637_set_brightness(http_display, i);
            vTaskDelay(300 / portTICK_PERIOD_MS);
        }
        for (int i = 7; i > 0 ; i--)
		{
			tm1637_set_brightness(http_display, i);
			vTaskDelay(300 / portTICK_PERIOD_MS);
		}
    }
}

static void dht_error()
{
    for(int i = 0; i < 4; ++i)
    {
        tm1637_set_segment_number(dht_display, i, 14, false);
    }
    while(1)
    {
        for (int i = 0; i < 7 ; i++)
        {
            tm1637_set_brightness(dht_display, i);
            vTaskDelay(300 / portTICK_PERIOD_MS);
        }
        for (int i = 7; i > 0 ; i--)
		{
			tm1637_set_brightness(dht_display, i);
			vTaskDelay(300 / portTICK_PERIOD_MS);
		}
    }
}

static int read_dht()
{
    float tempC, humidity;
    if (dht_read_float_data(DHT_TYPE_AM2301, GPIO_NUM_2, &humidity, &tempC) != ESP_OK)
    {
        dht_error();
    }
    int tempF = (tempC * 1.8) + 32.0;
    ESP_LOGI("main", "indoor temp celsius: %f", tempC);
    ESP_LOGI("main", "indoor temp fahrenheit: %i", tempF);
    return tempF;
}

static int get_temp(char* weather)
{
	int tempC = 999;
    const cJSON *temperature = NULL;
    const cJSON *current_weather = NULL;
    cJSON *weather_json = cJSON_Parse(weather);

    if (weather_json == NULL)
    {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL)
        {
        	ESP_LOGE("main", "Error before: %s\n", error_ptr);
        	cJSON_Delete(weather_json);
			http_error();
        }
    }

    current_weather = cJSON_GetObjectItemCaseSensitive(weather_json, "current_weather");
	temperature = cJSON_GetObjectItemCaseSensitive(current_weather, "temperature");

	if (!cJSON_IsNumber(temperature))
	{
		cJSON_Delete(weather_json);
        ESP_LOGE("main", "Temperature NaN");
		http_error();
	}
	else
	{
		tempC = temperature->valuedouble;
		ESP_LOGI("main", "Successfully parsed JSON response.");
	}

    cJSON_Delete(weather_json);
    return tempC;
}

static void main_loop(void *pvParameters)
{
    while(1)
    {
        http_params params = {
            METEO_SERVER,
            METEO_PORT,
            METEO_REQUEST,
            http_buffer,
            512
        };

        if (http_request(params) == 0)
        {
        	http_error();
        }

        int outdoorTempC = get_temp(http_buffer);
        outdoorTempF = (outdoorTempC * 1.8) + 32.0;
        ESP_LOGI("main", "outdoor temp celsius: %i", outdoorTempC);
        ESP_LOGI("main", "outdoor temp fahrenheit: %i", outdoorTempF);
        tm1637_set_number(http_display, outdoorTempF);

        for(int countdown = 300; countdown >= 0; countdown--)
        {
            ESP_LOGI("main", "%d... ", countdown);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
    }
}

static void calcFreq(void *pvParameters)
{
    while(1)
    {
        int tempDiff = indoorTempF - outdoorTempF;
        tempDiff = tempDiff < 0 ? 0 : tempDiff;
        int newFreq = 500 + (tempDiff * 100);
        newFreq = newFreq > 2500 ? 2500 : newFreq;
        servoFreq = servoFreq < 500 ? 500 : servoFreq;
        if (newFreq > servoFreq)
        {
            servoFreq++;
        }
        if (newFreq < servoFreq)
        {
            servoFreq--;
        }
        tm1637_set_number(freq_display, servoFreq);
        vTaskDelay(1 / portTICK_PERIOD_MS);
    }
}

static void dht(void *pvParameters)
{
    while (1)
    {
        indoorTempF = read_dht();
        tm1637_set_number(dht_display, indoorTempF);
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

static void servo(void *pvParameters)
{
    while(1)
    {
        gpio_set_level(GPIO_NUM_4, servoPulseState);
        servoPulseState = servoPulseState == 0 ? 1 : 0;
        ets_delay_us(servoFreq);
    }
}

void app_main(void)
{
    gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
    
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    wifi_init_sta();

    http_display = tm1637_init(HTTP_CLK, HTTP_DTA);
    dht_display = tm1637_init(DHT_CLK, DHT_DTA);
    freq_display = tm1637_init(FRQ_CLK, FRQ_DTA);
	tm1637_set_brightness(http_display, 5);
    tm1637_set_brightness(dht_display, 5);

    xTaskCreate(&main_loop, "main_loop", 4096, NULL, 5, NULL);
    xTaskCreate(&calcFreq, "calcFreq", 4096, NULL, 5, NULL);
    xTaskCreate(&servo, "servo", 4096, NULL, 5, NULL);
    xTaskCreate(&dht, "dht", 4096, NULL, 5, NULL);
}
4-Digit Display
4-Digit Display
4-Digit Display