/**@@@+++@@@@******************************************************************
**
** Microsoft Windows Media
** Copyright (C) Microsoft Corporation. All rights reserved.
**
***@@@---@@@@******************************************************************
*/

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmcontextsizes.h>
#include <drmsyncstore.h>
#include <drmhds.h>


static const DRM_HDS_NAMESPACE g_namespaceSyncStore = 
{ 
    TWO_BYTES('s',  'y'),  TWO_BYTES('n',  'c'),  TWO_BYTES('s',  't'),  TWO_BYTES('o',  'r'), 
    TWO_BYTES('e',  '\0'), TWO_BYTES('\0', '\0'), TWO_BYTES('\0', '\0'), TWO_BYTES('\0', '\0') 
};

extern const DRM_CONST_STRING *g_apdstrActions [1];


/*********************************************************************
** Function: DRM_SNC_DeleteKID
**
** Synopsis: delete the indicated KID from the sync store; its absense
**           is not an error
**
** Parameters:
**
** [f_pcontextSYN]          -- initialized SYNC context
** [f_pkid]                 -- pointer to KID to be added/updated
**********************************************************************/

DRM_RESULT DRM_API DRM_SNC_DeleteKID(
    IN       DRM_SYNC_CONTEXT *f_pcontextSYN, 
    IN const DRM_KID          *f_pkid)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_SYNC_CONTEXT_PRIVATE *pcontextSYN = (DRM_SYNC_CONTEXT_PRIVATE *) f_pcontextSYN;

    ChkArg(f_pcontextSYN != NULL
        && f_pkid        != NULL);

    dr = DRM_HDS_DeleteSlot(&pcontextSYN->contextNameSpace, 
                            f_pkid, 
                            f_pkid,
                            NULL,
                            TRUE);

    if (dr == DRM_E_HDSSLOTNOTFOUND)
    {
        dr = DRM_SUCCESS;
    }
    else
    {
        ChkDR(dr);
    }
    
ErrorExit:
    return dr;
} /* DRM_SNC_DeleteKID */

/**********************************************************************
 *				     PUBLIC FUNCTION DRM_SNC_OpenStore
 **********************************************************************/

DRM_RESULT DRM_API DRM_SNC_OpenStore  (
    IN  DRM_HDS_CONTEXT        *f_pcontextHDS,
    OUT DRM_SYNC_CONTEXT       *f_pcontextSYN)
{
    DRM_RESULT				  dr           =  DRM_SUCCESS;
    DRM_SYNC_CONTEXT_PRIVATE *pcontextSync = (DRM_SYNC_CONTEXT_PRIVATE *) f_pcontextSYN;

    ChkArg (f_pcontextHDS   != NULL
         && f_pcontextSYN != NULL);

    DRMASSERT (SIZEOF (DRM_SYNC_CONTEXT_PRIVATE) <= SIZEOF (DRM_SYNC_CONTEXT));

    /* open the namespace for the Sync Store */    
    ChkDR( DRM_HDS_OpenNamespace( f_pcontextHDS, 
                                  &g_namespaceSyncStore, 
                                  eDRM_HDS_CREATE_IF_NEW | eDRM_HDS_LOCKWAIT,
                                  16,
                                  &pcontextSync->contextNameSpace ) );
    pcontextSync->fInited            = TRUE;
    pcontextSync->pcontextHDSStorage = f_pcontextHDS;

ErrorExit:

    return dr;
}

/**********************************************************************
 *				     PUBLIC FUNCTION DRM_SNC_CloseStore
 **********************************************************************/

DRM_RESULT DRM_API DRM_SNC_CloseStore (
    IN  DRM_SYNC_CONTEXT       *f_pcontextSYN)
{
    DRM_SYNC_CONTEXT_PRIVATE *pcontextSync = (DRM_SYNC_CONTEXT_PRIVATE *) f_pcontextSYN;
    DRM_RESULT	dr = DRM_SUCCESS;

    ChkArg (f_pcontextSYN != NULL);

    if (pcontextSync->fInited)
    {
        ChkDR (DRM_HDS_CloseNamespace (&pcontextSync->contextNameSpace));
    }

    ZEROMEM(f_pcontextSYN, SIZEOF (DRM_SYNC_CONTEXT));
    
ErrorExit:
    return dr;
}

