#include "Medidor.h"
#include <cstring>
#include <unistd.h>
#include <iostream>
#include <iomanip>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <limits>

Medidor::Medidor() : sock(-1), direccion(1) {
    memset(registros, 0, sizeof(registros));
}

Medidor::~Medidor() {
    if (sock != -1) {
        close(sock);
    }
}

int Medidor::CRC8(unsigned char *puchMsg, unsigned int inicio, unsigned int fin) {
    unsigned long suma = 0;
    for (unsigned int i = inicio; i <= fin; i++) {
        suma += puchMsg[i];
    }
    return (suma % 256);
}

int Medidor::enviarADSUfijo(int control) {
    unsigned char mensajeTX[6];
    mensajeTX[0] = 0x10;
    mensajeTX[1] = control;
    mensajeTX[2] = (this->direccion) & 255;
    mensajeTX[3] = (this->direccion) >> 8;
    mensajeTX[4] = CRC8(mensajeTX, 1, 3);
    mensajeTX[5] = 0x16;

    std::cout << "Enviando mensaje: ";
    for (int i = 0; i < 6; i++) {
        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)mensajeTX[i] << " ";
    }
    std::cout << std::dec << std::endl;

    int result = send(sock, mensajeTX, 6, 0);
    if (result <= 0) {
        std::cout << "Error al enviar: " << strerror(errno) << std::endl;
    } else {
        std::cout << "Bytes enviados: " << result << std::endl;
    }
    return result;
}

int Medidor::esperaPaqueteADSU(unsigned int vDelay, unsigned char *vMensajeRX) {
    fd_set readfds;
    struct timeval timeout;
    int bytesRecibidos;

    FD_ZERO(&readfds);
    FD_SET(sock, &readfds);

    // Aumentar el timeout a 10 segundos
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    std::cout << "Esperando respuesta del medidor (timeout: 10 segundos)..." << std::endl;
    int selectResult = select(sock + 1, &readfds, NULL, NULL, &timeout);
    if (selectResult > 0) {
        bytesRecibidos = recv(sock, vMensajeRX, 256, 0);
        std::cout << "Bytes recibidos: " << bytesRecibidos << std::endl;
        if (bytesRecibidos > 0) {
            std::cout << "Bytes recibidos: ";
            for (int i = 0; i < bytesRecibidos; i++) {
                std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)vMensajeRX[i] << " ";
            }
            std::cout << std::dec << std::endl;

            if (vMensajeRX[0] == 0x10 && CRC8(vMensajeRX, 1, 3) == vMensajeRX[4]) {
                return vMensajeRX[1];
            }
            if (vMensajeRX[0] == 0x68 && vMensajeRX[1] == vMensajeRX[2] &&
                CRC8(vMensajeRX, 4, vMensajeRX[1] + 3) == vMensajeRX[vMensajeRX[1] + 4]) {
                return vMensajeRX[4];
            }
            std::cout << "Formato de respuesta no reconocido" << std::endl;
        } else {
            std::cout << "Error en recv: " << strerror(errno) << std::endl;
        }
    } else if (selectResult == 0) {
        std::cout << "Timeout al esperar respuesta del medidor" << std::endl;
    } else {
        std::cout << "Error en select: " << strerror(errno) << std::endl;
    }
    return -1;
}

