#include "meter.h"

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

#include "esp_log.h"
#include "esp_system.h"

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

#include "driver/spi_master.h"
#include "driver/gpio.h"

#include "ade7758.h"
#include "evse_api.h"

#define TAG "meter"

// SPI Config
#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

// Calibration constants
#define VRMS_CAL  4732.78f
#define IRMS_CAL  53416.0f

#define METER_READ_INTERVAL_MS 5000

static TaskHandle_t meter_task = NULL;
static MeterData metervalue;
static SemaphoreHandle_t meter_mutex = NULL;

static uint32_t meter_watchdog_counter = 0;

void meter_initData(void) {
    if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
        memset(&metervalue, 0, sizeof(metervalue));
        xSemaphoreGive(meter_mutex);
    } else {
        ESP_LOGE(TAG, "Falha ao adquirir semáforo para zerar dados.");
    }
}

MeterData meter_getData(void) {
    MeterData copy;
    if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
        copy = metervalue;
        xSemaphoreGive(meter_mutex);
    } else {
        ESP_LOGE(TAG, "Falha ao adquirir semáforo para leitura de dados.");
        memset(&copy, 0, sizeof(copy));
    }
    return copy;
}

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

uint32_t meter_get_watchdog_counter(void) {
    return meter_watchdog_counter;
}

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

    MeterData previousData = {0};
    bool dataChanged = false;

    while (true) {
        if (evse_state_is_charging(evse_get_state())) {
            MeterData local = {0};

            local.vrmsA = avrms() / VRMS_CAL;
            local.vrmsB = bvrms() / VRMS_CAL;
            local.vrmsC = cvrms() / VRMS_CAL;

            local.irmsA = airms() / IRMS_CAL;
            local.irmsB = birms() / IRMS_CAL;
            local.irmsC = cirms() / IRMS_CAL;

            ESP_LOGD(TAG, "VRMS: A=%.2f, B=%.2f, C=%.2f", local.vrmsA, local.vrmsB, local.vrmsC);
            ESP_LOGD(TAG, "IRMS: A=%.2f, B=%.2f, C=%.2f", local.irmsA, local.irmsB, local.irmsC);

            if (setPotLine(PHASE_A, 20)) {
                local.wattA = getWatt(PHASE_A);
                ESP_LOGD(TAG, "Watt A: %" PRIi32, local.wattA);
            }

            if (setPotLine(PHASE_B, 20)) {
                local.wattB = getWatt(PHASE_B);
                ESP_LOGD(TAG, "Watt B: %" PRIi32, local.wattB);
            }

            if (setPotLine(PHASE_C, 20)) {
                local.wattC = getWatt(PHASE_C);
                ESP_LOGI(TAG, "Watt C: %" PRIi32, local.wattC);
            }

            // Verifique se os dados mudaram antes de atualizar
            if (memcmp(&local, &previousData, sizeof(MeterData)) != 0) {
                dataChanged = true;
                previousData = local;
            } else {
                dataChanged = false;
            }

            if (dataChanged && xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
                metervalue = local;
                meter_watchdog_counter++;
                xSemaphoreGive(meter_mutex);
            }
        }

        vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS));
    }
}

void Calibrate_ADE7758(void) {
    gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1);
    setupDivs(1, 1, 1);
    setLcycMode(0x00);
    resetStatus();
}

void meter_init(void) {
    ESP_LOGI(TAG, "Initializing meter");

    if (!meter_mutex) {
        meter_mutex = xSemaphoreCreateMutex();
        if (!meter_mutex) {
            ESP_LOGE(TAG, "Erro ao criar semáforo de mutex");
            return;  // Pode parar a inicialização caso não consiga criar o mutex
        }
    }

    meter_initData();
    esp_err_t err = Init(EEPROM_HOST, PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Erro na inicialização do hardware SPI: %d", err);
        return;
    }

    InitSpi(PIN_NUM_CS);
}

void meter_start(void) {
    ESP_LOGI(TAG, "Starting meter");

    Calibrate_ADE7758();
    meter_initData();

    if (!meter_task) {
        xTaskCreate(meter_task_func, "meter_task", 5 * 1024, NULL, 5, &meter_task);
    }
}

void meter_stop(void) {
    ESP_LOGI(TAG, "Stopping meter");

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

    meter_initData();
}