/**********************************************************************
 *				    PUBLIC FUNCTION DRM_SNC_InitEnum
 ***********************************************************************/

DRM_RESULT DRM_API DRM_SNC_InitEnum   (
    IN  DRM_SYNC_CONTEXT       *f_pcontextSYN, 
    OUT DRM_SYNC_ENUM_CONTEXT  *f_pcontextSYNEnum)
{
    DRM_SYNC_CONTEXT_PRIVATE      *pcontextSync     = (DRM_SYNC_CONTEXT_PRIVATE *)      f_pcontextSYN;
    DRM_SYNC_ENUM_CONTEXT_PRIVATE *pcontextSyncEnum = (DRM_SYNC_ENUM_CONTEXT_PRIVATE *) f_pcontextSYNEnum;
    DRM_RESULT dr =  DRM_SUCCESS;  

    ChkArg(f_pcontextSYN         != NULL
        && f_pcontextSYNEnum     != NULL);
    ChkArg(pcontextSync->fInited);

    DRMASSERT (SIZEOF (DRM_SYNC_CONTEXT_PRIVATE) <= SIZEOF (DRM_SYNC_CONTEXT));

    ZEROMEM(f_pcontextSYNEnum, SIZEOF (DRM_SYNC_ENUM_CONTEXT));

    dr = DRM_HDS_InitSlotEnum (&pcontextSync->contextNameSpace, 
                               NULL, 
                               eDRM_HDS_LOCKSHARED | eDRM_HDS_LOCKWAIT, 
                              &pcontextSyncEnum->contextHDSEnum);

    if (DRM_SUCCEEDED (dr))
    {
        pcontextSyncEnum->fAny = TRUE;
    }
    else if (dr == DRM_E_HDSSLOTNOTFOUND)
    {
        /* No KIDs found. */
        
		pcontextSyncEnum->fAny = FALSE;
        dr = DRM_SUCCESS;
    }
    else
    {
        ChkDR (dr);
    }
    
    pcontextSyncEnum->fInited      = TRUE;
    pcontextSyncEnum->pcontextSync = pcontextSync;

ErrorExit:    
    return dr;
}

/**********************************************************************
 *				    PUBLIC FUNCTION DRM_SNC_EnumNext
 ***********************************************************************/

DRM_RESULT DRM_API DRM_SNC_EnumNext   (
    IN  DRM_SYNC_ENUM_CONTEXT  *f_pcontextSYNEnum, 
    OUT DRM_KID                *f_pkid,
    OUT DRM_HDS_SLOT_HINT      *f_pslotHint)
{
	DRM_SYNC_ENUM_CONTEXT_PRIVATE *pcontextSyncEnum = (DRM_SYNC_ENUM_CONTEXT_PRIVATE *) f_pcontextSYNEnum;
    DRM_HDS_UNIQUEKEY keyHash;
    DRM_RESULT        dr     = DRM_SUCCESS;
    DRM_DWORD         cbSlot = SIZEOF (DRM_DWORD);

    ChkArg (f_pcontextSYNEnum != NULL
         && f_pkid            != NULL);

    ChkArg (pcontextSyncEnum->fInited);

    DRMASSERT (SIZEOF (DRM_SYNC_CONTEXT_PRIVATE) 
            <= SIZEOF (DRM_SYNC_CONTEXT));

    if  (pcontextSyncEnum->fAny == FALSE)
    {
        dr = DRM_E_NOMORE;
        goto ErrorExit;            
    }

    dr = DRM_HDS_SlotEnumNext(&pcontextSyncEnum->contextHDSEnum, 
                              &pcontextSyncEnum->contextHDSSlot, 
                              &keyHash,
                              &pcontextSyncEnum->itemSync.kid, 
                              &cbSlot);

    if (dr == DRM_E_HDSBLOCKMISMATCH 
    ||  dr == DRM_E_HDSSLOTNOTFOUND)
    {
        ChkDR (DRM_E_SYNC_ENTRYNOTFOUND);
    }

    ChkDR (dr); /* includes DRM_E_NOMORE */

    MEMCPY (f_pkid->rgb, 
            pcontextSyncEnum->itemSync.kid.rgb, 
            SIZEOF (DRM_KID));

    if( f_pslotHint != NULL )
    {
        DRM_HDS_MakeSlotHint( &pcontextSyncEnum->contextHDSSlot, 
                               f_pslotHint );
    }

    ChkDR (DRM_HDS_CloseSlot (&pcontextSyncEnum->contextHDSSlot));
    
ErrorExit:
    
    return dr;
}

