#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"
#include "esp_timer.h"

#define GPIO_EXPANDER_ADD 0x20
#define DELAY 9.926543578
#define DELAY_MIN 9.8220249091
#define CHANNEL_COUNT 8
#define POWER_COUNT 6
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 received_channel;
uint8_t received_power;
volatile uint8_t read_value;
static const char *TAG = "dimmer_demo";
static void app_init(void);
static void uart_task(void *pvParameters);
typedef struct
{
    uint8_t channel;
    uint8_t power;
} channel_power;

channel_power ch[CHANNEL_COUNT];
uint8_t power_channel[CHANNEL_COUNT] = {0};
uint8_t channel_buffer[CHANNEL_COUNT] = {-1};
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);
}
void timer_callback(void *param)
{
    if (xSemaphoreTake(ProcessSignal_on, portMAX_DELAY))
    {
        writePin(0, HIGH);
        xSemaphoreTakeFromISR(ProcessSignal_on, &xHigherPriorityTaskWoken);
        writePin(0, LOW);
        xSemaphoreTakeFromISR(ProcessSignal_off, &xHigherPriorityTaskWoken);
    }
}

void timer_callback_channel_2(void *param)
{
    if (xSemaphoreTake(ProcessSignal_on, portMAX_DELAY))
    {
        writePin(2, HIGH);
        xSemaphoreTakeFromISR(ProcessSignal_on, &xHigherPriorityTaskWoken);
        writePin(2, LOW);
        xSemaphoreTakeFromISR(ProcessSignal_off, &xHigherPriorityTaskWoken);
    }
}
void Channel_timer_init()
{
    esp_timer_create_args_t my_timer_args = {
        .callback = &timer_callback,
        .name = "My Timer"};
    esp_timer_handle_t timer_handler;
    ESP_ERROR_CHECK(esp_timer_create(&my_timer_args, &timer_handler));
    ESP_ERROR_CHECK(esp_timer_start_periodic(timer_handler, 1000));
}
void Channel2_timer_init()
{
    esp_timer_create_args_t my_timer_args_2 = {
        .callback = &timer_callback_channel_2,
        .name = "My Timer_2"};
    esp_timer_handle_t timer_handler_2;
    ESP_ERROR_CHECK(esp_timer_create(&my_timer_args_2, &timer_handler_2));
    ESP_ERROR_CHECK(esp_timer_start_periodic(timer_handler_2, 312));
}
static void power(uint8_t power, uint8_t channel)
{

    for (int i = 0; i < 8; i++)
    {
        if (i == channel)
        {
            // channel_buffer[i] = i;
            switch (power)
            {
            case 0:
                // ch[i].channel = i;
                // ch[i].power = 0;
                power_channel[i] = 0;
                break;
            case 1:
                // ch[i].channel = i;
                // ch[i].power = 25;
                power_channel[i] = 25;
                break;
            case 2:
                // ch[i].channel = i;
                // ch[i].power = 35;
                power_channel[i] = 35;
                break;
            case 3:
                // ch[i].channel = i;
                // ch[i].power = 55;
                power_channel[i] = 55;
                break;
            case 4:
                // ch[i].channel = i;
                // ch[i].power = 75;
                power_channel[i] = 75;
                break;
            case 5:
                // ch[i].channel = i;
                // ch[i].power = 100;
                power_channel[i] = 100;
                break;
            default:
                break;
            }
            printf("Channel No : %d\r\n", i);
            printf("Fan Speed: %d\n", power_channel[i]);
            if (power == 0)
            {
                // channel_buffer[i] = -1;
                setPower(ptr_dimmer[channel_buffer[i]], power_channel[i]);
                setState(ptr_dimmer[channel_buffer[i]], OFF);
            }
            else
            {
                setPower(ptr_dimmer[channel_buffer[i]], power_channel[i]);
                setState(ptr_dimmer[channel_buffer[i]], 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);
                    received_channel = atoi((char *)dtmp);
                    printf("%d\r\n", received_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)
                    {
                        received_power = atoi((char *)dtmp_1);
                        printf("%d\r\n", received_power);
                        switch (received_channel)
                        {
                        case 0:
                            channel_buffer[0] = 0;
                            power(received_power, received_channel);
                            Channel_timer_init();
                            break;
                        case 1:
                            channel_buffer[1] = 1;
                            power(received_power, received_channel);
                            break;
                        case 2:
                            channel_buffer[2] = 2;
                            power(received_power, received_channel);
                            Channel2_timer_init();
                            break;
                        case 3:
                            channel_buffer[3] = 3;
                            power(received_power, received_channel);
                            break;
                        case 4:
                            channel_buffer[4] = 4;
                            power(received_power, received_channel);
                            break;
                        case 5:
                            channel_buffer[5] = 5;
                            power(received_power, received_channel);
                            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)
//     {
//     }
// }

/*
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 < 8; 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);
    // }
}
