#include "DX_VOS_BaseSecureTime.h"
#include "VOS_API/DX_VOS_SecureStorage.h"

static DxBool g_DxCacheEnabled[DX_NUM_OF_CLOCKS] = { DX_FALSE, DX_FALSE, DX_FALSE};
static DxUint32 g_DxDelta[DX_NUM_OF_CLOCKS] = { 0, 0, 0};

static DxStatus GetClockDelta(DxUint32 clockId, DxUint32* delta)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);

    DX_ASSERT_PARAM(clockId < DX_NUM_OF_CLOCKS);
    DX_ASSERT_PARAM(delta != DX_NULL);

    if (clockId == DX_GENERIC_CLOCK)
    {
        *delta = 0;
        DX_RETURN(DX_SUCCESS);
    }

    if (!g_DxCacheEnabled[clockId])
    {
        g_DxCacheEnabled[clockId] = DX_TRUE;
        g_DxDelta[clockId] = DX_MAX_INT32;
        result = DX_VOS_LoadSecureItem(DX_SECURE_TIME_ITEM_ID + clockId, delta, sizeof(delta), DX_NULL);
        if (result != DX_SUCCESS)
            RETURN_CONST_STATUS(DX_VOS_SECURE_TIME_NOT_SET);

        g_DxDelta[clockId] = *delta;
    }

    if (g_DxDelta[clockId] == DX_MAX_INT32)
        return DX_VOS_SECURE_TIME_NOT_SET;

    *delta = g_DxDelta[clockId];

    DX_RETURN(DX_SUCCESS);
}

static DxStatus SetClockDelta(DxUint32 clockId, DxUint32 delta)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);

    if (clockId == DX_GENERIC_CLOCK)
        DX_RETURN(DX_SUCCESS);

    result = DX_VOS_SaveSecureItem(DX_SECURE_TIME_ITEM_ID + clockId, &delta, sizeof(delta));
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    g_DxCacheEnabled[clockId] = DX_TRUE;
    g_DxDelta[clockId] = delta;

    DX_RETURN(DX_SUCCESS);
}

static DxStatus SetRealSecureClock(DxUint32 secsTime)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);
    DxUint32 clockId = 0;
    
    result = DX_VOS_BaseSetSecureTime(secsTime);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    for (clockId = DX_OMA_CLOCK; clockId < DX_NUM_OF_CLOCKS; clockId++)
    {
        DX_VOS_DeleteSecureItem(DX_SECURE_TIME_ITEM_ID + clockId);

        g_DxCacheEnabled[clockId] = DX_FALSE;
        g_DxDelta[clockId] = 0;
    }

    DX_RETURN(DX_SUCCESS);
}

DxStatus DX_VOS_SetSecureTimeFromSecs(DxUint32 clockId, DxTime_t secs)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);
    DxUint32 delta = 0;
    DxTime_t currTime = 0;
    
    result = DX_VOS_BaseGetSecureTime(&currTime);
    if (result == DX_VOS_SECURE_TIME_NOT_SET || clockId == DX_GENERIC_CLOCK)
    {
        result = SetRealSecureClock(secs);
        currTime = secs;
    }

    if (result != DX_SUCCESS)
    	RETURN_VAR_STATUS(result);

    delta = secs - currTime;

    result = SetClockDelta(clockId, delta);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(DX_VOS_SECURE_TIME_ERROR);

    DX_RETURN(DX_SUCCESS);
}

DxStatus DX_VOS_GetSecureTimeAsSecs(DxUint32 clockId, DxTime_t* secs)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);
    DxUint32 delta = 0;

    DX_ASSERT_PARAM(secs != DX_NULL);

	*secs = -1;

    result = DX_VOS_BaseGetSecureTime(secs);
    if (result != DX_SUCCESS)
        RETURN_OLD_ERROR(result);

    result = GetClockDelta(clockId, &delta);
    if (result != DX_SUCCESS)
        RETURN_OLD_ERROR(result);

    *secs += delta;

    DX_RETURN(DX_SUCCESS);
}

DxStatus DX_VOS_SetSecureTimeFromGenTime(DxUint32 clockId, const DxGeneralizedTime_t *aTime)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);
    DxTime_t secsTime = 0;

    result = DX_VOS_GenTimeToSecs(aTime, &secsTime);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    result = DX_VOS_SetSecureTimeFromSecs(clockId, secsTime);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    DX_RETURN(DX_SUCCESS);
}

DxStatus DX_VOS_GetSecureTimeAsGenTime(DxUint32 clockId, DxGeneralizedTime_t *aTime)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);
    DxTime_t secsTime = 0;

    result = DX_VOS_GetSecureTimeAsSecs(clockId, &secsTime);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    result = DX_VOS_SecsToGenTime(secsTime, aTime);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    DX_RETURN(DX_SUCCESS);
}

DxStatus DX_VOS_GetSecureTimeStruct(DxUint32 clockId, DxTimeStruct_t *aTime)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);
    DxTime_t secsTime = 0;

    result = DX_VOS_GetSecureTimeAsSecs(clockId, &secsTime);
    if (result != DX_SUCCESS)
        RETURN_OLD_ERROR(result);

    result = DX_VOS_GetGlobalTime(secsTime, aTime);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    DX_RETURN(DX_SUCCESS);
}


DxStatus DX_VOS_SetSecureTimeStruct(DxUint32 clockId, const DxTimeStruct_t *aTime)
{
    DX_DECLARE(DxStatus, result, DX_SUCCESS);
    DxTime_t secsTime = 0;

    result = DX_VOS_GlobalTimeToSecs(aTime, &secsTime);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    result = DX_VOS_SetSecureTimeFromSecs(clockId, secsTime);
    if (result != DX_SUCCESS)
        RETURN_VAR_STATUS(result);

    DX_RETURN(DX_SUCCESS);
}

DxStatus DX_VOS_UnsetSecureTime()
{
    return DX_VOS_BaseUnsetSecureTime();
}
