#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "driver/pulse_cnt.h"
#include "esp_log.h"

#include "H2201_dispatcher.h"
#include "H2201_buttons.h"
#include "H2201_i2c.h"

// static QueueHandle_t H2201_button_event_queue = NULL;
// static QueueHandle_t H2201_encoder_volume_queue = NULL;
static QueueHandle_t h2201_encoder_queue = NULL;
static TaskHandle_t h2201_encoder_task_handle = NULL;
// static uint32_t encoder_pcnt_unit = 0;

//#define UP 1
//#define DOWN 0

volatile uint8_t master_volume = 0xE7;

static void h2201_buttons_eventhandler(uint16_t event, void *p_param)
{
    ESP_LOGD(H2201_BUTTONS_TAG, "%s evt %d", __func__, event);
    switch (event)
    {
    case BUTTON_PRESS_EVENT:;
        uint32_t io_num = *((uint32_t *)(p_param));
        printf("button press %u\n", io_num);
        if (gpio_get_level(io_num) == 0)
        {
            H2201_i2c_beep();
        }
        break;
    case ENCODER_UP_EVENT:
        if (master_volume < 0xE7)
        {
            master_volume++;
        }
        // uint8_t volume = *((uint8_t *)(p_param));
        // printf("volume %u\n", master_volume);
        H2201_i2c_mastervolume(master_volume);
        break;
    case ENCODER_DOWN_EVENT:
        if (master_volume > 0x03)
        {
            master_volume--;
        }
        // uint8_t volume = *((uint8_t *)(p_param));
        // printf("volume %u\n", master_volume);
        H2201_i2c_mastervolume(master_volume);
        break;
    default:
        ESP_LOGE(H2201_BUTTONS_TAG, "%s unhandled evt %d", __func__, event);
        break;
    }
}

//         // printf("1=%d ", H2201_dipswitch_read(1));
//         // printf("2=%d ", H2201_dipswitch_read(2));
//         // printf("3=%d ", H2201_dipswitch_read(3));
//         // printf("4=%d ", H2201_dipswitch_read(4));
//         // printf("5=%d ", H2201_dipswitch_read(5));
//         // printf("6=%d ", H2201_dipswitch_read(6));
//         // printf("7=%d ", H2201_dipswitch_read(7));
//         // printf("8=%d\n", H2201_dipswitch_read(8));
//     }
// }
//******************* shiftregsiter ********************************
void H2201_shiftregister_init(void)
{
    gpio_set_direction(SHIFT_CLOCK, GPIO_MODE_OUTPUT);
    gpio_set_direction(SHIFT_LATCH, GPIO_MODE_OUTPUT);
    gpio_set_direction(SHIFT_DATA, GPIO_MODE_INPUT);
}

void H2201_shiftregister_read(bool dipswitch[9])
{
    // clock inputs in registers
    gpio_set_level(SHIFT_LATCH, 1);
    gpio_set_level(SHIFT_CLOCK, 1);
    gpio_set_level(SHIFT_CLOCK, 0);
    gpio_set_level(SHIFT_LATCH, 0);

    // get de bits
    for (size_t i = 8; i > 0; --i)
    {
        gpio_set_level(SHIFT_CLOCK, 1);
        dipswitch[i] = gpio_get_level(SHIFT_DATA);
        gpio_set_level(SHIFT_CLOCK, 0);
    }
}

bool H2201_dipswitch_read(int sw)
{
    if (sw < 0 || sw > 8)
    {
        return false;
    }
    bool dipswitch[9];
    H2201_shiftregister_read(dipswitch);
    return dipswitch[sw];
}

//******************* buttons ********************************

static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    uint32_t io_num = (uint32_t)arg;
    // if (gpio_get_level(io_num) == 0)
    //{
    // h2201_dispatcher_add_event(BUTTON_EVENTS, BUTTON_PRESS_EVENT, io_num);
    h2201_dispatcher_work_dispatch_fromISR(h2201_buttons_eventhandler, BUTTON_PRESS_EVENT, &io_num);
    // }
}

