#include "esp32idfDimmer.h"
#include "PI4IOE5V96248.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_err.h"

#define GPIO_EXPANDER_ADD 0x20
#define DELAY 9.926543578
static QueueHandle_t uart0_queue;
TaskHandle_t uart_verify = NULL;
uart_event_t event;
dimmertyp *ptr_dimmer[MAX_CHANNEL] = {NULL};
SemaphoreHandle_t ProcessSignal_on = NULL;
SemaphoreHandle_t ProcessSignal_off = NULL;
TaskHandle_t dimming_f_off = NULL;
TaskHandle_t dimming_f_on = NULL;

// Todo
BaseType_t xHigherPriorityTaskWoken = pdFALSE;

static char *token_u = NULL;
static char *delim_h = "#";
static const char *TAG = "dimmer_demo";
static void app_init(void);
static void uart_task(void *pvParameters);
struct
{
    uint16_t channel;
    uint32_t power;
} channel_power;

static void app_init(void)
{
    /*
        Zero-Crossing detector PIN configuration
        Mode-> input
        Int_Edge -> Positive
        Pullup -> Enable
    */
    gpio_config_t io_conf =
        {
            .intr_type = GPIO_INTR_POSEDGE,
            .mode = GPIO_MODE_INPUT,
            .pin_bit_mask = (1ULL << ZEROCROSS_GPIO),
            .pull_down_en = 0,
            .pull_up_en = 1,
        };
    gpio_config(&io_conf);

    /*
        PWM channel pin configuration
        GPIO Mode -> OUTPUT
        PULLUP/PULLDOWN-> DIABLE
    */
    for (int i = 0; i < MAX_CHANNEL; i++)
    {
        gpio_config_t io_conf_2 =
            {
                .intr_type = GPIO_INTR_DISABLE,
                .mode = GPIO_MODE_OUTPUT,
                .pin_bit_mask = (1ULL << channel_pins[i]),
                .pull_down_en = 0,
                .pull_up_en = 0,
            };
        gpio_config(&io_conf_2);
    }

    for (int i = 0; i < MAX_CHANNEL; i++)
    {
        ptr_dimmer[i] = createDimmer(channel_pins[i], ZEROCROSS_GPIO);

        if (ptr_dimmer[i] != NULL)
        {
            begin(ptr_dimmer[i], NORMAL_MODE, OFF);
        }
        else
        {
            ESP_LOGI(TAG, "Creted NULL dimming channel!");
            while (1)
                ;
        }
    }

    // Configure UART
    static const uart_config_t uart_config = {
        .baud_rate = UART_BDR,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT};
    ESP_ERROR_CHECK(uart_param_config(UART_PORT_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_PORT_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
    uart_driver_install(UART_PORT_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 10, &uart0_queue, 0);
}
static void power(uint32_t power)
{
    switch (power)
    {
    case 0:
        channel_power.power = 0;
        break;
    case 1:
        channel_power.power = 25;
        break;
    case 2:
        channel_power.power = 35;
        break;
    case 3:
        channel_power.power = 55;
        break;
    case 4:
        channel_power.power = 75;
        break;
    case 5:
        channel_power.power = 100;
        break;
    default:
        break;
    }
    printf("Channel No : %d\r\n", channel_power.channel);
    printf("Fan Speed: %ld\n", channel_power.power);
    setPower(ptr_dimmer[channel_power.channel], channel_power.power);
    setState(ptr_dimmer[channel_power.channel], ON);
}
static void uart_task(void *pvParameters)
{
    uint8_t *dtmp = (uint8_t *)malloc(BUF_SIZE);

    for (;;)
    {
        // Waiting for UART event.
        if (xQueueReceive(uart0_queue, (void *)&event, 1000 / portTICK_PERIOD_MS))
        {
            bzero(dtmp, BUF_SIZE);

            switch (event.type)
            {
                // Event of UART receving data
            case UART_DATA:
                int len = uart_read_bytes(UART_NUM_0, dtmp, BUF_SIZE, 1000 / portTICK_PERIOD_MS);
                if (len > 0)
                {
                    // token_u = strtok((char *)dtmp, delim_h);
                    channel_power.channel = atoi((char *)dtmp);
                    printf("%d\r\n", channel_power.channel);
                    uint8_t *dtmp_1 = (uint8_t *)malloc(BUF_SIZE);
                    bzero(dtmp_1, BUF_SIZE);
                    int len_1 = uart_read_bytes(UART_NUM_0, dtmp_1, BUF_SIZE, 1000 / portTICK_PERIOD_MS);
                    if (len_1 > 0)
                    {
                        channel_power.power = atoi((char *)dtmp_1);
                        printf("%ld\r\n", channel_power.power);
                        switch (channel_power.channel)
                        {
                        case 0:
                            power(channel_power.power);
                            break;
                        case 1:
                            power(channel_power.power);
                            break;
                        case 2:
                            power(channel_power.power);
                            break;
                        case 3:
                            power(channel_power.power);
                            break;
                        case 4:
                            power(channel_power.power);
                            break;
                        case 5:
                            power(channel_power.power);
                            break;
                        default:
                            printf("Enter valid in beetween 0 to 5\n");
                            break;
                        }
                    }
                }
                break;
            default:
                break;
            }
        }
    }
}

/*
Task function to set Expander GPIO level HIGH
Task Block untile receive signal from ISR to set HIGH
*/
void dimming_fun_on(void *arg)
{
    while (1)
    {
        if (xSemaphoreTake(ProcessSignal_on, portMAX_DELAY))
        {
            if (channel_power.power == 0)
            {
                writePin(channel_power.channel, LOW);
            }
            else if (channel_power.power == 100)
            {
                writePin(channel_power.channel, HIGH);
            }
            else
            {
                writePin(channel_power.channel, HIGH);
                vTaskDelay(DELAY / portTICK_PERIOD_MS);
                xSemaphoreTakeFromISR(ProcessSignal_on, &xHigherPriorityTaskWoken);
                // printf("HIGH\n");
            }
        }
    }
}

/*
Task Function for the Expander GPIO set level HIGH
Untile receive high signal task in block
*/
void dimming_fun_off(void *arg)
{
    while (1)
    {
        if (xSemaphoreTake(ProcessSignal_off, portMAX_DELAY))
        {
            // printf("LOW\n");
            if (channel_power.power == 0)
            {
                writePin(channel_power.channel, LOW);
            }
            else if (channel_power.power == 100)
            {
                writePin(channel_power.channel, HIGH);
            }
            else
            {
                writePin(channel_power.channel, LOW);
                xSemaphoreTakeFromISR(ProcessSignal_off, &xHigherPriorityTaskWoken);
            }
        }
    }
}

void app_main()
{
    app_init();
    i2c_init();
    i2c_scanner();
    begin_expander(GPIO_EXPANDER_ADD);

    // Semaphore create for dimming on
    ProcessSignal_on = xSemaphoreCreateBinary();
    if (ProcessSignal_on == NULL)
    {
        printf("Failed to create Binary Semaphore ProccessSignal_on!\r\n");
        vSemaphoreDelete(ProcessSignal_on);
    }

    // Semaphore create for dimming off
    ProcessSignal_off = xSemaphoreCreateBinary();
    if (ProcessSignal_off == NULL)
    {
        printf("Failed to create Binary Semaphore ProccessSignal_off!\r\n");
        vSemaphoreDelete(ProcessSignal_off);
    }

    xTaskCreate(uart_task, "UART Task", 4096, NULL, 10, &uart_verify);
    if (uart_verify == NULL)
    {
        printf("UART TASK Create Fail /n");
        vTaskDelete(uart_verify);
    }

    // dimming function task for on
    xTaskCreate(dimming_fun_on, "Dimming functionality on", 4096, NULL, 9, &dimming_f_on);
    if (dimming_f_on == NULL)
    {
        printf("Dimming On Task Fail to create\n");
        vTaskDelete(dimming_f_on);
    }

    // dimming function task for off
    xTaskCreate(dimming_fun_off, "Dimming functionality off", 4096, NULL, 8, &dimming_f_off);
    if (dimming_f_off == NULL)
    {
        printf("Dimming Off Task Fail to create\n");
        vTaskDelete(dimming_f_off);
    }

    // wait
    // while (1)
    // {
    //     vTaskDelay(20 / portTICK_PERIOD_MS);
    // }
}