int Medidor::autenticarMedidor() {
    if (!verificarSocket()) {
        std::cerr << "El socket no está en buen estado para comunicarse." << std::endl;
        return ERROR_COMUNICACION;
    }

    unsigned char mensajeTX[200];
    unsigned char mensajeRX[256];

    std::cout << "Iniciando proceso de autenticación..." << std::endl;

    // Paso 1: Enviar solicitud de estado de enlace
    std::cout << "Enviando solicitud de estado de enlace..." << std::endl;
    if (enviarADSUfijo(0x49) <= 0) {
        std::cerr << "Error al enviar solicitud de estado de enlace" << std::endl;
        return ERROR_COMUNICACION;
    }
    int respuesta = esperaPaqueteADSU(5000, mensajeRX);
    if (respuesta != 0x0B) {
        std::cerr << "Respuesta inesperada a la solicitud de estado de enlace: 0x" << std::hex << respuesta << std::dec << std::endl;
        return ERROR_COMUNICACION;
    }
    std::cout << "Solicitud de estado de enlace exitosa" << std::endl;

    // Paso 2: Enviar reposición de enlace remoto
    std::cout << "Enviando reposición de enlace remoto..." << std::endl;
    if (enviarADSUfijo(0x40) <= 0) {
        std::cerr << "Error al enviar reposición de enlace remoto" << std::endl;
        return ERROR_COMUNICACION;
    }
    respuesta = esperaPaqueteADSU(5000, mensajeRX);
    if (respuesta != 0x00) {
        std::cerr << "Respuesta inesperada a la reposición de enlace remoto: 0x" << std::hex << respuesta << std::dec << std::endl;
        return ERROR_COMUNICACION;
    }
    std::cout << "Reposición de enlace remoto exitosa" << std::endl;

    // Paso 3: Enviar solicitud de estado de enlace nuevamente
    std::cout << "Enviando segunda solicitud de estado de enlace..." << std::endl;
    if (enviarADSUfijo(0x49) <= 0) {
        std::cerr << "Error al enviar segunda solicitud de estado de enlace" << std::endl;
        return ERROR_COMUNICACION;
    }
    respuesta = esperaPaqueteADSU(5000, mensajeRX);
    if (respuesta != 0x0B) {
        std::cerr << "Respuesta inesperada a la segunda solicitud de estado de enlace: 0x" << std::hex << respuesta << std::dec << std::endl;
        return ERROR_COMUNICACION;
    }
    std::cout << "Segunda solicitud de estado de enlace exitosa" << std::endl;

    // Paso 4: Envío de clave de acceso
    std::cout << "Enviando clave de acceso..." << std::endl;
    mensajeTX[0] = 0x68;
    mensajeTX[1] = 0x0D;
    mensajeTX[2] = 0x0D;
    mensajeTX[3] = 0x68;
    mensajeTX[4] = 0x73;
    mensajeTX[5] = (this->direccion) & 255;
    mensajeTX[6] = (this->direccion) >> 8;
    mensajeTX[7] = 0xB7;
    mensajeTX[8] = 0x01;
    mensajeTX[9] = 0x06;
    mensajeTX[10] = 0x01;
    mensajeTX[11] = 0x00;
    mensajeTX[12] = 0x00;
    mensajeTX[13] = 0x01;
    mensajeTX[14] = 0x00;
    mensajeTX[15] = 0x00;
    mensajeTX[16] = 0x00;
    mensajeTX[17] = CRC8(mensajeTX, 4, 16);
    mensajeTX[18] = 0x16;

    if (send(sock, mensajeTX, 19, 0) <= 0) {
        std::cerr << "Error al enviar clave de acceso" << std::endl;
        return ERROR_COMUNICACION;
    }
    respuesta = esperaPaqueteADSU(5000, mensajeRX);
    if (respuesta != 0x00) {
        std::cerr << "Respuesta inesperada al envío de clave de acceso: 0x" << std::hex << respuesta << std::dec << std::endl;
        return ERROR_COMUNICACION;
    }
    std::cout << "Envío de clave de acceso exitoso" << std::endl;

    // Paso 5: Enviar solicitud de datos de clase 2
    std::cout << "Enviando solicitud de datos de clase 2..." << std::endl;
    if (enviarADSUfijo(0x5B) <= 0) {
        std::cerr << "Error al enviar solicitud de datos de clase 2" << std::endl;
        return ERROR_COMUNICACION;
    }
    respuesta = esperaPaqueteADSU(5000, mensajeRX);
    if (respuesta != 0x08 || mensajeRX[7] != 0xB7 || mensajeRX[9] != 0x07) {
        std::cerr << "Respuesta inesperada a la solicitud de datos de clase 2: 0x" << std::hex << respuesta << std::dec << std::endl;
        std::cerr << "mensajeRX[7]: 0x" << std::hex << (int)mensajeRX[7] << ", mensajeRX[9]: 0x" << (int)mensajeRX[9] << std::dec << std::endl;
        return ERROR_COMUNICACION;
    }
    std::cout << "Solicitud de datos de clase 2 exitosa" << std::endl;

    std::cout << "Autenticación completada con éxito" << std::endl;

    // Leer todos los grupos de valores instantáneos
    int grupos[] = {192, 193, 194};
    bool todosExitosos = true;
    for (int grupo : grupos) {
        int resultado = leerInstantaneos(grupo);
        if (resultado != COMUNICACION_EXITOSA) {
            std::cerr << "Error o sin datos al leer valores instantáneos del grupo " << grupo << std::endl;
            todosExitosos = false;
        }
    }

    if (!todosExitosos) {
        std::cout << "Advertencia: No se pudieron leer todos los grupos de datos instantáneos" << std::endl;
    } else {
        std::cout << "Todos los grupos de datos instantáneos fueron leídos exitosamente" << std::endl;
        // Mostrar las variables después de leer los datos instantáneos
        mostrarVariablesInstantaneas();
    }

    return COMUNICACION_EXITOSA;
}

