#include "meter_ade7758.h"
#include "spi_bus_manager.h"
#include "ade7758.h"
#include "meter_events.h"

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

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

#include "driver/gpio.h"

#define TAG "meter_ade7758"

// === Pinos ===
#define PIN_NUM_CS    15
#define PIN_ADUM_EN    4

// === Constantes de calibração ===
#define VRMS_CAL  4732.78f
#define IRMS_CAL  53416.0f

#define METER_READ_INTERVAL_MS 5000

// === Estrutura interna ===
typedef struct {
    float vrms[3];
    float irms[3];
    int watt[3];
    int var[3];  // reservado
    int va[3];   // reservado
} meter_ade7758_internal_data_t;

static meter_ade7758_internal_data_t meter_data;
static TaskHandle_t meter_task = NULL;
static SemaphoreHandle_t meter_mutex = NULL;
static uint32_t meter_watchdog_counter = 0;


// === Post de evento ===
static void meter_ade7758_post_event(const meter_ade7758_internal_data_t *data) {
    meter_event_data_t evt = {
        .frequency = 0,
        .power_factor = 0,
        .total_energy = 0
    };

    memcpy(evt.vrms, data->vrms, sizeof(evt.vrms));
    memcpy(evt.irms, data->irms, sizeof(evt.irms));
    memcpy(evt.watt, data->watt, sizeof(evt.watt));

    esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, &evt, sizeof(evt), portMAX_DELAY);
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
    }
}

static void meter_ade7758_task_func(void *param) {
    ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada");

    meter_ade7758_internal_data_t previous = {0};

    while (true) {
        meter_ade7758_internal_data_t meterData = {0};

        ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada %d",getVersion());

        meterData.vrms[0] = avrms() / VRMS_CAL;
        meterData.vrms[1] = bvrms() / VRMS_CAL;
        meterData.vrms[2] = cvrms() / VRMS_CAL;

        meterData.irms[0] = airms() / IRMS_CAL;
        meterData.irms[1] = birms() / IRMS_CAL;
        meterData.irms[2] = cirms() / IRMS_CAL;

        if (setPotLine(PHASE_A, 20)) meterData.watt[0] = getWatt(PHASE_A);
        if (setPotLine(PHASE_B, 20)) meterData.watt[1] = getWatt(PHASE_B);
        if (setPotLine(PHASE_C, 20)) meterData.watt[2] = getWatt(PHASE_C);

        ESP_LOGI(TAG, "VRMS: A=%.2f V, B=%.2f V, C=%.2f V", 
                (double)meterData.vrms[0], 
                (double)meterData.vrms[1], 
                (double)meterData.vrms[2]);

        ESP_LOGI(TAG, "IRMS: A=%.2f A, B=%.2f A, C=%.2f A", 
                (double)meterData.irms[0], 
                (double)meterData.irms[1], 
                (double)meterData.irms[2]);

        ESP_LOGI(TAG, "Watt: A=%.2f W, B=%.2f W, C=%.2f W", 
                (double)meterData.watt[0], 
                (double)meterData.watt[1], 
                (double)meterData.watt[2]);

        if (memcmp(&previous, &meterData, sizeof(meterData)) != 0) {
            if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
                meter_data = meterData;
                meter_watchdog_counter++;
                xSemaphoreGive(meter_mutex);

                meter_ade7758_post_event(&meterData);
            }
            previous = meterData;
        }

        vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS));
    }
}

// === Inicialização ===
esp_err_t meter_ade7758_init(void) {
    ESP_LOGI(TAG, "Inicializando ADE7758...");

    if (!meter_mutex) {
        meter_mutex = xSemaphoreCreateMutex();
        if (!meter_mutex) {
            ESP_LOGE(TAG, "Falha ao criar mutex");
            return ESP_ERR_NO_MEM;
        }
    }

    if (!spi_bus_manager_is_initialized()) {
        esp_err_t err = spi_bus_manager_init();  // usa pinos padrão
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Erro ao inicializar SPI: %s", esp_err_to_name(err));
            return err;
        }
    }

    vTaskDelay(pdMS_TO_TICKS(10));  // Delay de estabilização

    esp_err_t err = Init(spi_bus_manager_get_host(), -1, -1, -1);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Erro Init SPI ADE7758: %s", esp_err_to_name(err));
        return err;
    }

    err = InitSpi(PIN_NUM_CS);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Erro ao registrar dispositivo SPI: %s", esp_err_to_name(err));
        return err;
    }

    gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1);
    setupDivs(1, 1, 1);
    setLcycMode(0x00);
    resetStatus();

    ESP_LOGI(TAG, "ADE7758 inicializado com sucesso.");
    return ESP_OK;
}

// === Execução ===
esp_err_t meter_ade7758_start(void) {
    if (meter_task) return ESP_ERR_INVALID_STATE;

    BaseType_t result = xTaskCreate(meter_ade7758_task_func, "meter_ade7758_task", 4096, NULL, 3, &meter_task);
    return result == pdPASS ? ESP_OK : ESP_FAIL;
}

void meter_ade7758_stop(void) {
    if (meter_task) {
        vTaskDelete(meter_task);
        meter_task = NULL;
    }
}
