#include "evse_meter.h"
#include "meter_events.h"
#include "esp_event.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include <string.h>
#include <inttypes.h>

static const char *TAG = "evse_meter";
static SemaphoreHandle_t meter_mutex;

typedef struct
{
    uint32_t power_watts[EVSE_METER_PHASE_COUNT];
    float voltage[EVSE_METER_PHASE_COUNT];
    float current[EVSE_METER_PHASE_COUNT];
    uint32_t energy_wh;
} evse_meter_data_t;

static evse_meter_data_t meter_data;

static void on_meter_event_dispatcher(void *arg, esp_event_base_t base, int32_t id, void *data)
{
    if (base == METER_EVENT && id == METER_EVENT_DATA_READY && data)
    {
        const meter_event_data_t *evt = (const meter_event_data_t *)data;
        if (strcmp(evt->source, "EVSE") == 0)
        {
            evse_meter_on_meter_event(arg, data);
        }
    }
}

void evse_meter_on_meter_event(void *arg, void *event_data)
{
    const meter_event_data_t *evt = (const meter_event_data_t *)event_data;
    if (!evt)
        return;

    xSemaphoreTake(meter_mutex, portMAX_DELAY);
    for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
    {
        meter_data.power_watts[i] = evt->watt[i];
        meter_data.voltage[i] = evt->vrms[i];
        meter_data.current[i] = evt->irms[i];
    }
    meter_data.energy_wh = (uint32_t)(evt->total_energy);
    xSemaphoreGive(meter_mutex);

    ESP_LOGD(TAG,
             "Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, "
             "voltage[V]={%.2f,%.2f,%.2f}, "
             "current[A]={%.2f,%.2f,%.2f}, "
             "total_energy=%" PRIu32 "Wh",
             meter_data.power_watts[0], meter_data.power_watts[1], meter_data.power_watts[2],
             meter_data.voltage[0], meter_data.voltage[1], meter_data.voltage[2],
             meter_data.current[0], meter_data.current[1], meter_data.current[2],
             meter_data.energy_wh);
}

void evse_meter_init(void)
{
    meter_mutex = xSemaphoreCreateMutex();
    ESP_ERROR_CHECK(meter_mutex ? ESP_OK : ESP_FAIL);
    ESP_ERROR_CHECK(esp_event_handler_register(
        METER_EVENT, METER_EVENT_DATA_READY,
        on_meter_event_dispatcher, NULL));
    memset(&meter_data, 0, sizeof(meter_data));
    ESP_LOGI(TAG, "EVSE Meter listener registered.");
}

int evse_meter_get_instant_power(void)
{
    xSemaphoreTake(meter_mutex, portMAX_DELAY);
    int sum = 0;
    for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
    {
        sum += meter_data.power_watts[i];
    }
    xSemaphoreGive(meter_mutex);
    return sum;
}

int evse_meter_get_total_energy(void)
{
    xSemaphoreTake(meter_mutex, portMAX_DELAY);
    int val = meter_data.energy_wh;
    xSemaphoreGive(meter_mutex);
    return val;
}

void evse_meter_get_power(int power[EVSE_METER_PHASE_COUNT])
{
    xSemaphoreTake(meter_mutex, portMAX_DELAY);
    for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
    {
        power[i] = meter_data.power_watts[i];
    }
    xSemaphoreGive(meter_mutex);
}

void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT])
{
    xSemaphoreTake(meter_mutex, portMAX_DELAY);
    for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
    {
        voltage[i] = meter_data.voltage[i];
    }
    xSemaphoreGive(meter_mutex);
}

void evse_meter_get_current(float current[EVSE_METER_PHASE_COUNT])
{
    xSemaphoreTake(meter_mutex, portMAX_DELAY);
    for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i)
    {
        current[i] = meter_data.current[i];
    }
    xSemaphoreGive(meter_mutex);
}
