
#include "cJSON_Utils.h"
#include "ocpp16_authorization.h"
#include "ocpp16_configkey.h"
/***************************************************************************************************************/

AuthorizationList_t  authorizationCache;
AuthorizationList_t  localAuthorizationList;

static void ocpp16_AuthorizationCache_cleanup(void)
{
    time_t current = time(NULL);
    int i, j;
    for (i=0; i<authorizationCache.count; i++)
    {
        if (authorizationCache.items[i].expiryDate < current)
        {
            for (j=i+1; j<authorizationCache.count; j++)
            {
                memcpy(&authorizationCache.items[j-1], &authorizationCache.items[j], sizeof(AuthorizationDataNFC_t));
            }
            authorizationCache.count--;
        }
    } 
}

AuthorizationDataNFC_t* copp16_AuthorizationCache_find(const uint8_t* idTag)
{
    int i;
    ocpp16_AuthorizationCache_cleanup();
    for (i=0; i<authorizationCache.count; i++)
    {
        if (strncmp(idTag, authorizationCache.items[i].idTag, strlen(idTag))==0)
        {
            return &authorizationCache.items[i];
        }
    }
    return NULL;
}


int ocpp16_AuthorizationCache_append(uint8_t* idTag, uint8_t* parentIdTag, time_t expiryDate, AuthorizationStatus_t status)
{
    ocpp16_AuthorizationCache_cleanup();
    if (status == AuthorizationStatus_Accepted || status == AuthorizationStatus_Blocked)
    {
        AuthorizationDataNFC_t* items = copp16_AuthorizationCache_find(idTag);
        if (items == NULL)
        {
            if (authorizationCache.count < ocpp16_configkeys.coreProfile.AuthorizationCacheMaxLength)
            {
                sprintf(authorizationCache.items[authorizationCache.count].idTag, "%s", idTag);
                if (parentIdTag)
                {
                    sprintf(authorizationCache.items[authorizationCache.count].parentIdTag, "%s", parentIdTag);
                }
                authorizationCache.items[authorizationCache.count].expiryDate = expiryDate;
                authorizationCache.items[authorizationCache.count].status = status;
                authorizationCache.count++;
                return 0;
            }
        }
        if (parentIdTag)
        {
            sprintf(items->parentIdTag, "%s", parentIdTag);
        }
        items->expiryDate = expiryDate;
        items->status = status;
    }
    return -1;
}

static void ocpp16_LocalAuthorizationList_cleanup(void)
{
    time_t current = time(NULL);
    int i, j;
    for (i=0; i<localAuthorizationList.count; i++)
    {
        if (localAuthorizationList.items[i].expiryDate < current)
        {
            for (j=i+1; j<localAuthorizationList.count; j++)
            {
                memcpy(&localAuthorizationList.items[j-1], &localAuthorizationList.items[j], sizeof(AuthorizationDataNFC_t));
            }
            localAuthorizationList.count--;
        }
    } 
}

AuthorizationDataNFC_t* copp16_LocalAuthorizationList_find(const uint8_t* idTag)
{
    int i;
    for (i=0; i<localAuthorizationList.count; i++)
    {
        if (strncmp(idTag, localAuthorizationList.items[i].idTag, strlen(idTag))==0)
        {
            return &localAuthorizationList.items[i];
        }
    }
    return NULL;
}

int ocpp16_LocalAuthorizationList_append(uint8_t* idTag, uint8_t* parentIdTag, time_t expiryDate, AuthorizationStatus_t status)
{
    ocpp16_LocalAuthorizationList_cleanup();
    if (status == AuthorizationStatus_Accepted || status == AuthorizationStatus_Blocked)
    {
        AuthorizationDataNFC_t* items = copp16_LocalAuthorizationList_find(idTag);
        if (items == NULL)
        {
            if (localAuthorizationList.count < ocpp16_configkeys.localAuthListManagemenProfile.LocalAuthListMaxLength)
            {
                sprintf(localAuthorizationList.items[localAuthorizationList.count].idTag, "%s", idTag);
                if (parentIdTag)
                {
                    sprintf(localAuthorizationList.items[localAuthorizationList.count].parentIdTag, "%s", parentIdTag);
                }else{
                    localAuthorizationList.items[localAuthorizationList.count].parentIdTag[0]='\0';
                }
                localAuthorizationList.items[localAuthorizationList.count].expiryDate = expiryDate;
                localAuthorizationList.items[localAuthorizationList.count].status = status;
                localAuthorizationList.count++;
                return 0;
            }
        }
        if (parentIdTag)
        {
            sprintf(items->parentIdTag, "%s", parentIdTag);
        }
        items->expiryDate = expiryDate;
        items->status = status;
    }
    return -1;
}

