#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/pcnt.h"
#include "esp_log.h"

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

static const char *TAG = "H2201_buttons";

static QueueHandle_t H2201_button_event_queue = NULL;
static QueueHandle_t H2201_encoder_volume_queue = NULL;

static uint32_t encoder_pcnt_unit = 0;

void H2201_shiftregister_read(void);

static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    uint32_t gpio_num = (uint32_t)arg;
    xQueueSendFromISR(H2201_button_event_queue, &gpio_num, NULL);
}

static void H2201_button_task(void *arg)
{
    uint32_t io_num;
    uint32_t encoder_val;
    for (;;)
    {
        if (xQueueReceive(H2201_button_event_queue, &io_num, 10 / portTICK_PERIOD_MS))
        {
            int level = gpio_get_level(io_num);
            // printf("GPIO[%d] intr, val: %d\n", io_num, level);
            if (level == 0)
            {
                H2201_i2c_beep();
            }
        }
        if (xQueueReceive(H2201_encoder_volume_queue, &encoder_val, 10 / portTICK_PERIOD_MS))
        {
            // printf("Encoder value: %zu\n", encoder_val);
            if (encoder_val == 1)
            {
                H2201_i2c_mastervolume_up();
            }
            else
            {
                H2201_i2c_mastervolume_down();
            }
        }
        H2201_shiftregister_read();
    }
}
//******************* 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];

    gpio_set_level(SHIFT_LATCH, 1);
    gpio_set_level(SHIFT_CLOCK, 1);
    gpio_set_level(SHIFT_CLOCK, 0);
    gpio_set_level(SHIFT_LATCH, 0);

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

    printf("1=%d 2=%d 3=%d 4=%d 5=%d 6=%d 7=%d 8=%d\n", dipswitch[1], dipswitch[2], dipswitch[3], dipswitch[4], dipswitch[5], dipswitch[6], dipswitch[7], dipswitch[8]);
}

//******************* buttons ********************************
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();

    H2201_button_event_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(H2201_button_task, "H2201_button_task", 4096, NULL, 10, NULL);

    gpio_install_isr_service(ESP_INTR_FLAG_EDGE);
    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 ********************************

static void IRAM_ATTR H2201_encoder_pcnt_overflow_handler(void *arg)
{
    uint32_t status;
    pcnt_get_event_status(encoder_pcnt_unit, &status);
    if (status & PCNT_EVT_H_LIM)
    {
        uint32_t val = 1;
        xQueueSendFromISR(H2201_encoder_volume_queue, &val, NULL);
    }
    else if (status & PCNT_EVT_L_LIM)
    {
        uint32_t val = 0;
        xQueueSendFromISR(H2201_encoder_volume_queue, &val, NULL);
    }
}

void H2201_encoder_init(void)
{
    H2201_encoder_volume_queue = xQueueCreate(10, sizeof(uint32_t));

    // Configure channel 0
    pcnt_config_t dev_config = {
        .pulse_gpio_num = ROTARY_A_GPIO,
        .ctrl_gpio_num = ROTARY_B_GPIO,
        .channel = PCNT_CHANNEL_0,
        .unit = encoder_pcnt_unit,
        .pos_mode = PCNT_COUNT_DEC,
        .neg_mode = PCNT_COUNT_INC,
        .lctrl_mode = PCNT_MODE_REVERSE,
        .hctrl_mode = PCNT_MODE_KEEP,
        // .counter_h_lim = ROTARY_HIGH_LIMIT,
        // .counter_l_lim = ROTARY_LOW_LIMIT,
        .counter_h_lim = (1),
        .counter_l_lim = (-1),
    };
    if (pcnt_unit_config(&dev_config) != ESP_OK)
    {
        ESP_LOGE(TAG, "config pcnt channel 0 failed");
    }

    // register interrupt handler
    if (pcnt_isr_service_install(0) != ESP_OK)
    {
        ESP_LOGE(TAG, "install isr service failed");
    }
    pcnt_isr_handler_add(dev_config.unit, H2201_encoder_pcnt_overflow_handler, &dev_config.unit);
    // pcnt_event_enable(dev_config.unit, PCNT_EVT_ZERO);
    pcnt_event_enable(dev_config.unit, PCNT_EVT_H_LIM);
    pcnt_event_enable(dev_config.unit, PCNT_EVT_L_LIM);

    /* Configure and enable the input filter */
    if (pcnt_set_filter_value(dev_config.unit, ROTARY_GLITCH_US * 80) != ESP_OK)
    {
        ESP_LOGE(TAG, "set glitch filter failed");
    }
    if (ROTARY_GLITCH_US)
    {
        pcnt_filter_enable(dev_config.unit);
    }
    else
    {
        pcnt_filter_disable(dev_config.unit);
    }

    // PCNT pause and reset value
    pcnt_counter_pause(dev_config.unit);
    pcnt_counter_clear(dev_config.unit);

    // start encoder
    if (pcnt_counter_resume(dev_config.unit) != ESP_OK)
    {
        ESP_LOGE(TAG, "counter start failed");
    }
}