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

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

uint8_t master_volume = MASTER_VOLUME_0DB;
uint8_t telephone_volume = TELEPHONE_VOLUME_0DB;
uint8_t pc_volume = PC_VOLUME_0DB;
uint8_t bluetooth_volume = BLUETOOTH_VOLUME_0DB;
uint32_t current_button = 0;

static void h2201_volume_add(uint8_t *volume, uint8_t limit)
{
    if (*volume < limit)
    {
        *volume = *volume + 1;
    }
}
static void h2201_volume_subtract(uint8_t *volume)
{
    if (*volume > 0x00)
    {
        *volume = *volume - 1;
    }
}

static void h2201_buttons_eventhandler(uint16_t event, void *p_param)
{
    ESP_LOGI(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)
        {
            current_button = io_num;
            if (current_button == MASTER_VOLUME_BUTTON)
            {
                H2201_i2c_doublebeep();
            }
            else
            {
                H2201_i2c_beep();
            }
        }
        break;
    case ENCODER_UP_EVENT:
        if (current_button == MASTER_VOLUME_BUTTON)
        {
            h2201_volume_add(&master_volume, MASTER_VOLUME_0DB);
            printf("m add vol %u\n", master_volume);
            H2201_i2c_mastervolume(&master_volume);
        }
        if (current_button == TELEPHONE_VOLUME_BUTTON)
        {
            h2201_volume_add(&telephone_volume, TELEPHONE_VOLUME_0DB);
            H2201_i2c_telephonevolume(&telephone_volume);
        }
        if (current_button == PC_VOLUME_BUTTON)
        {
            h2201_volume_add(&pc_volume, PC_VOLUME_0DB);
            H2201_i2c_pcvolume(&pc_volume);
        }
        if (current_button == BLUETOOTH_VOLUME_BUTTON)
        {
            printf("bt add vol %u\n", bluetooth_volume);
            h2201_volume_add(&bluetooth_volume, BLUETOOTH_VOLUME_0DB);
            H2201_i2c_bluetoothvolume(&bluetooth_volume);
        }
        break;
    case ENCODER_DOWN_EVENT:
        if (current_button == MASTER_VOLUME_BUTTON)
        {
            h2201_volume_subtract(&master_volume);
            printf("m sub vol %u\n", master_volume);
            H2201_i2c_mastervolume(&master_volume);
        }
        if (current_button == TELEPHONE_VOLUME_BUTTON)
        {
            h2201_volume_subtract(&telephone_volume);
            H2201_i2c_telephonevolume(&telephone_volume);
        }
        if (current_button == PC_VOLUME_BUTTON)
        {
            h2201_volume_subtract(&pc_volume);
            H2201_i2c_pcvolume(&pc_volume);
        }
        if (current_button == BLUETOOTH_VOLUME_BUTTON)
        {
            printf("bt sub vol %u\n", bluetooth_volume);
            h2201_volume_subtract(&bluetooth_volume);
            H2201_i2c_bluetoothvolume(&bluetooth_volume);
        }
        break;
    default:
        ESP_LOGE(H2201_BUTTONS_TAG, "%s unhandled evt %d", __func__, event);
        break;
    }
}
//******************* 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 H2201_buttons_ISR(void *arg)
{
    uint32_t io_num = (uint32_t)arg;
    h2201_dispatcher_dowork_fromISR(h2201_buttons_eventhandler, BUTTON_PRESS_EVENT, &io_num, sizeof(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 << MASTER_VOLUME_BUTTON) | (1ULL << TELEPHONE_VOLUME_BUTTON) | (1ULL << PC_VOLUME_BUTTON) | (1ULL << BLUETOOTH_VOLUME_BUTTON));

    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(0); // no flags needed
    gpio_isr_handler_add(MASTER_VOLUME_BUTTON, H2201_buttons_ISR, (void *)MASTER_VOLUME_BUTTON);
    gpio_isr_handler_add(TELEPHONE_VOLUME_BUTTON, H2201_buttons_ISR, (void *)TELEPHONE_VOLUME_BUTTON);
    gpio_isr_handler_add(PC_VOLUME_BUTTON, H2201_buttons_ISR, (void *)PC_VOLUME_BUTTON);
    gpio_isr_handler_add(BLUETOOTH_VOLUME_BUTTON, H2201_buttons_ISR, (void *)BLUETOOTH_VOLUME_BUTTON);
}

//******************* encoder ********************************

static void IRAM_ATTR H2201_encoder_ISR(void *args)
{
    rotary_encoder_info_t *info = (rotary_encoder_info_t *)args;
    uint8_t event = 0;
    if (info != NULL)
    {
        // Get state of input pins.
        uint8_t pin_state = (gpio_get_level(info->pin_b) << 1) | gpio_get_level(info->pin_a);

        // Determine new state from the pins and state table.
#ifdef ROTARY_ENCODER_DEBUG
        uint8_t old_state = info->table_state;
#endif
        info->table_state = info->table[info->table_state & 0xf][pin_state];

        // Return emit bits, i.e. the generated event.
        event = info->table_state & 0x30;
#ifdef ROTARY_ENCODER_DEBUG
        ESP_EARLY_LOGD(TAG, "BA %d%d, state 0x%02x, new state 0x%02x, event 0x%02x", pin_state >> 1, pin_state & 1, old_state, info->table_state, event);
#endif
    }

    if (event == DIR_CW)
    {
        if (h2201_dispatcher_dowork_simple_fromISR(h2201_buttons_eventhandler, ENCODER_DOWN_EVENT))
        {
            portYIELD_FROM_ISR();
        }
    }
    if (event == DIR_CCW)
    {
        if (h2201_dispatcher_dowork_simple_fromISR(h2201_buttons_eventhandler, ENCODER_UP_EVENT))
        {
            portYIELD_FROM_ISR();
        }
    }
}

rotary_encoder_info_t encoder_info;

void H2201_encoder_init(void)
{
    encoder_info.pin_a = ROTARY_A_GPIO;
    encoder_info.pin_b = ROTARY_B_GPIO;
    encoder_info.table = &H2201_encoder_transition_table[0];
    encoder_info.table_state = R_START;

    // configure GPIOs
    gpio_config_t io_conf = {};
    io_conf.intr_type = GPIO_INTR_ANYEDGE;
    io_conf.pin_bit_mask = ((1ULL << encoder_info.pin_a) | (1ULL << encoder_info.pin_b));
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
    gpio_config(&io_conf);

    // install interrupt handlers
    gpio_isr_handler_add(encoder_info.pin_a, H2201_encoder_ISR, &encoder_info);
    gpio_isr_handler_add(encoder_info.pin_b, H2201_encoder_ISR, &encoder_info);
}