#include "qt_interface.h"
#include "interface.h"
#include "modbusTCP_TC.h"
#include "modbusTCP_contex.h"
#include "ccu_port.h"
#include "secc_port.h"

modbusTCP_QT_t qttcu = {
    .ccu = &ccu_port
};

static int modbusTCP_QT_send(modbusTCP_QT_t* dev, uint8_t* data, int length)
{
    int ret = 0;
    if (dev->quit == 0 && dev->fd > 0)
    {
        if (dev->debug) printline(data, length, "modbusTCP_QT(%d).send", dev->fd);
        ret = send(dev->fd, data, length, 0);
        if (ret <= 0) { 
            printe("ret:%d, errno:%d\n", ret, errno); 
            dev->txbyte = 0;
            shutdown(dev->fd, SHUT_RDWR); 
            close(dev->fd);
            dev->fd = -1; 
            dev->debug = 1;
            return -1;
        }else{
            // dev->debug = 0;
        }
    }

    return ret;
}

static int modbusTCP_QT_recv(modbusTCP_QT_t* dev, int timeout)
{
    fd_set rset;
    struct timeval tv;
    FD_ZERO(&rset);
    FD_SET(dev->fd, &rset);
    tv.tv_sec = timeout/1000;
    tv.tv_usec = (timeout%1000) * 1000;
    int ret = select(dev->fd + 1, &rset, NULL, NULL, &tv);
    if (ret > 0)
    {
        modbusTCP_frame_t* pkg = (modbusTCP_frame_t*)&dev->frame;
        int FRAMELEN = 8;
        dev->rxbyte = 0;
        while (dev->rxbyte <= FRAMELEN)
        {
            int ret = recv(dev->fd, &dev->rxbuff[dev->rxbyte], FRAMELEN - dev->rxbyte, 0);
            if (ret <= 0) { 
                printe("ret:%d, errno:%d\n", ret, errno); 
                dev->rxbyte = 0;
                shutdown(dev->fd, SHUT_RDWR); 
                close(dev->fd);
                dev->fd = -1; 
                dev->debug = 1;
                return -1;
            }
            dev->rxbyte += ret;

            if (dev->rxbyte >= FRAMELEN){
                decode_FRAME(dev->rxbuff, pkg);
                // printd(":dev->rxbyte:%d, FRAMELEN:%d, pkg->protoID:%d\n", dev->rxbyte, FRAMELEN, pkg->protoID);
                if (pkg->protoID == 0x0000){
                    FRAMELEN = 8 + pkg->datalen;
                    break;
                }
                memmove(&dev->rxbuff[0], &dev->rxbuff[1], dev->rxbyte - 1);
                dev->rxbyte = dev->rxbyte - 1;
            }
        }
        // printd(":dev->rxbyte:%d, FRAMELEN:%d, pkg->protoID:%d\n", dev->rxbyte, FRAMELEN, pkg->protoID);
        while(dev->rxbyte < FRAMELEN)
        {
            int ret = recv(dev->fd, &dev->rxbuff[dev->rxbyte], FRAMELEN - dev->rxbyte, 0);
            if (ret <= 0) { 
                printe("ret:%d, errno:%d\n", ret, errno); 
                dev->rxbyte = 0;
                shutdown(dev->fd, SHUT_RDWR); 
                close(dev->fd); 
                dev->fd = -1; 
                dev->debug = 1;
                return -1;
            }
            dev->rxbyte += ret;
        }
        if (dev->debug) printline(dev->rxbuff, dev->rxbyte, "modbusTCP_QT(%d).recv", dev->fd);
        return pkg->command;

    }

    return -1;
}

static int modbusTCP_QT_pipe_send(modbusTCP_QT_t* dev, uint8_t* data, int length)
{
    int ret = 0;
    if (dev->quit == 0 && dev->fp[0] > 0)
    {
        // printline(data, length, "modbusTCP_QT(%d).pipe", dev->fp[0]);
        ret = send(dev->fp[0], data, length, 0);
        if (ret <= 0) { 
            printe("ret:%d, errno:%d\n", ret, errno); 
            shutdown(dev->fp[0], SHUT_RDWR); 
            close(dev->fp[0]);
            dev->fp[0] = -1; 
            return -1;
        }
    }
    return ret;
}