/*********************************************************************
 *                   INTERNAL FUNCTIONS _GetKIDStoreData
 *                                      _SetKIDStoreData
 *
 * purpose: get/set the data currently in the sync store for this KID
 *********************************************************************/

/* Obselete hash key that was used in the previous sync store. */
static const DRM_HDS_HASHKEY g_keyHashSync = 
{ 
    TWO_BYTES(0xA9, 0x68), TWO_BYTES(0xAE, 0xEF),
    TWO_BYTES(0xA7, 0x66), TWO_BYTES(0xF4, 0xD7),
    TWO_BYTES(0xD0, 0xEE), TWO_BYTES(0x73, 0xDE),
    TWO_BYTES(0x65, 0xAE), TWO_BYTES(0xA7, 0xEA)
};

DRM_RESULT _GetKIDStoreData(
    IN       DRM_SYNC_CONTEXT       *f_pcontextSYN,
    IN const DRM_KID                *f_pkid,
    IN       DRM_HDS_SLOT_HINT      *f_pslotHint,
       OUT   DRM_LICENSE_STATE_DATA *f_plsd)
{
    DRM_SYNC_CONTEXT_PRIVATE *pcontextSync = (DRM_SYNC_CONTEXT_PRIVATE *) f_pcontextSYN;
    DRM_DWORD   cbSlot   = SIZEOF (DRM_LICENSE_STATE_DATA);
    DRM_RESULT dr        = DRM_SUCCESS;
    DRM_BOOL   fSlotOpen = FALSE;    
    DRM_WORD   iMember   = 0;
    
    dr = DRM_HDS_OpenSlot (&pcontextSync->contextNameSpace, 
                             eDRM_HDS_OPEN_EXISTING | eDRM_HDS_LOCKWAIT, 
                             f_pkid,
                             f_pkid, 
                             f_pslotHint,
                            &cbSlot, 
                            &pcontextSync->contextHDSSlot);
    
    if( dr == DRM_E_HDSSLOTNOTFOUND )
    {
        /* Older HDS files had a constant hashkey, so try opening the slot with that,
           and if it's there we should "upgrade" it to the new hashkey */
        ChkDR (DRM_HDS_OpenSlot (&pcontextSync->contextNameSpace, 
                         eDRM_HDS_OPEN_EXISTING | eDRM_HDS_LOCKWAIT, 
                        &g_keyHashSync,
                         f_pkid, 
                         NULL,
                        &cbSlot, 
                        &pcontextSync->contextHDSSlot));
        
        fSlotOpen = TRUE;

        /* It's there, so read out the data */
        ChkDR (DRM_HDS_SlotRead (&pcontextSync->contextHDSSlot, 
                                SIZEOF (DRM_LICENSE_STATE_DATA), 
                    (DRM_BYTE *)f_plsd, 
                                &cbSlot));
        DRM_HDS_CloseSlot (&pcontextSync->contextHDSSlot);
        fSlotOpen = FALSE;

        /* Delete the old slot */
        ChkDR(DRM_HDS_DeleteSlot(&pcontextSync->contextNameSpace, 
                &g_keyHashSync,
                 f_pkid, 
                 NULL,
                 FALSE ));

        /* Open the new slot */
        ChkDR (DRM_HDS_OpenSlot (&pcontextSync->contextNameSpace, 
                         eDRM_HDS_CREATE_IF_NEW | eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT, 
                         f_pkid,
                         f_pkid,
                         NULL,
                        &cbSlot,
                        &pcontextSync->contextHDSSlot));
        fSlotOpen = TRUE;

        /* Write the data for the new slot */
        ChkDR (DRM_HDS_SlotWrite (&pcontextSync->contextHDSSlot, 
                                SIZEOF (DRM_LICENSE_STATE_DATA), 
                    (DRM_BYTE *)f_plsd, 
                                &cbSlot));
        
#if !_DATASTORE_WRITE_THRU
        ChkDR (DRM_HDS_CommitNamespace(&pcontextSync->contextNameSpace));
#endif
        
    }
    else
    {
        ChkDR( dr );
        fSlotOpen = TRUE;

        ChkDR (DRM_HDS_SlotRead (&pcontextSync->contextHDSSlot, 
                                SIZEOF (DRM_LICENSE_STATE_DATA), 
                    (DRM_BYTE *)f_plsd, 
                                &cbSlot));
    }


                             
    FIX_ENDIAN_DWORD(f_plsd->dwCategory);                        
    FIX_ENDIAN_DWORD(f_plsd->dwStreamId);
    FIX_ENDIAN_DWORD(f_plsd->dwNumCounts);
    FIX_ENDIAN_DWORD(f_plsd->dwNumDates); 
    FIX_ENDIAN_DWORD(f_plsd->dwVague);    
    
    for (iMember = 0; 
         iMember < NO_OF(f_plsd->datetime); 
         iMember++)
    {
        DRM_UINT64 ui64;
        FILETIME_TO_UI64(f_plsd->datetime [iMember], ui64 );
        FIX_ENDIAN_QWORD(ui64);
        UI64_TO_FILETIME( ui64, f_plsd->datetime [iMember] );
    }

    for (iMember = 0; 
         iMember < NO_OF(f_plsd->dwCount); 
         iMember++)
    {
        FIX_ENDIAN_DWORD(f_plsd->dwCount [iMember]);
    }
                             
ErrorExit:
    if (fSlotOpen)
    {
        dr = DRM_HDS_CloseSlot (&pcontextSync->contextHDSSlot);
    }

    return dr;
} /* _GetKIDStoreData */