void H2201_buttons_init(void)
{
    gpio_config_t io_conf = {};

    io_conf.intr_type = GPIO_INTR_NEGEDGE;
    io_conf.pin_bit_mask = ((1ULL << BUTTON_1) | (1ULL << BUTTON_2) | (1ULL << BUTTON_3) | (1ULL << BUTTON_4));

    io_conf.mode = GPIO_MODE_INPUT;
    gpio_config(&io_conf);

    // Please do not use the interrupt of GPIO36 and GPIO39 when using ADC or Wi-Fi with sleep mode enabled.
    // Please refer to the comments of `adc1_get_raw`.
    // Please refer to section 3.11 of 'ECO_and_Workarounds_for_Bugs_in_ESP32' for the description of this issue.
    // As a workaround, call adc_power_acquire() in the app. This will result in higher power consumption (by ~1mA),
    // but will remove the glitches on GPIO36 and GPIO39.
    adc_power_acquire();

    // gpio_install_isr_service((1L << ESP_INTR_FLAG_EDGE) | (1L << ESP_INTR_FLAG_LEVEL1));
    // gpio_install_isr_service(ESP_INTR_FLAG_EDGE);
    gpio_install_isr_service(0); // no flags needed
    gpio_isr_handler_add(BUTTON_1, gpio_isr_handler, (void *)BUTTON_1);
    gpio_isr_handler_add(BUTTON_2, gpio_isr_handler, (void *)BUTTON_2);
    gpio_isr_handler_add(BUTTON_3, gpio_isr_handler, (void *)BUTTON_3);
    gpio_isr_handler_add(BUTTON_4, gpio_isr_handler, (void *)BUTTON_4);
}
//******************* encoder ********************************
bool IRAM_ATTR example_pcnt_on_reach(pcnt_unit_handle_t unit, pcnt_watch_event_data_t *edata, void *user_ctx)
{
    if (edata->watch_point_value == 1)
    {
        return h2201_dispatcher_dowork_fromISR(h2201_buttons_eventhandler, ENCODER_UP_EVENT);
    }
    else
    {
        return h2201_dispatcher_dowork_fromISR(h2201_buttons_eventhandler, ENCODER_DOWN_EVENT);
    }
}

void H2201_encoder_init(void)
{
    pcnt_unit_config_t unit_config = {
        .high_limit = ROTARY_HIGH_LIMIT,
        .low_limit = ROTARY_LOW_LIMIT,
    };
    pcnt_unit_handle_t pcnt_unit = NULL;
    ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));

    pcnt_glitch_filter_config_t filter_config = {
        .max_glitch_ns = ROTARY_GLITCH_NS,
    };
    ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config));

    pcnt_chan_config_t chan_a_config = {
        .edge_gpio_num = ROTARY_A_GPIO,
        .level_gpio_num = ROTARY_B_GPIO,
    };
    pcnt_channel_handle_t pcnt_chan_a = NULL;
    ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_a_config, &pcnt_chan_a));

    ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
    ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));

    pcnt_chan_config_t chan_b_config = {
        .edge_gpio_num = ROTARY_B_GPIO,
        .level_gpio_num = ROTARY_A_GPIO,
    };
    pcnt_channel_handle_t pcnt_chan_b = NULL;
    ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_b_config, &pcnt_chan_b));

    ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE));
    ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));

    // ESP_LOGI(TAG, "add watch points and register callbacks");
    // int watch_points[] = {ROTARY_LOW_LIMIT, -50, 0, 50, ROTARY_HIGH_LIMIT};
    int watch_points[] = {ROTARY_LOW_LIMIT, ROTARY_HIGH_LIMIT};
    for (size_t i = 0; i < sizeof(watch_points) / sizeof(watch_points[0]); i++)
    {
        ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, watch_points[i]));
    }

    pcnt_event_callbacks_t cbs = {
        .on_reach = example_pcnt_on_reach,
    };

    h2201_encoder_queue = xQueueCreate(10, sizeof(int));
    // ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &cbs, queue));
    ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &cbs, h2201_encoder_queue));

    ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
    ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));

    // xTaskCreate(H2201_encoder_task, "encoderT", 4096, NULL, configMAX_PRIORITIES - 3, &h2201_encoder_task_handle);
}