#include <stdio.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_intr_alloc.h"
#include "esp32/rom/uart.h"
  
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"

nvs_handle_t  my_handle;
int32_t       e_read_data;
int32_t       write_data;
char          epr_addr[6] = "data1";

#define LED_D GPIO_NUM_5
#define LED_C GPIO_NUM_4
#define LED_B GPIO_NUM_15
#define LED_A GPIO_NUM_2

#define BTN_PIN  GPIO_NUM_18

bool led_value = false;
    
unsigned char sc,mn,hr;

#define EX_UART_NUM   UART_NUM_0
#define BUF_SIZE      1024

// Both definition are same and valid
static intr_handle_t handle_console;

// Receive buffer to collect incoming data
uint8_t rxbuf[256];

char g_str[64];

uint16_t i;
static void IRAM_ATTR uart_intr_handle(void *arg)
{
    uint16_t rx_fifo_len, status;
    
    status = UART0.int_st.val; // read UART interrupt Status
    rx_fifo_len = UART0.status.rxfifo_cnt; // read number of bytes in UART buffer
    
    while(rx_fifo_len)
    {
        rxbuf[i++] = UART0.fifo.rw_byte; // read all bytes
        rx_fifo_len--;
    }
    if(rxbuf[i-1]==10)
    {
        rxbuf[i-1]='\0';
        memcpy(g_str, rxbuf,i);
        i=0;
        rxbuf[i]='\0';
    }
    // after reading bytes from buffer clear UART interrupt status
    uart_clear_intr_status(EX_UART_NUM, UART_RXFIFO_FULL_INT_CLR|UART_RXFIFO_TOUT_INT_CLR);
}

void fn_Led_Task(void *pvParameter)
{
    nvs_stats_t nvs_s;

    gpio_pad_select_gpio(BTN_PIN);
    gpio_set_direction(BTN_PIN, GPIO_MODE_INPUT);
    gpio_set_pull_mode(BTN_PIN, GPIO_PULLUP_ONLY);

    gpio_pad_select_gpio(LED_A);
    gpio_set_direction(LED_A, GPIO_MODE_OUTPUT);
    gpio_pad_select_gpio(LED_B);
    gpio_set_direction(LED_B, GPIO_MODE_OUTPUT);
    gpio_pad_select_gpio(LED_C);
    gpio_set_direction(LED_C, GPIO_MODE_OUTPUT);
    gpio_pad_select_gpio(LED_D);
    gpio_set_direction(LED_D, GPIO_MODE_OUTPUT);
    
    gpio_set_level(LED_A, HIGH); 
    gpio_set_level(LED_B, HIGH); 
    gpio_set_level(LED_C, HIGH); 
    gpio_set_level(LED_D, HIGH); 
                
    while(1)
    {
        if(strcmp(g_str,"AON")==0)
            gpio_set_level(LED_A, LOW);
        else if(strcmp(g_str,"AOF")==0)
            gpio_set_level(LED_A, HIGH);
        else if(strcmp(g_str,"BON")==0)
            gpio_set_level(LED_B, LOW);
        else if(strcmp(g_str,"BOF")==0)
            gpio_set_level(LED_B, HIGH);
        else if(strcmp(g_str,"CON")==0)
            gpio_set_level(LED_C, LOW);
        else if(strcmp(g_str,"COF")==0)
            gpio_set_level(LED_C, HIGH);
        else if(strcmp(g_str,"DON")==0)
            gpio_set_level(LED_D, LOW);
        else if(strcmp(g_str,"DOF")==0)
            gpio_set_level(LED_D, HIGH);
        else if((g_str[0]=='G')&&(g_str[1]=='T'))
        {
            String saat;
            saat=ESP32Time_getTime("%A, %B %d %Y %H:%M:%S\n");
            saat.toCharArray(g_str, (saat.length() + 1));
            printf(g_str);
        }
        else if((g_str[0]=='S')&&(g_str[1]=='T'))
        {
            hr=((g_str[2]-'0')*10)+(g_str[3]-'0');
            mn=((g_str[4]-'0')*10)+(g_str[5]-'0');
            sc=((g_str[6]-'0')*10)+(g_str[7]-'0');
            ESP32Time_setTime(sc, mn, hr, 20, 8, 2022, 0);
            printf("st");
        }
        else if((g_str[0]=='E')&&(g_str[1]=='W'))
        {
            epr_addr[4]=g_str[2];
            write_data = ((g_str[3] - '0')*1000)+((g_str[4] - '0')*100)+((g_str[5] - '0')*10)+((g_str[6] - '0')*1);
            nvs_set_i32(my_handle, epr_addr, write_data);
            if(nvs_commit(my_handle)==ESP_OK)
                printf("EW Ok\n");
            else
                printf("EW HATA!\n");
        }
        else if((g_str[0]=='E')&&(g_str[1]=='R'))
        {
            e_read_data = 0;
            epr_addr[4]=g_str[2];
            nvs_get_i32(my_handle, epr_addr, &e_read_data);
            printf("Read data: %d \n", e_read_data);
            printf("ER Ok\n");
        }
        else if((g_str[0]=='E')&&(g_str[1]=='C'))
        {
            if(nvs_erase_all(my_handle)==ESP_OK)
                printf("ERASE Ok\n");
             else
                printf("ERASE HATA\n");
        }
        else if((g_str[0]=='E')&&(g_str[1]=='K'))
        {
            nvs_close(my_handle);
            printf("nvs closed!\n");
        }
        else if((g_str[0]=='E')&&(g_str[1]=='E'))
        {
            if(nvs_get_stats(NULL, &nvs_s)==ESP_OK)
            {
                printf("used_entries: %d \n", nvs_s.used_entries);
                printf("free_entries: %d \n", nvs_s.free_entries);
                printf("total_entries: %d \n", nvs_s.total_entries);
                printf("namespace_count: %d \n", nvs_s.namespace_count);
                printf("EE Ok\n");
            }
            else
                printf("EE HATA!\n");
        }
        else
        {
            if(gpio_get_level(BTN_PIN))
                gpio_set_level(LED_A, HIGH);
            else
                gpio_set_level(LED_A, LOW);
        }
        g_str[0]='\0';


        vTaskDelay(20 / portTICK_PERIOD_MS);
    }
}

extern "C" void app_main()
{
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
    };
    ESP_ERROR_CHECK(uart_param_config(EX_UART_NUM, &uart_config));
    //Set UART pins (using UART0 default pins ie no changes.)
    ESP_ERROR_CHECK(uart_set_pin(EX_UART_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
    //Install UART driver, and get the queue.
    ESP_ERROR_CHECK(uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, 0, 0, NULL, 0));
    // release the pre registered UART handler/subroutine
    ESP_ERROR_CHECK(uart_isr_free(EX_UART_NUM));
    // register new UART subroutine
    ESP_ERROR_CHECK(uart_isr_register(EX_UART_NUM,uart_intr_handle, NULL, ESP_INTR_FLAG_IRAM, &handle_console));
    // enable RX interrupt
    ESP_ERROR_CHECK(uart_enable_rx_intr(EX_UART_NUM));
  
    nvs_flash_init();
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    uart_write_bytes(EX_UART_NUM, (const char*) "AT Okey.\n", 9);
    nvs_open("storage", NVS_READWRITE, &my_handle);

    xTaskCreate(&fn_Led_Task, "Led Task", 2048, NULL, 4, NULL);
}