#include "task_manager.h"

#include "esp_log.h"
#include "sdkconfig.h"
#include <stddef.h>

#if CONFIG_BT_NIMBLE_ENABLED
#include "nimble/nimble_port_freertos.h"
#endif

static const char *tag_s = "TASK_MGR";

typedef struct {
    const char *name;
    uint32_t stack_depth;
    UBaseType_t priority;
    int core_id;
} app_task_config_t;

static const app_task_config_t task_cfg_s[APP_TASK_COUNT] = {
    [APP_TASK_DNS_SERVER] = {
        .name = "dns",
        .stack_depth = 3072,
        .priority = 5,
        .core_id = APP_TASK_CORE_NO_AFFINITY,
    },
    [APP_TASK_BUTTON_MONITOR] = {
        .name = "btn_task",
        .stack_depth = 4096,
        .priority = 5,
        .core_id = APP_TASK_CORE_NO_AFFINITY,
    },
    [APP_TASK_BLE_HEARTBEAT] = {
        .name = "ble_hb",
        .stack_depth = 4096,
        .priority = 5,
        .core_id = APP_TASK_CORE_NO_AFFINITY,
    },
    [APP_TASK_MQTT_HEARTBEAT] = {
        .name = "mqtt_hb",
        .stack_depth = 4096,
        .priority = 5,
        .core_id = APP_TASK_CORE_NO_AFFINITY,
    },
    [APP_TASK_MQTT_OTA_REPORT] = {
        .name = "ota_msg_task",
        .stack_depth = 4096,
        .priority = 5,
        .core_id = APP_TASK_CORE_NO_AFFINITY,
    },
    [APP_TASK_MQTT_EXIT] = {
        .name = "mqtt_exit",
        .stack_depth = 512,
        .priority = 5,
        .core_id = APP_TASK_CORE_NO_AFFINITY,
    },
    [APP_TASK_MQTT_INIT] = {
        .name = "mqtt_init",
        .stack_depth = 10240,
        .priority = 5,
        .core_id = APP_TASK_CORE_NO_AFFINITY,
    },
    [APP_TASK_UART_COMM_EXAMPLE] = {
        .name = "uart_comm",
        .stack_depth = 2048,
        .priority = 5,
        .core_id = APP_TASK_CORE_NO_AFFINITY,
    },
};

static esp_err_t app_task_create(const char *name,
                                 TaskFunction_t entry,
                                 uint32_t stack_depth,
                                 void *arg,
                                 UBaseType_t priority,
                                 int core_id,
                                 TaskHandle_t *out_handle)
{
    BaseType_t rc = pdFAIL;

    if (core_id == APP_TASK_CORE_NO_AFFINITY) {
        rc = xTaskCreate(entry, name, stack_depth, arg, priority, out_handle);
    } else {
        rc = xTaskCreatePinnedToCore(entry, name, stack_depth, arg, priority, out_handle, core_id);
    }

    if (rc != pdPASS) {
        ESP_LOGE(tag_s, "create task fail: %s core=%d", name ? name : "unknown", core_id);
        return ESP_FAIL;
    }
    return ESP_OK;
}

esp_err_t app_task_start(app_task_id_t id,
                         TaskFunction_t entry,
                         void *arg,
                         TaskHandle_t *out_handle)
{
    if (id < 0 || id >= APP_TASK_COUNT || entry == NULL) {
        ESP_LOGE(tag_s, "invalid task id=%d", (int)id);
        return ESP_ERR_INVALID_ARG;
    }

    const app_task_config_t *cfg = &task_cfg_s[id];
    return app_task_create(cfg->name, entry, cfg->stack_depth, arg, cfg->priority, cfg->core_id, out_handle);
}

void app_task_stop(TaskHandle_t *handle)
{
    if (handle == NULL || *handle == NULL) {
        return;
    }
    vTaskDelete(*handle);
    *handle = NULL;
}

esp_err_t app_task_start_nimble_host(TaskFunction_t entry)
{
    if (entry == NULL) {
        return ESP_ERR_INVALID_ARG;
    }
#if CONFIG_BT_NIMBLE_ENABLED
    nimble_port_freertos_init(entry);
    return ESP_OK;
#else
    ESP_LOGE(tag_s, "NimBLE is disabled");
    return ESP_ERR_NOT_SUPPORTED;
#endif
}