int Medidor::leerInstantaneos(int grupo) {
    unsigned char mensajeTX[200];
    unsigned char mensajeRX[256];

    std::cout << "Leyendo grupo " << grupo << " de valores instantáneos..." << std::endl;

    // Preparar el mensaje de solicitud
    mensajeTX[0] = 0x68;
    mensajeTX[1] = 0x0A;
    mensajeTX[2] = 0x0A;
    mensajeTX[3] = 0x68;
    mensajeTX[4] = 0x73;
    mensajeTX[5] = (this->direccion) & 255;
    mensajeTX[6] = (this->direccion) >> 8;
    mensajeTX[7] = 0xA2;
    mensajeTX[8] = 0x01;
    mensajeTX[9] = 0x05;
    mensajeTX[10] = 0x01;
    mensajeTX[11] = 0x00;
    mensajeTX[12] = 0x00;
    mensajeTX[13] = grupo;
    mensajeTX[14] = CRC8(mensajeTX, 4, 13);
    mensajeTX[15] = 0x16;

    // Enviar la solicitud
    if (send(sock, mensajeTX, 16, 0) <= 0) {
        std::cerr << "Error al enviar solicitud de valores instantáneos para el grupo " << grupo << std::endl;
        return ERROR_COMUNICACION;
    }

    // Esperar la respuesta inicial
    int respuesta = esperaPaqueteADSU(5000, mensajeRX);
    if (respuesta != 0x00) {
        std::cerr << "Respuesta inesperada a la solicitud de valores instantáneos: 0x" << std::hex << respuesta << std::dec << std::endl;
        return ERROR_COMUNICACION;
    }

    // Enviar solicitud de datos
    if (enviarADSUfijo(0x5B) <= 0) {
        std::cerr << "Error al enviar solicitud de datos para el grupo " << grupo << std::endl;
        return ERROR_COMUNICACION;
    }

    // Esperar los datos
    respuesta = esperaPaqueteADSU(5000, mensajeRX);

    // Analizar la respuesta
    if (respuesta == 0x08 && mensajeRX[7] == 0xA2 && mensajeRX[9] == 0x0E) {
        std::cout << "El medidor indica que no hay datos disponibles para el grupo " << grupo << std::endl;
        return COMUNICACION_EXITOSA; // Consideramos esto como éxito, pero sin datos
    }

    if (respuesta != 0x08 || mensajeRX[7] != 163 || mensajeRX[9] != 0x05) {
        std::cerr << "Respuesta inesperada a la solicitud de datos: 0x" << std::hex << respuesta << std::dec << std::endl;
        std::cerr << "mensajeRX[7]: 0x" << std::hex << (int)mensajeRX[7] << ", mensajeRX[9]: 0x" << (int)mensajeRX[9] << std::dec << std::endl;
        return ERROR_COMUNICACION;
    }

    // Imprimir datos crudos para depuración
    std::cout << "Datos crudos para el grupo " << grupo << ": ";
    for (int i = 0; i < mensajeRX[1] - 3; i++) {
        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)mensajeRX[14 + i] << " ";
    }
    std::cout << std::dec << std::endl;

    // Funciones de comprobación de consistencia
    auto isVoltageValid = [](float voltage) { return voltage >= 180 && voltage <= 260; };
    auto isCurrentValid = [](float current) { return current >= 0 && current <= 100; };
    auto isPowerValid = [](float power) { return power >= -10000 && power <= 10000; };
    auto isPowerFactorValid = [](float pf) { return pf >= -1 && pf <= 1; };
    auto isEnergyValid = [](float energy) { return energy >= 0 && energy <= 1e9; };

    // Procesar los datos recibidos según el grupo
    switch (grupo) {
        case 192: // Energías
            for (int i = 0; i < 6; i++) {
                registros[i] = convertirA32BitsSinSigno(&mensajeRX[14 + i*4]) * 0.001f; // kWh o kVArh
                if (!isEnergyValid(registros[i])) {
                    std::cout << "Advertencia: Valor de energía inválido en registro " << i << ": " << registros[i] << " kWh/kVArh" << std::endl;
                }
            }
            break;
        case 193: // Potencias
            registros[6] = convertirA32BitsSignado(&mensajeRX[14]) * 0.001f; // Potencia activa total en kW
            registros[7] = convertirA32BitsSignado(&mensajeRX[18]) * 0.001f; // Potencia reactiva total en kVAr
            registros[8] = ((float)mensajeRX[22]) * 0.01f; // Factor de potencia total
            if (!isPowerValid(registros[6])) std::cout << "Advertencia: Potencia activa total inválida: " << registros[6] << " kW" << std::endl;
            if (!isPowerValid(registros[7])) std::cout << "Advertencia: Potencia reactiva total inválida: " << registros[7] << " kVAr" << std::endl;
            if (!isPowerFactorValid(registros[8])) std::cout << "Advertencia: Factor de potencia total inválido: " << registros[8] << std::endl;
            for (int i = 0; i < 3; i++) {
                registros[9 + i*3] = convertirA32BitsSignado(&mensajeRX[24 + i*8]) * 0.001f; // Potencia activa por fase en kW
                registros[10 + i*3] = convertirA32BitsSignado(&mensajeRX[28 + i*8]) * 0.001f; // Potencia reactiva por fase en kVAr
                registros[11 + i*3] = ((float)mensajeRX[32 + i*8]) * 0.01f; // Factor de potencia por fase
                if (!isPowerValid(registros[9 + i*3])) std::cout << "Advertencia: Potencia activa inválida en fase " << i+1 << ": " << registros[9 + i*3] << " kW" << std::endl;
                if (!isPowerValid(registros[10 + i*3])) std::cout << "Advertencia: Potencia reactiva inválida en fase " << i+1 << ": " << registros[10 + i*3] << " kVAr" << std::endl;
                if (!isPowerFactorValid(registros[11 + i*3])) std::cout << "Advertencia: Factor de potencia inválido en fase " << i+1 << ": " << registros[11 + i*3] << std::endl;
            }
            break;
        case 194: // Tensiones y Corrientes
            for (int i = 0; i < 3; i++) {
                registros[18 + i] = convertirA32BitsSinSigno(&mensajeRX[14 + i*8]) * 0.1f; // Tensiones en V
                registros[21 + i] = convertirA32BitsSinSigno(&mensajeRX[18 + i*8]) * 0.001f; // Corrientes en A
                if (!isVoltageValid(registros[18 + i])) std::cout << "Advertencia: Tensión inválida en fase " << i+1 << ": " << registros[18 + i] << " V" << std::endl;
                if (!isCurrentValid(registros[21 + i])) std::cout << "Advertencia: Corriente inválida en fase " << i+1 << ": " << registros[21 + i] << " A" << std::endl;
            }
            break;
        default:
            std::cerr << "Grupo de valores instantáneos no reconocido: " << grupo << std::endl;
            return ERROR_COMUNICACION;
    }

    std::cout << "Lectura de valores instantáneos del grupo " << grupo << " completada" << std::endl;

    // Imprimir los valores leídos para depuración
    std::cout << "Valores leídos:" << std::endl;
    int inicio, fin;
    switch (grupo) {
        case 192: inicio = 0; fin = 5; break;
        case 193: inicio = 6; fin = 17; break;
        case 194: inicio = 18; fin = 23; break;
        default: inicio = 0; fin = 0; break;
    }
    for (int i = inicio; i <= fin; i++) {
        std::cout << "  Registro " << i << ": " << registros[i] << std::endl;
    }

    return COMUNICACION_EXITOSA;
}