DRM_RESULT _SetKIDStoreData(
    IN       DRM_SYNC_CONTEXT       *f_pcontextSYN,
    IN const DRM_KID                *f_pkid,
    IN       DRM_HDS_SLOT_HINT      *f_pslotHint,
    IN const DRM_LICENSE_STATE_DATA *f_plsd,
    IN       DRM_BOOL                f_fCreateIfNotExisting )
{
    DRM_SYNC_CONTEXT_PRIVATE *pcontextSync = (DRM_SYNC_CONTEXT_PRIVATE *) f_pcontextSYN;
    DRM_LICENSE_STATE_DATA    lsd = { 0 };
    DRM_DWORD  cbSlot    = SIZEOF (DRM_LICENSE_STATE_DATA);
    DRM_RESULT dr        = DRM_SUCCESS;
    DRM_BOOL   fSlotOpen = FALSE;    
    DRM_WORD   iMember   = 0;
    
    MEMCPY( &lsd, f_plsd, SIZEOF( lsd ) );

    FIX_ENDIAN_DWORD(lsd.dwCategory);                        
    FIX_ENDIAN_DWORD(lsd.dwStreamId);
    FIX_ENDIAN_DWORD(lsd.dwNumCounts);
    FIX_ENDIAN_DWORD(lsd.dwNumDates); 
    FIX_ENDIAN_DWORD(lsd.dwVague);    
    
    for (iMember = 0; 
         iMember < NO_OF(lsd.datetime); 
         iMember++)
    {
        DRM_UINT64 ui64;
        FILETIME_TO_UI64(lsd.datetime [iMember], ui64 );
        FIX_ENDIAN_QWORD(ui64);
        UI64_TO_FILETIME( ui64, lsd.datetime [iMember] );
    }

    for (iMember = 0; 
         iMember < NO_OF(lsd.dwCount); 
         iMember++)
    {
        FIX_ENDIAN_DWORD(lsd.dwCount [iMember]);
    }

    /* check to see if the entry already exists */
    dr = DRM_HDS_OpenSlot (&pcontextSync->contextNameSpace, 
                           eDRM_HDS_OPEN_EXISTING | eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT, 
                           f_pkid,
                           f_pkid,
                           f_pslotHint,
                          &cbSlot,
                          &pcontextSync->contextHDSSlot);

    /* doesn't exist; create it */
    if (dr == DRM_E_HDSSLOTNOTFOUND 
     && f_fCreateIfNotExisting )
    {
        ChkDR (DRM_HDS_OpenSlot (&pcontextSync->contextNameSpace, 
                                 eDRM_HDS_CREATE_IF_NEW | eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT, 
                                 f_pkid,
                                 f_pkid,
                                 NULL,
                                &cbSlot,
                                &pcontextSync->contextHDSSlot));
    }
    else
    {
        ChkDR (dr);
    }


    fSlotOpen = TRUE;

    ChkDR (DRM_HDS_SlotWrite (&pcontextSync->contextHDSSlot, 
                       SIZEOF (DRM_LICENSE_STATE_DATA), 
                (DRM_BYTE *)   &lsd, 
                              &cbSlot));
#if !_DATASTORE_WRITE_THRU
    ChkDR (DRM_HDS_CommitNamespace(&pcontextSync->contextNameSpace));
#endif

ErrorExit:
    if (fSlotOpen)
    {
        dr = DRM_HDS_CloseSlot (&pcontextSync->contextHDSSlot);
    }

    return dr;
} /* _SetKIDStoreData */