static const char* AuthorizationStatus_toString(AuthorizationStatus_t status)
{
    switch(status)
    {
        case AuthorizationStatus_Accepted : return "Accepted";
        case AuthorizationStatus_Blocked : return "Blocked";
        case AuthorizationStatus_Expired : return "Expired";
        case AuthorizationStatus_Invalid : return "Invalid";
        default : return "ConcurrentTx";
    }
}
/***************************************************************************************************************/
void ocpp16_authorization_save(AuthorizationList_t* local, AuthorizationList_t* cache, const char* filename)
{
    int i;
    char strtime[128];

    cJSON* root = cJSON_CreateObject();
    cJSON* caches = cJSON_CreateObject();
    cJSON* locals = cJSON_CreateObject();
    for (i=0; i<local->count; i++)
    {
        cJSON* items = cJSON_CreateObject();
        cJSON_AddStringToObject(items, "idTag", local->items[i].idTag);
        cJSON_AddStringToObject(items, "expiryDate", format_UTCTime(strtime, local->items[i].expiryDate));
        cJSON_AddStringToObject(items, "parentIdTag", local->items[i].parentIdTag);
        cJSON_AddStringToObject(items, "status", AuthorizationStatus_toString(local->items[i].status));

        cJSON_AddItemToArray(locals, items);
    }
    for (i=0; i<cache->count; i++)
    {
        cJSON* items = cJSON_CreateObject();
        cJSON_AddStringToObject(items, "idTag", cache->items[i].idTag);
        cJSON_AddStringToObject(items, "expiryDate", format_UTCTime(strtime, cache->items[i].expiryDate));
        cJSON_AddStringToObject(items, "parentIdTag", cache->items[i].parentIdTag);
        cJSON_AddStringToObject(items, "status", AuthorizationStatus_toString(cache->items[i].status));

        cJSON_AddItemToArray(caches, items);
    }
    cJSON_AddItemToObject(root, "LocalAuthorizationList", locals);
    cJSON_AddItemToObject(root, "AuthorizationCache", caches);
    char *fbuff = cJSON_Print(root);
    FILE *fp = fopen(filename, "wb");
    if (fp != NULL)
    {
        fwrite(fbuff, 1, strlen(fbuff), fp);
        fclose(fp);
    }else{
        printd("open(%s, wb) failed.\n", filename);
    }
    free(fbuff);
    cJSON_Delete(root);
}

