#include "cJSON_Utils.h"
#include "ocpp16_client.h"
#include "ocpp16_OfflineChargingRecords.h"
#include "ocpp16_configkey.h"
#include "secc_port.h"
/***************************************************************************************************************/

ChargingRecord_t gunArecord = {
    .connectorId = 1,
    .maxLength = 0,
    .count = 0,
    .items = NULL
};

ChargingRecord_t gunBrecord = {
    .connectorId = 2,
    .maxLength = 0,
    .count = 0,
    .items = NULL
};

static void ocpp16_OfflineChargingRecords_save(ChargingRecord_t* record)
{
    int i;
    char strtime[256];
    printd("step\n");
    if (record->startTime==0) return;
    cJSON* root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "connectorId", record->connectorId);
    cJSON_AddNumberToObject(root, "transactionId", record->transactionId);
    cJSON_AddNumberToObject(root, "connectorId", record->connectorId);
    cJSON_AddNumberToObject(root, "startTime", record->startTime);
    cJSON_AddNumberToObject(root, "stopTime", record->stopTime);
    cJSON_AddNumberToObject(root, "startKWh", record->startKWh);
    cJSON_AddNumberToObject(root, "stopKWh", record->stopKWh);
    cJSON* records = cJSON_AddArrayToObject(root, "items");
    for (i=0; i<record->count; i++)
    {
        cJSON* items = cJSON_CreateObject();
        cJSON_AddNumberToObject(items, "timestamp", record->items[i].timestamp);
        cJSON_AddNumberToObject(items, "energyKWh", record->items[i].energyKWh);
        cJSON_AddItemToArray(records, items);
    }
    
    char *fbuff = cJSON_Print(root);
    printd("step\n");
    char filename[256];
    sprintf(filename, "/home/root/app_run/record/GUN%d-%s.json", record->connectorId, format_UTCFileName(strtime, record->startTime));
    printd("step filename=%s\n", filename);
    printd("step\n");
    FILE *fp = fopen(filename, "wb");
    if (fp != NULL)
    {
        printd("step\n");
        fwrite(fbuff, 1, strlen(fbuff), fp);
        fclose(fp);
    }else{
        printd("open(%s, wb) failed.\n", filename);
    }
    free(fbuff);
    printd("step\n");
    cJSON_Delete(root);  
}

static int ocpp16_OfflineChargingRecords_load(const char* filename)
{
    int i;
    int ret = -1;
    FILE* file = fopen(filename, "rb");
    if (file)
    {
        fseek(file, 0, SEEK_END);
        int flen = ftell(file);
        fseek(file, 0, SEEK_SET);
        char* fbuff = malloc(flen+10);
        if (fbuff)
        {
            fread(fbuff, flen, 1, file);
            cJSON* root = cJSON_Parse(fbuff);
            if (root)
            {
                int connectorId = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "connectorId"));
                int transactionId = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "transactionId"));
                char* idTag = cJSON_GetStringValue(cJSON_GetObjectItem(root, "idTag"));
                int startTime = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "startTime"));
                int stopTime = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "stopTime"));
                int startKWh = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "startKWh"));
                int stopKWh = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(root, "stopKWh"));
                cJSON* records = cJSON_GetObjectItem(root, "items");
                int count = cJSON_GetArraySize(records);

                {
                    ccu_port_t* ccu = (ccu_port_t*)&ccu_port;
                    StartTransactionReq_t req;
                    StartTransactionConf_t conf;
                    req.connectorId = connectorId;
                    sprintf(req.idTag, "%s", idTag);
                    req.reservationId = 0;
                    req.timestamp = startTime;
                    req.meterStart = startKWh;
                    if (ocpp16_StartTransaction_request(ccu->ocpp0, &req, &conf) == AuthorizationStatus_Accepted)
                    {
                        transactionId = conf.transactionId;
                        printi("offline record report(connectorId:%d, transactionId:%d)\n", connectorId, transactionId);
                        {
                            StopTransactionReq_t req;
                            StopTransactionConf_t conf;
                            req.transactionId = transactionId;
                            req.meterStop  = stopKWh;
                            req.reason = 0;
                            req.timestamp = stopTime;
                            req.transactiondata.count = 0;
                            for (i=0; i < count; i++)
                            {
                                cJSON* items = cJSON_GetArrayItem(records, i);
                                int timestamp = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(items, "timestamp"));
                                float energyKWh = (float)cJSON_GetNumberValue(cJSON_GetObjectItem(items, "energyKWh"));
                                sprintf_UTCTime(req.transactiondata.items[i].timestamp, timestamp);
                                req.transactiondata.items[i].sampledvalue.count=1;
                                req.transactiondata.items[i].sampledvalue.items[0].unit = UnitOfMeasure_kWh;
                                req.transactiondata.items[i].sampledvalue.items[0].measurand = Measurand_Energy_Active_Import_Register;
                                sprintf(req.transactiondata.items[i].sampledvalue.items[0].value,"%f", energyKWh);
                                req.transactiondata.count++;
                            }
                            if (ocpp16_StopTransaction_request(ccu->ocpp0, &req, &conf) == AuthorizationStatus_Accepted)
                            {
                                ret = 0;
                                printi("offline record report(connectorId:%d, transactionId:%d)\n", connectorId, transactionId);
                            }else{
                                printe("offline record report(connectorId:%d) failed\n", connectorId);
                            }
                        }
                    }else{
                        printe("offline record report(connectorId:%d) failed\n", connectorId);
                    }
                }

                cJSON_Delete(root);
                
            }else{
                printd("cJSON_Parse(%p) failed.\n", fbuff);
            }
            free(fbuff);
        }else{
            printd("malloc(%d) failed.\n", flen);
        }
        fclose(file);
    }else{
        printd("open(%s, rb) failed.\n", filename);
    }
    return ret;
}