int32_t Medidor::convertirA32BitsSignado(unsigned char* bytes) {
    return (int32_t)((bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]);
}

uint32_t Medidor::convertirA32BitsSinSigno(unsigned char* bytes) {
    return (uint32_t)((bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]);
}

float Medidor::convertirAFloat32(unsigned char* bytes) {
    uint32_t intValue = convertirA32BitsSinSigno(bytes);
    float floatValue;
    std::memcpy(&floatValue, &intValue, sizeof(float));
    return floatValue;
}

float Medidor::aplicarEscala(int32_t valor, float escala) {
    return valor * escala;
}

float Medidor::aplicarEscala(uint32_t valor, float escala) {
    return valor * escala;
}

void Medidor::mostrarVariablesInstantaneas() {
    std::cout << "\nVariables después de leerInstantaneos:\n";
    std::cout << std::fixed << std::setprecision(3);

    // Energías (Grupo 192)
    std::cout << "Energías:\n";
    std::cout << "  VR (Energía activa importada): " << registros[0] << " kWh\n";
    std::cout << "  VS (Energía activa exportada): " << registros[1] << " kWh\n";
    std::cout << "  VT (Energía reactiva Q1): " << registros[2] << " kVArh\n";
    std::cout << "  IR (Energía reactiva Q2): " << registros[3] << " kVArh\n";
    std::cout << "  IS (Energía reactiva Q3): " << registros[4] << " kVArh\n";
    std::cout << "  IT (Energía reactiva Q4): " << registros[5] << " kVArh\n";

    // Potencias (Grupo 193)
    std::cout << "Potencias:\n";
    std::cout << "  PR (Potencia activa total): " << registros[6] << " kW\n";
    std::cout << "  PS (Potencia reactiva total): " << registros[7] << " kVAr\n";
    std::cout << "  PT (Factor de potencia total): " << registros[8] << "\n";
    std::cout << "  PrR (Potencia activa Fase 1): " << registros[9] << " kW\n";
    std::cout << "  PrS (Potencia reactiva Fase 1): " << registros[10] << " kVAr\n";
    std::cout << "  PrT (Factor de potencia Fase 1): " << registros[11] << "\n";
    std::cout << "  P3 (Potencia activa Fase 2): " << registros[12] << " kW\n";
    std::cout << "  FPR (Potencia reactiva Fase 2): " << registros[13] << " kVAr\n";
    std::cout << "  FPS (Factor de potencia Fase 2): " << registros[14] << "\n";
    std::cout << "  FPT (Potencia activa Fase 3): " << registros[15] << " kW\n";
    std::cout << "  Pr3 (Potencia reactiva Fase 3): " << registros[16] << " kVAr\n";
    std::cout << "  (Factor de potencia Fase 3): " << registros[17] << "\n";

    // Tensiones y Corrientes (Grupo 194)
    std::cout << "Tensiones y Corrientes:\n";
    std::cout << "  VR (Tensión Fase 1): " << registros[18] << " V\n";
    std::cout << "  VS (Tensión Fase 2): " << registros[19] << " V\n";
    std::cout << "  VT (Tensión Fase 3): " << registros[20] << " V\n";
    std::cout << "  IR (Corriente Fase 1): " << registros[21] << " A\n";
    std::cout << "  IS (Corriente Fase 2): " << registros[22] << " A\n";
    std::cout << "  IT (Corriente Fase 3): " << registros[23] << " A\n";
}

bool Medidor::verificarSocket() {
    int error = 0;
    socklen_t len = sizeof(error);
    int retval = getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
    
    if (retval != 0) {
        std::cerr << "Error al verificar el socket: " << strerror(errno) << std::endl;
        return false;
    }
    
    if (error != 0) {
        std::cerr << "Socket en estado de error: " << strerror(error) << std::endl;
        return false;
    }
    
    return true;
}