static int modbusTCP_QT_pipe_recv(modbusTCP_QT_t* dev, int timeout)
{
    fd_set rset;
    struct timeval tv;
    FD_ZERO(&rset);
    FD_SET(dev->fp[1], &rset);
    tv.tv_sec = timeout/1000;
    tv.tv_usec = (timeout%1000) * 1000;
    int ret = select(dev->fp[1] + 1, &rset, NULL, NULL, &tv);
    if (ret>0)
    {
        uint8_t rxbuff[2048];
        modbusTCP_frame_t frame;
        modbusTCP_frame_t* pkg = (modbusTCP_frame_t*)&frame;
        int FRAMELEN = 8;
        int rxbyte = 0;
        pthread_mutex_lock(&dev->mutex1);
        while (rxbyte <= FRAMELEN)
        {
            int ret = recv(dev->fp[1], &rxbuff[rxbyte], FRAMELEN - rxbyte, 0);
            if (ret <= 0) { 
                pthread_mutex_unlock(&dev->mutex1);
                printe("ret:%d, errno:%d\n", ret, errno); 
                shutdown(dev->fp[1], SHUT_RDWR); 
                close(dev->fp[1]);
                dev->fp[1] = -1; 
                return -1;
            }
            rxbyte += ret;
            if (rxbyte >= FRAMELEN){
                decode_FRAME(rxbuff, pkg);
                if (pkg->protoID == 0x0000){
                    FRAMELEN = 8 + pkg->datalen;
                    break;
                }
                memmove(&rxbuff[0], &rxbuff[1], rxbyte - 1);
                rxbyte = rxbyte - 1;
            }
        }
        while(rxbyte < FRAMELEN)
        {
            int ret = recv(dev->fp[1], &rxbuff[rxbyte], FRAMELEN - rxbyte, 0);
            if (ret <= 0) { 
                pthread_mutex_unlock(&dev->mutex1);
                printe("ret:%d, errno:%d\n", ret, errno); 
                shutdown(dev->fp[1], SHUT_RDWR); 
                close(dev->fp[1]); 
                dev->fp[1] = -1; 
                return -1;
            }
            rxbyte += ret;
        }
        pthread_mutex_unlock(&dev->mutex1);
        // printline(rxbuff, rxbyte, "modbusTCP_QT(%d).wait", dev->fp[1]);
        return pkg->command;
    }
    return -1;
}

int modbusTCP_QT_cmd_0x90(modbusTCP_QT_t* dev, uint32_t connectorId, uint32_t GUN_Error, uint32_t GUN_State)
{
    ccu_config_t *config = &ccu_config;
    if (config->qttcu.enable == 0) return -1;

    ccu_port_t *ccu = (ccu_port_t*)dev->ccu;
    secc_port_t *gun = NULL;
    if (connectorId == 1) gun = (secc_port_t*)ccu->seccA;
    if (connectorId == 2) gun = (secc_port_t*)ccu->seccB;
    if (gun == NULL) return -1;

    uint32_t GUN_Type = gun->config->GUN_Type;
    uint32_t BOX_Temp = (uint32_t)(ccu->BOX_Temp * 10);
    
    pthread_mutex_lock(&dev->mutex0);
    int idx = 8;
    uint8_t *stream = dev->txbuff;
    idx = encode_U32(stream, idx, 1, &connectorId);
    idx = encode_U32(stream, idx, 1, &GUN_Error);
    idx = encode_U32(stream, idx, 1, &GUN_State);
    idx = encode_U32(stream, idx, 1, &GUN_Type);
    idx = encode_U32(stream, idx, 1, &BOX_Temp);
    dev->txbyte = encode_HEADER(stream, dev->transID++, config->TCU_ID, modbusTCP_CMD_0x90, NULL, idx - 8);
    int ret = modbusTCP_QT_send(dev, dev->txbuff, dev->txbyte);
    pthread_mutex_unlock(&dev->mutex0);
    if ( ret == dev->txbyte)
    {
        return 0;
    }
    return -1;
}