void ocpp16_authorization_load(AuthorizationList_t* local, AuthorizationList_t* cache, const char* filename)
{
    int i;
    if (local->items) free(local->items);
    memset(local, 0, sizeof(AuthorizationList_t));
    if (cache->items) free(cache->items);
    memset(cache, 0, sizeof(AuthorizationList_t));
    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+1);
        if (fbuff)
        {
            fread(fbuff, flen, 1, file);
            cJSON* root = cJSON_Parse(fbuff);
            if (root)
            {
                local->count = 0;
                cJSON* locals = cJSON_GetObjectItem(root, "LocalAuthorizationList");
                int size = cJSON_GetArraySize(locals);
                if (size > ocpp16_configkeys.localAuthListManagemenProfile.LocalAuthListMaxLength)
                {
                    ocpp16_configkeys.localAuthListManagemenProfile.LocalAuthListMaxLength = size;
                }
                local->items = malloc(sizeof(AuthorizationDataNFC_t) * size);
                memset(local->items, 0, sizeof(AuthorizationDataNFC_t) * size);

                for (i=0; i< size; i++)
                {
                    cJSON* items = cJSON_GetArrayItem(locals, i);
                    local->count++;
                    sprintf(local->items[i].idTag, "%s", cJSON_GetStringValue(cJSON_GetObjectItem(items, "idTag")));
                    local->items[i].expiryDate = RFC3339String_to_time(cJSON_GetStringValue(cJSON_GetObjectItem(items, "expiryDate")), NULL, NULL);
                    sprintf(local->items[i].parentIdTag, "%s", cJSON_GetStringValue(cJSON_GetObjectItem(items, "parentIdTag")));
                    local->items[i].status = AuthorizationStatus_ConcurrentTx;
                    char* status = cJSON_GetStringValue(cJSON_GetObjectItem(items, "status"));
                    if (strncmp(status, "Accepted", strlen(status)==0)) local->items[i].status = AuthorizationStatus_Accepted;
                    if (strncmp(status, "Blocked", strlen(status)==0)) local->items[i].status = AuthorizationStatus_Blocked;
                    if (strncmp(status, "Expired", strlen(status)==0)) local->items[i].status = AuthorizationStatus_Expired;
                    if (strncmp(status, "Invalid", strlen(status)==0)) local->items[i].status = AuthorizationStatus_Invalid;
                }

                cache->count = 0;
                cJSON* caches = cJSON_GetObjectItem(root, "AuthorizationCache");
                size = cJSON_GetArraySize(caches);
                if (size > ocpp16_configkeys.coreProfile.AuthorizationCacheMaxLength)
                {
                    ocpp16_configkeys.coreProfile.AuthorizationCacheMaxLength = size;
                }
                cache->items = malloc(sizeof(AuthorizationDataNFC_t) * size);
                memset(cache->items, 0, sizeof(AuthorizationDataNFC_t) * size);
                for (i=0; i< size; i++)
                {
                    cJSON* items = cJSON_GetArrayItem(caches, i);
                    cache->count++;
                    sprintf(cache->items[i].idTag, "%s", cJSON_GetStringValue(cJSON_GetObjectItem(items, "idTag")));
                    cache->items[i].expiryDate = RFC3339String_to_time(cJSON_GetStringValue(cJSON_GetObjectItem(items, "expiryDate")), NULL, NULL);
                    sprintf(cache->items[i].parentIdTag, "%s", cJSON_GetStringValue(cJSON_GetObjectItem(items, "parentIdTag")));
                    cache->items[i].status = AuthorizationStatus_ConcurrentTx;
                    char* status = cJSON_GetStringValue(cJSON_GetObjectItem(items, "status"));
                    if (strncmp(status, "Accepted", strlen(status)==0)) cache->items[i].status = AuthorizationStatus_Accepted;
                    if (strncmp(status, "Blocked", strlen(status)==0)) cache->items[i].status = AuthorizationStatus_Blocked;
                    if (strncmp(status, "Expired", strlen(status)==0)) cache->items[i].status = AuthorizationStatus_Expired;
                    if (strncmp(status, "Invalid", strlen(status)==0)) cache->items[i].status = AuthorizationStatus_Invalid;
                }
                
                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);
    } 
}

void ocpp16_authorization_init(void)
{
    if (localAuthorizationList.items) { free(localAuthorizationList.items); }
    memset(&localAuthorizationList, 0, sizeof(AuthorizationList_t));
    if (authorizationCache.items) { free(authorizationCache.items); }
    memset(&authorizationCache, 0, sizeof(AuthorizationList_t));
    ocpp16_authorization_load(&localAuthorizationList, &authorizationCache, "/home/root/app_run/etc/ocpp16_configkey.json");
}

void ocpp16_authorization_exit(void)
{
    ocpp16_authorization_save(&localAuthorizationList, &authorizationCache, "/home/root/app_run/etc/ocpp16_configkey.json");
    if (localAuthorizationList.items) { free(localAuthorizationList.items); }
    memset(&localAuthorizationList, 0, sizeof(AuthorizationList_t));
    if (authorizationCache.items) { free(authorizationCache.items); }
    memset(&authorizationCache, 0, sizeof(AuthorizationList_t));
}
