#include "meter_ade7758.h"
#include "ade7758.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/spi_master.h"


#define TAG "meter"

// === Configurações de hardware ===
#define PIN_NUM_CLK   15
#define PIN_NUM_MOSI  2
#define PIN_NUM_MISO  4
#define PIN_NUM_CS    23
#define EEPROM_HOST   HSPI_HOST

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

#define METER_READ_INTERVAL_MS 5000

// === Dados internos ===
typedef struct {
    float vrms[3];
    float irms[3];
    int watt[3];
    int var[3];  // reservados
    int va[3];   // reservados
} meter_internal_data_t;

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

// === Utilitários internos ===

static void meter_clear_internal_data(void) {
    if (meter_mutex && xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
        memset(&meter_data, 0, sizeof(meter_data));
        xSemaphoreGive(meter_mutex);
    }
}

static bool meter_read_internal(meter_internal_data_t *out) {
    if (!out) return false;
    if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
        *out = meter_data;
        xSemaphoreGive(meter_mutex);
        return true;
    }
    return false;
}

static void meter_task_func(void *param) {
    ESP_LOGI(TAG, "Meter task started");

    meter_internal_data_t previous = {0};

    while (true) {
        meter_internal_data_t current = {0};

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

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

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

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

        vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS));
    }
}

// === Interface pública: controle ===

esp_err_t meter_init(void) {
    ESP_LOGI(TAG, "Inicializando medidor...");

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

    meter_clear_internal_data();

    esp_err_t err = Init(EEPROM_HOST, PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Erro ao inicializar SPI (%d)", err);
        return err;
    }

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

    return ESP_OK;
}

esp_err_t meter_start(void) {
    if (meter_task) return ESP_ERR_INVALID_STATE;

    meter_clear_internal_data();
    BaseType_t result = xTaskCreate(meter_task_func, "meter_task", 4096, NULL, 5, &meter_task);
    return result == pdPASS ? ESP_OK : ESP_FAIL;
}

void meter_stop(void) {
    if (meter_task) {
        vTaskDelete(meter_task);
        meter_task = NULL;
    }
    meter_clear_internal_data();
}

bool meter_is_running(void) {
    return meter_task != NULL;
}

void meter_clear_data(void) {
    meter_clear_internal_data();
}

// === Interface pública: acesso aos dados ===

float meter_get_vrms_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.vrms[0] : 0; }
float meter_get_vrms_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.vrms[1] : 0; }
float meter_get_vrms_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.vrms[2] : 0; }

float meter_get_irms_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.irms[0] : 0; }
float meter_get_irms_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.irms[1] : 0; }
float meter_get_irms_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.irms[2] : 0; }

int meter_get_watt_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.watt[0] : 0; }
int meter_get_watt_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.watt[1] : 0; }
int meter_get_watt_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.watt[2] : 0; }

int meter_get_var_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.var[0] : 0; }
int meter_get_var_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.var[1] : 0; }
int meter_get_var_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.var[2] : 0; }

int meter_get_va_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.va[0] : 0; }
int meter_get_va_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.va[1] : 0; }
int meter_get_va_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.va[2] : 0; }

// === Diagnóstico ===

uint32_t meter_get_watchdog_counter(void) {
    return meter_watchdog_counter;
}