int modbusTCP_QT_cmd_0x91(modbusTCP_QT_t* dev, uint32_t connectorId)
{
    ccu_config_t *config = &ccu_config;
    if (config->qttcu.enable == 0) return -1;

    ccu_port_t *ccu = (ccu_port_t*)dev->ccu;
    secc_port_t *gun = NULL;
    if (connectorId == 1) gun = (secc_port_t*)ccu->seccA;
    if (connectorId == 2) gun = (secc_port_t*)ccu->seccB;
    if (gun == NULL) return -1;

    
    uint32_t now = time(NULL);
    uint32_t started_KWh = (uint32_t)(gun->started_KWh*10);
    uint32_t now_KWh = (uint32_t)(gun->EMeters*10);
    uint32_t gunTemp1 = (uint32_t)(gun->GUNLine_Temp1*10);
    uint32_t gunTemp2 = (uint32_t)(gun->GUNLine_Temp2*10);
    uint32_t powerKW = (uint32_t)(gun->PMeters*10);
    uint32_t voltage = (uint32_t)(gun->VMeters*10);
    uint32_t current = (uint32_t)(gun->IMeters*10);

    pthread_mutex_lock(&dev->mutex0);
    int idx = 8;
    uint8_t *stream = dev->txbuff;
    idx = encode_U32(stream, idx, 1, &connectorId);
    idx = encode_U32(stream, idx, 1, &gun->transactionId);
    idx = encode_U32(stream, idx, 1, &gun->SOC);

    idx = encode_U32(stream, idx, 1, &gun->started_UTC);
    idx = encode_U32(stream, idx, 1, &now);

    idx = encode_U32(stream, idx, 1, &started_KWh);
    idx = encode_U32(stream, idx, 1, &now_KWh);

    idx = encode_U32(stream, idx, 1, &gunTemp1);
    idx = encode_U32(stream, idx, 1, &gunTemp2);

    idx = encode_U32(stream, idx, 1, &powerKW);
    idx = encode_U32(stream, idx, 1, &voltage);
    idx = encode_U32(stream, idx, 1, &current);

    dev->txbyte = encode_HEADER(stream, dev->transID++, config->TCU_ID, modbusTCP_CMD_0x91, NULL, idx - 8);
    int ret = modbusTCP_QT_send(dev, dev->txbuff, dev->txbyte);
    pthread_mutex_unlock(&dev->mutex0);
    if (ret == dev->txbyte)
    {
        return 0;
    }
    return -1;
}
static int generate_random_i32(void)
{
    srand((int32_t)time(0));
    return  rand();
}

static int modebusTCP_QT_cmd_0x92_resp(modbusTCP_QT_t* dev)
{
    int ret = 0;
    ccu_config_t *config = &ccu_config;
    ccu_port_t* ccu = (ccu_port_t*)dev->ccu;
    ocpp_client_t *ocpp0 = (ocpp_client_t*)ccu->ocpp0;
    secc_port_t *seccA = (secc_port_t*)ccu->seccA;
    secc_port_t *seccB = (secc_port_t*)ccu->seccB;
    {
        int idx = 8;
        uint8_t *stream = dev->rxbuff;
        int  connectorId;
        uint8_t emaid[64];
        uint32_t request_KWh;
        uint32_t request_Minutes;
        uint32_t request_SOC;
        uint32_t authorizated;
        
        idx = decode_U32(stream, idx, 1, &connectorId);
        idx = decode_U8(stream, idx, 64, emaid);
        idx = decode_U32(stream, idx, 1, &request_KWh);
        idx = decode_U32(stream, idx, 1, &request_Minutes);
        idx = decode_U32(stream, idx, 1, &request_SOC);
        idx = decode_U32(stream, idx, 1, &authorizated);

        if (authorizated == 0)
        {
            authorizated = ocpp16_request_AuthorizateEMAID(ocpp0, emaid);
        }

        if (authorizated == AuthorizationStatus_Accepted)
        {
            if (connectorId == 1 ) { ret = secc_eim_startup(seccA, emaid, request_KWh/10, request_Minutes, request_SOC); }
            if (connectorId == 2 ) { ret = secc_eim_startup(seccB, emaid, request_KWh/10, request_Minutes, request_SOC); }
        }
    }
    {
        int idx = 8;
        uint8_t *stream = dev->txbuff;
        idx = encode_U32(stream, idx, 1, &ret);
        dev->txbyte = encode_HEADER(stream, dev->transID++, config->TCU_ID, modbusTCP_CMD_0x92, NULL, idx - 8);
        return modbusTCP_QT_send(dev, dev->txbuff, dev->txbyte);
    }
}

