#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_heap_task_info.h"
#include "esp_log.h"
//#include "esp_hw_log.h"
//#include "esp_event_base.h"

#include "H2201_dispatcher.h"

static QueueHandle_t h2201_dispatcher_queue = NULL;
static TaskHandle_t h2201_dispatcher_task_handle = NULL;

// esp_event_loop_handle_t h2201_dispatcher_eventloop;

static bool h2201_dispatcher_send_msg(h2201_dispatcher_msg_t *msg)
{
    if (msg == NULL)
    {
        return false;
    }

    if (xQueueSend(h2201_dispatcher_queue, msg, 10 / portTICK_PERIOD_MS) != pdTRUE)
    {
        ESP_LOGE(H2201_DISPATCHER_TAG, "%s xQueue send failed", __func__);
        return false;
    }
    return true;
}

bool h2201_dispatcher_work_dispatch(h2201_dispatcher_cb_t p_cback, uint16_t event, void *p_params, int param_len, h2201_dispatcher_copy_cb_t p_copy_cback)
{
    ESP_LOGD(H2201_DISPATCHER_TAG, "%s event 0x%x, param len %d", __func__, event, param_len);

    h2201_dispatcher_msg_t msg;
    memset(&msg, 0, sizeof(h2201_dispatcher_msg_t));

    msg.sig = BT_APP_SIG_WORK_DISPATCH;
    msg.event = event;
    msg.cb = p_cback;

    if (param_len == 0)
    {
        return h2201_dispatcher_send_msg(&msg);
    }
    else if (p_params && param_len > 0)
    {
        if ((msg.param = malloc(param_len)) != NULL)
        {
            memcpy(msg.param, p_params, param_len);
            /* check if caller has provided a copy callback to do the deep copy */
            if (p_copy_cback)
            {
                p_copy_cback(&msg, msg.param, p_params);
            }
            return h2201_dispatcher_send_msg(&msg);
        }
    }

    return false;
}

void h2201_dispatcher_work_dispatch_fromISR(h2201_dispatcher_cb_t p_cback, uint16_t event, void *p_params) //, int param_len)
{
    // ESP_LOGD(H2201_DISPATCHER_TAG, "%s event 0x%x, param len %d", __func__, event, param_len);

    h2201_dispatcher_msg_t msg;
    memset(&msg, 0, sizeof(h2201_dispatcher_msg_t));

    msg.sig = BT_APP_SIG_WORK_DISPATCH;
    msg.event = event;
    msg.cb = p_cback;

    int param_len = 0;
    if (p_params)
    {
        param_len = sizeof(p_params);
    }

    if (param_len == 0)
    {
        if (xQueueSendFromISR(h2201_dispatcher_queue, &msg, NULL) != pdTRUE)
        {
            ESP_LOGE(H2201_DISPATCHER_TAG, "%s xQueue send failed", __func__);
            return;
        }
    }
    else if (p_params && param_len > 0)
    {
        if ((msg.param = malloc(param_len)) != NULL)
        {
            memcpy(msg.param, p_params, param_len);
            if (xQueueSendFromISR(h2201_dispatcher_queue, &msg, NULL) != pdTRUE)
            {
                ESP_LOGE(H2201_DISPATCHER_TAG, "%s xQueue send failed", __func__);
                return;
            }
        }
    }
}


bool h2201_dispatcher_dowork_fromISR(h2201_dispatcher_cb_t p_cback, uint16_t event)
{
    h2201_dispatcher_msg_t msg;
    // memset(&msg, 0, sizeof(h2201_dispatcher_msg_t));

    msg.sig = BT_APP_SIG_WORK_DISPATCH;
    msg.event = event;
    msg.cb = p_cback;
    msg.param = NULL;

    BaseType_t high_task_wakeup;
    if (xQueueSendFromISR(h2201_dispatcher_queue, &msg, &high_task_wakeup) != pdTRUE)
    {
        //ESP_HW_LOGE(H2201_DISPATCHER_TAG, "%s xQueue send failed", __func__);
        esp_rom_printf("%s xQueue send failed\n", __func__);
    }
    return (high_task_wakeup == pdTRUE);
}

// #define MAX_TASK_NUM 20  // Max number of per tasks info that it can store
// #define MAX_BLOCK_NUM 20 // Max number of per block info that it can store
// static size_t s_prepopulated_num = 0;
// static heap_task_totals_t s_totals_arr[MAX_TASK_NUM];
// static heap_task_block_t s_block_arr[MAX_BLOCK_NUM];

// static void esp_dump_per_task_heap_info(void)
// {
//     heap_task_info_params_t heap_info = {0};
//     heap_info.caps[0] = MALLOC_CAP_8BIT; // Gets heap with CAP_8BIT capabilities
//     heap_info.mask[0] = MALLOC_CAP_8BIT;
//     heap_info.caps[1] = MALLOC_CAP_32BIT; // Gets heap info with CAP_32BIT capabilities
//     heap_info.mask[1] = MALLOC_CAP_32BIT;
//     heap_info.tasks = NULL; // Passing NULL captures heap info for all tasks
//     heap_info.num_tasks = 0;
//     heap_info.totals = s_totals_arr; // Gets task wise allocation details
//     heap_info.num_totals = &s_prepopulated_num;
//     heap_info.max_totals = MAX_TASK_NUM;  // Maximum length of "s_totals_arr"
//     heap_info.blocks = s_block_arr;       // Gets block wise allocation details. For each block, gets owner task, address and size
//     heap_info.max_blocks = MAX_BLOCK_NUM; // Maximum length of "s_block_arr"

//     heap_caps_get_per_task_info(&heap_info);

//     for (int i = 0; i < *heap_info.num_totals; i++)
//     {
//         printf("Task: %s -> CAP_8BIT: %d CAP_32BIT: %d\n",
//                heap_info.totals[i].task ? pcTaskGetName(heap_info.totals[i].task) : "Pre-Scheduler allocs",
//                heap_info.totals[i].size[0],  // Heap size with CAP_8BIT capabilities
//                heap_info.totals[i].size[1]); // Heap size with CAP32_BIT capabilities
//     }

//     printf("\n\n");
// }

static void H2201_dispatcher_task(void *arg)
{
    h2201_dispatcher_msg_t msg;
    for (;;)
    {
        if (xQueueReceive(h2201_dispatcher_queue, &msg, (TickType_t)portMAX_DELAY))
        {
            // ESP_LOGI(H2201_DISPATCHER_TAG, "sig 0x%x, event 0x%x, param 0x%x ", msg.sig, msg.callback, msg.param);
            ESP_LOGI(H2201_DISPATCHER_TAG, "sig 0x%x", msg.sig);
            switch (msg.sig)
            {
            case BT_APP_SIG_WORK_DISPATCH:
                // h2201_dispatcher_work_dispatched(&msg);
                if (msg.cb)
                {
                    msg.cb(msg.event, msg.param);
                }
                break;
            default:
                ESP_LOGW(H2201_DISPATCHER_TAG, "%s, unhandled sig: %d", __func__, msg.sig);
                break;
            }

            if (msg.param)
            {
                free(msg.param);
            }

            // esp_dump_per_task_heap_info();
        }
    }
}

void H2201_dispacther_init(void)
{
    h2201_dispatcher_queue = xQueueCreate(20, sizeof(h2201_dispatcher_msg_t));
    xTaskCreate(H2201_dispatcher_task, "DispatcherT", 4096, NULL, configMAX_PRIORITIES - 3, &h2201_dispatcher_task_handle);
}