#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
#define CHANNEL_COUNT 10
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;
uint8_t receive_power;
uint8_t receive_channel;
uint8_t channel_buffer[CHANNEL_COUNT] = {-1};
uint8_t power_buffer[CHANNEL_COUNT] = {0};

// 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);

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(uint8_t power, uint8_t channel)
{
    for (int i = 0; i < CHANNEL_COUNT; i++)
    {
        if (i == channel)
        {
            switch (power)
            {
            case 0:
                power_buffer[i] = 0;
                break;
            case 1:
                power_buffer[i] = 30;
                break;
            case 2:
                power_buffer[i] = 50;
                break;
            case 3:
                power_buffer[i] = 60;
                break;
            case 4:
                power_buffer[i] = 80;
                break;
            case 5:
                power_buffer[i] = 100;
                break;
            default:
                break;
            }

            if (power == 0)
            {
                printf("Channel No : %d\r\n", channel_buffer[i]);
                printf("Fan Speed: %d\n", power_buffer[i]);
                setPower(ptr_dimmer[channel_buffer[i]], power_buffer[i]);
                setState(ptr_dimmer[channel_buffer[i]], OFF);
            }
            else
            {
                printf("Channel No : %d\r\n", channel_buffer[i]);
                printf("Fan Speed: %d\n", power_buffer[i]);
                setPower(ptr_dimmer[channel_buffer[i]], power_buffer[i]);
                setState(ptr_dimmer[channel_buffer[i]], ON);
            }
        }
        else
        {
            setPower(ptr_dimmer[channel_buffer[i]], power_buffer[i]);
            setState(ptr_dimmer[channel_buffer[i]], OFF);
        }
    }
}
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);
                    receive_channel = atoi((char *)dtmp);
                    printf("%d\r\n", receive_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)
                    {
                        receive_power = atoi((char *)dtmp_1);
                        printf("%d\r\n", receive_power);
                        switch (receive_channel)
                        {
                        case 0:
                            channel_buffer[0] = 0;
                            power(receive_power, receive_channel);
                            break;
                        case 1:
                            channel_buffer[1] = 1;
                            power(receive_power, receive_channel);
                            break;
                        case 2:
                            channel_buffer[2] = 2;
                            power(receive_power, receive_channel);
                            break;
                        case 3:
                            channel_buffer[3] = 3;
                            power(receive_power, receive_channel);
                            break;
                        case 4:
                            channel_buffer[4] = 4;
                            power(receive_power, receive_channel);
                            break;
                        case 5:
                            channel_buffer[5] = 5;
                            power(receive_power, receive_channel);
                            break;
                        default:
                            printf("Enter valid in beetween 0 to 5\n");
                            break;
                        }
                        // printf("Channel No : %d\r\n", receive_channel);
                        // printf("Fan Speed: %ld\n", receive_power);
                        // setPower(ptr_dimmer[channel_power.channel], channel_power.power);
                        // setState(ptr_dimmer[channel_power.channel], ON);
                    }
                }
                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))
        {
            for (int i = 0; i < CHANNEL_COUNT; i++)
            {
                if (power_buffer[i] == 0)
                {
                    writePin(channel_buffer[i], LOW);
                }
                else
                {
                    writePin(channel_buffer[i], HIGH);
                    vTaskDelay(DELAY / portTICK_PERIOD_MS);
                    xSemaphoreTakeFromISR(ProcessSignal_on, &xHigherPriorityTaskWoken);
                }
            }
        }
    }
}

/*
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))
        {
            for (int i = 0; i < CHANNEL_COUNT; i++)
            {
                writePin(channel_buffer[i], 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);
    // }
}