static int modebusTCP_QT_cmd_0x93_resp(modbusTCP_QT_t* dev)
{
    int ret = 0;
    ccu_config_t *config = &ccu_config;
    ccu_port_t* ccu = (ccu_port_t*)dev->ccu;
    secc_port_t *seccA = (secc_port_t*)ccu->seccA;
    secc_port_t *seccB = (secc_port_t*)ccu->seccB;
    {
        int idx = 8;
        uint8_t *stream = dev->rxbuff;
        uint32_t connectorId;
        uint8_t emaid[64];
        uint32_t authorizated;
        
        idx = decode_U32(stream, idx, 1, &connectorId);
        idx = decode_U8(stream, idx, 64, emaid);
        idx = decode_U32(stream, idx, 1, &authorizated);

        if (connectorId == 1 ) { ret = secc_eim_shutdown(seccA); }
        if (connectorId == 2 ) { ret = secc_eim_shutdown(seccB); }
    }
    {
        int idx = 8;
        uint8_t *stream = dev->txbuff;
        idx = encode_U32(stream, idx, 1, &ret);
        dev->txbyte = encode_HEADER(stream, dev->transID++, config->TCU_ID, modbusTCP_CMD_0x93, NULL, idx - 8);
        return modbusTCP_QT_send(dev, dev->txbuff, dev->txbyte);
    }
}

static int modebusTCP_QT_cmd_0x94_resp(modbusTCP_QT_t* dev)
{
    int ret = 0;
    ccu_config_t *config = &ccu_config;
    ccu_port_t* ccu = (ccu_port_t*)dev->ccu;
    ocpp_client_t *ocpp0 = (ocpp_client_t*)ccu->ocpp0;
    {
        int idx = 8;
        uint8_t *stream = dev->rxbuff;
        uint8_t emaid[64];
        
        idx = decode_U8(stream, idx, 64, emaid);

        ret = ocpp16_request_AuthorizateEMAID(ocpp0, emaid);
    }
    {
        int idx = 8;
        uint8_t *stream = dev->txbuff;
        idx = encode_U32(stream, idx, 1, &ret);
        dev->txbyte = encode_HEADER(stream, dev->transID++, config->TCU_ID, modbusTCP_CMD_0x94, NULL, idx - 8);
        return modbusTCP_QT_send(dev, dev->txbuff, dev->txbyte);
    }
}

void* modbusTCP_QT_Heartbeat(void* argv)
{
    modbusTCP_QT_t *dev = (modbusTCP_QT_t *)argv;
    qttcu_config_t *config = (qttcu_config_t*)dev->config;
    ccu_port_t* ccu = (ccu_port_t*)dev->ccu;
    secc_port_t* seccA = (secc_port_t*)ccu->seccA;
    secc_port_t* seccB = (secc_port_t*)ccu->seccB;

    if (config->enable == 0) return NULL;

    while( dev->quit == 0 )
    {
        sleep(2);
        if (dev->fd > 0)
        {
            modbusTCP_QT_cmd_0x90(dev, seccA->config->connectorId, seccA->GUN_Error, seccA->GUN_State);
            modbusTCP_QT_cmd_0x90(dev, seccB->config->connectorId, seccB->GUN_Error, seccB->GUN_State);
        }
    }
    return NULL;
}