/*********************************************************************
** Function: DRM_SNC_UpdateKID
**
** Synopsis: force the updating of the license state data of a KID
**           entry in the sync store.  
**         * if the pcontextViewRightsIn parameter is not NULL, the
**           license store is read for the data
**         * if the pcontextViewRightsIn parameter is NULL, the data
**           is cleared.  This typically follows the deletion of a 
**           license
**
** Parameters:
**
** [f_pcontextSYN]          -- initialized SYNC context
** [f_pcontextViewRightsIn] -- optional VIEWRIGHTS context (see above)
** [f_pkid]                 -- pointer to KID to be added/updated
**********************************************************************/

DRM_RESULT DRM_API DRM_SNC_UpdateKID(
    IN       DRM_SYNC_CONTEXT        *f_pcontextSYN, 
    IN       DRM_VIEW_RIGHTS_CONTEXT *f_pcontextViewRightsIn,
    IN const DRM_KID                 *f_pkid)
{
    DRM_LICENSE_STATE_DATA    lsd = { 0 };
    DRM_SYNC_CONTEXT_PRIVATE *pcontextSync = (DRM_SYNC_CONTEXT_PRIVATE *) f_pcontextSYN;
    DRM_RESULT dr        = DRM_SUCCESS;
   
    ChkArg (f_pcontextSYN != NULL
         && f_pkid        != NULL);

    if (f_pcontextViewRightsIn != NULL)
    {
        MEMCPY(&f_pcontextViewRightsIn->KID, f_pkid, SIZEOF (DRM_KID));

        ChkDR( DRM_ASD_GetLicenseAggregateData( g_apdstrActions, 
                                                &lsd, 
                                                NO_OF( g_apdstrActions ), 
                                                f_pcontextViewRightsIn, 
                                                pcontextSync->pcontextHDSStorage,
                                                TRUE,
                                                DRM_ASD_AGGREGATE_SIMPLE_AND_LEAF_LICENSES ) );

        /* if this is an UNLIMITED license delete it from the sync store 
        ** if present since it's now attached to an UNLIM license and will never 
        ** be synced 
        */
                                
        if (lsd.dwCategory == WM_DRM_LICENSE_STATE_UNLIM)
        {
            ChkDR(DRM_SNC_DeleteKID(f_pcontextSYN, f_pkid));
        }
        else
        {
            _SetKIDStoreData(f_pcontextSYN, f_pkid, NULL, &lsd, TRUE);
        }       
    }

    /* a call with a NULL f_pcontextViewRightsIn means that the KID should 
    ** be stored in the synclist and updated from the license store next time the sync list is generated
    */    

    else
    {
        lsd.dwCategory = WM_DRM_LICENSE_STATE_FORCE_SYNC;
        
        _SetKIDStoreData(f_pcontextSYN, f_pkid, NULL, &lsd, TRUE);
    }

ErrorExit:
    return dr;
} /* DRM_SNC_UpdateKID */