void ocpp16_OfflineChargingRecords_startup(void *secc)
{
    secc_port_t* gun = (secc_port_t*)secc;
    ChargingRecord_t* record = (ChargingRecord_t*)gun->offline_recorder;
    if (record->items == NULL)
    {
        record->items = malloc(sizeof(MeterEnergy_t) * 1000);
        record->maxLength = 1000;
        record->count = 0;
    }
    if (record->count >= record->maxLength)
    {
        record->items = realloc(record->items, sizeof(MeterEnergy_t) * (record->maxLength+1000));
        record->maxLength += 1000;
    }
    record->count = 0;
    record->connectorId = gun->config->connectorId;
    record->transactionId = gun->transactionId;
    sprintf(record->idTag, "%s", gun->emaid);
    record->startTime = gun->started_UTC;
    record->startKWh = gun->started_KWh;
    record->lastTime = time(NULL);
}

void ocpp16_OfflineChargingRecords_metervalues(void *secc)
{
    secc_port_t* gun = (secc_port_t*)secc;
    ChargingRecord_t* record = (ChargingRecord_t*)gun->offline_recorder;

    if (record->transactionId != gun->transactionId)
    {   //充电中途，ocpp掉线，造成漏掉了ocpp16_OfflineChargingRecords_startup
        record->count = 0;
        record->connectorId = gun->config->connectorId;
        record->transactionId = gun->transactionId;
        sprintf(record->idTag, "%s", gun->emaid);
        record->startTime = gun->started_UTC;
        record->startKWh = gun->started_KWh;
    }

    time_t now = time(NULL);
    if ((now-record->lastTime)> (15*60))
    {
        record->lastTime = now;
        if (record->items == NULL)
        {
            record->items = malloc(sizeof(MeterEnergy_t) * 1000);
            record->maxLength = 1000;
            record->count = 0;
        }
        if (record->count >= record->maxLength)
        {
            record->items = realloc(record->items, sizeof(MeterEnergy_t) * (record->maxLength+1000));
            record->maxLength += 1000;
        }
        record->items[record->count].timestamp = time(NULL);
        record->items[record->count].energyKWh = gun->EMeters;
        record->count++;
    }
}

void ocpp16_OfflineChargingRecords_shutdown(void *secc)
{
    secc_port_t* gun = (secc_port_t*)secc;
    ChargingRecord_t* record = (ChargingRecord_t*)gun->offline_recorder;

    if (record->transactionId != gun->transactionId)
    {   //充电中途，ocpp掉线，造成漏掉了ocpp16_OfflineChargingRecords_startup
        record->count = 0;
        record->connectorId = gun->config->connectorId;
        record->transactionId = gun->transactionId;
        sprintf(record->idTag, "%s", gun->emaid);
        record->startTime = gun->started_UTC;
        record->startKWh = gun->started_KWh;
    }

    record->stopTime = time(NULL);
    record->stopKWh = gun->EMeters;
    printd("step\n");
    ocpp16_OfflineChargingRecords_save(record);
}

void ocpp16_OfflineChargingRecords_upload(void)
{
    DIR *dir = NULL;
    dir = opendir("/home/root/app_run/record/");
    if (dir)
    {
        struct dirent *dirent = NULL;
        while ((dirent = readdir(dir)))
        {
            if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) { continue; }
            if (ocpp16_OfflineChargingRecords_load(dirent->d_name) == 0)
            {
                remove(dirent->d_name);
            }
        }
        closedir(dir);
    }
}

void ocpp16_OfflineChargingRecords_exit(void)
{
    if (gunArecord.items) free(gunArecord.items);
    gunArecord.items = NULL;
    gunArecord.maxLength=0;
    gunArecord.count=0;
    if (gunBrecord.items) free(gunBrecord.items);
    gunBrecord.items = NULL;
    gunBrecord.maxLength=0;
    gunBrecord.count=0;
}