static void modbusTCP_QT_connect(modbusTCP_QT_t* dev)
{
    qttcu_config_t *config = (qttcu_config_t*)dev->config;
    if (dev->fd <= 0)
    {
        if (strncmp(config->connect_ip_string, "eth", 3) == 0)
        {
            struct sockaddr_in if_addr;
            socklen_t addrlen = sizeof(if_addr);
            dev->connect_ip_string[0] = '\0';
            if (get_ipv4_address(config->connect_ip_string, &if_addr, dev->connect_ip_string, sizeof(dev->connect_ip_string)) == 0)
            {
                printd("get_ipv4_address(%s:%d)\n", dev->connect_ip_string, config->connect_port);
            }else{
                printe("get_ipv4_address(%s:%d), failed.\n", dev->connect_ip_string, config->connect_port);
            }
        }
        if (strlen(dev->connect_ip_string) > 0)
        {
            dev->fd = socket(AF_INET, SOCK_STREAM, 0);
            int enable = 1;
            setsockopt(dev->fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
            struct sockaddr_in peer;
            peer.sin_family = AF_INET;
            peer.sin_port = htons(config->connect_port);
            peer.sin_addr.s_addr = inet_addr(dev->connect_ip_string);
            int ret = connect(dev->fd, (struct sockaddr *)&peer, sizeof(peer));
            if (ret < 0)
            {
                printe("socket connect(%s:%d).fd=%d, error=%d, ret=%d\n", dev->connect_ip_string, config->connect_port, dev->fd, errno, ret);
                shutdown(dev->fd, SHUT_RDWR); close(dev->fd); dev->fd = -1;
            }else{
                printi("socket connect(%s:%d) ok, fd = %d\n", dev->connect_ip_string, config->connect_port, dev->fd);
            }
        }
    }
}

void* modbusTCP_QT_loop(void* args)
{
    modbusTCP_QT_t* dev = (modbusTCP_QT_t*)args;
    qttcu_config_t *config = (qttcu_config_t*)dev->config;
    if (config->enable == 0) return NULL;
    while (dev->quit == 0)
    {
        modbusTCP_QT_connect(dev);
        int cmd = modbusTCP_QT_recv(dev, 500);
        // printd("dev->rxbyte:%d, FRAMELEN:%d, pkg->protoID:%d\n", dev->rxbyte, FRAMELEN, dev->frame.protoID);
        switch(cmd)
        {
            case modbusTCP_CMD_0x90: break;
            case modbusTCP_CMD_0x91: break;
            case modbusTCP_CMD_0x92:{ modebusTCP_QT_cmd_0x92_resp(dev);} break;
            case modbusTCP_CMD_0x93:{ modebusTCP_QT_cmd_0x93_resp(dev);} break;
            case modbusTCP_CMD_0x94:{ modebusTCP_QT_cmd_0x94_resp(dev);} break;
        }
    }
    return NULL;
}


int modbusTCP_QT_init(modbusTCP_QT_t* dev)
{
    if (dev)
    {
        qttcu_config_t *config = (qttcu_config_t*)&ccu_config.qttcu;
        dev->config = config;

        pthread_mutex_init(&dev->mutex0, NULL);
        pthread_mutex_init(&dev->mutex1, NULL);

        dev->quit = 0;
        dev->debug = 1;
        socketpair(AF_UNIX, SOCK_STREAM, 0, dev->fp);

        modbusTCP_QT_connect(dev);
        mthread_init(&dev->threadRx, modbusTCP_QT_loop, dev);
        mthread_init(&dev->threadTx, modbusTCP_QT_Heartbeat, dev);
        return 0;
    }
    return -1;
}

void modbusTCP_QT_exit(modbusTCP_QT_t* dev)
{
    if (dev)
    {
        printd("...\n");
        dev->quit = 1;
        mthread_exit(&dev->threadRx);
        mthread_exit(&dev->threadTx);
        if (dev->fd > 0)
        {
            shutdown(dev->fd, SHUT_RDWR);
            close(dev->fd);
            dev->fd = -1;
        }
        if (dev->fp[0] > 0)
        {
            shutdown(dev->fp[0], SHUT_RDWR);
            close(dev->fp[0]);
            dev->fp[0] = -1;
        }
        
        if (dev->fp[1] > 0)
        {
            shutdown(dev->fp[1], SHUT_RDWR);
            close(dev->fp[1]);
            dev->fp[1] = -1;
        }        
        pthread_mutex_destroy(&dev->mutex0);
        pthread_mutex_destroy(&dev->mutex1);
    }
}