#include <stdbool.h>
#include <string.h>

#include "esp_event.h"
#include "sdkconfig.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#include "esp_event.h"
#include "esp_event_private.h"
#include "esp_event_internal.h"

#include "esp_heap_caps.h"

#include "sdkconfig.h"
#include "unity.h"

#include "test_utils.h"

typedef struct {
    void* data;
    SemaphoreHandle_t mutex;
} simple_arg_t;

static const char* TAG = "test_event";

ESP_EVENT_DECLARE_BASE(s_default_test_base1);
ESP_EVENT_DECLARE_BASE(s_default_test_base2);

ESP_EVENT_DEFINE_BASE(s_default_test_base1);
ESP_EVENT_DEFINE_BASE(s_default_test_base2);

enum {
    TEST_EVENT_BASE1_EV1,
    TEST_EVENT_BASE1_EV2,
    TEST_EVENT_BASE1_MAX
};

enum {
    TEST_EVENT_BASE2_EV1,
    TEST_EVENT_BASE2_EV2,
    TEST_EVENT_BASE2_MAX
};

// The initial logging "initializing test" is to ensure mutex allocation is not counted against memory not being freed
// during teardown.
#define TEST_SETUP() \
        ESP_LOGI(TAG, "initializing test");

static void test_event_simple_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    if (!event_handler_arg) {
        return;
    }
    simple_arg_t* arg = (simple_arg_t*) event_handler_arg;
    xSemaphoreTake(arg->mutex, portMAX_DELAY);

    int* count = (int*) arg->data;

    if (event_data == NULL) {
        (*count)++;
    } else {
        (*count) += *((int*) event_data);
    }

    xSemaphoreGive(arg->mutex);
}

TEST_CASE("default loop: can create and delete loop", "[event]")
{
    TEST_SETUP();

    TEST_ESP_OK(esp_event_loop_create_default());

    TEST_ESP_OK(esp_event_loop_delete_default());
}

TEST_CASE("default loop: registering fails on uninitialized default loop", "[event]")
{
    TEST_SETUP();

    esp_event_handler_instance_t instance;
    TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_event_handler_instance_register(s_default_test_base1,
            TEST_EVENT_BASE1_EV1,
            test_event_simple_handler,
            NULL,
            &instance));
}

TEST_CASE("default loop: registering/unregistering event works", "[event]")
{
    TEST_SETUP();

    int count = 0;

    simple_arg_t arg = {
        .data = &count,
        .mutex = xSemaphoreCreateMutex()
    };

    TEST_ESP_OK(esp_event_loop_create_default());

    esp_event_handler_instance_t instance;
    TEST_ESP_OK(esp_event_handler_instance_register(s_default_test_base1,
            TEST_EVENT_BASE1_EV1,
            test_event_simple_handler,
            &arg,
            &instance));
    TEST_ASSERT(instance);
    TEST_ESP_OK(esp_event_post(s_default_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY));

    vTaskDelay(10);

    TEST_ASSERT_EQUAL(1, count);

    TEST_ESP_OK(esp_event_handler_instance_unregister(s_default_test_base1,
            TEST_EVENT_BASE1_EV1,
            &instance));

    vTaskDelay(10);

    TEST_ASSERT_EQUAL(1, count);

    TEST_ESP_OK(esp_event_loop_delete_default());

    vSemaphoreDelete(arg.mutex);
}

TEST_CASE("default event loop: registering event handler instance without instance context works", "[event]") {
    TEST_SETUP();

    int count_1 = 0;

    simple_arg_t arg_1 = {
        .data = &count_1,
        .mutex = xSemaphoreCreateMutex()
    };

    TEST_ESP_OK(esp_event_loop_create_default());

    TEST_ESP_OK(esp_event_handler_instance_register(ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg_1, NULL));

    TEST_ESP_OK(esp_event_post(s_default_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY));

    vTaskDelay(10);

    TEST_ASSERT_EQUAL(1, count_1);

    TEST_ESP_OK(esp_event_loop_delete_default());

    vSemaphoreDelete(arg_1.mutex);
}