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

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmcontextsizes.h>
#include <drmlicenseparser.h>
#include <drmlicstore.h>
#include <drmhds.h>


/*************************************************************************
** local constants
**************************************************************************/
static const DRM_WORD _wLicenseNumChildren=16;
static const DRM_HDS_NAMESPACE _oLicenseStoreNamespace = { TWO_BYTES('L', 'i'), TWO_BYTES('c', 'S'), TWO_BYTES('t', 'o'), TWO_BYTES('r', 'e') };

/*************************************************************************
** static functions
**************************************************************************/

/*
** Load the prioritiszed list
*/
static DRM_RESULT _LoadPrioritizedList(
    _LicEnumContext *pEnum)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD i=0;
    DRM_HDS_SLOT_CONTEXT slotContextTemp;

    ChkArg(pEnum != NULL);
    pEnum->List.oPrioritized.nNumLicenses  = 0;
    pEnum->List.oPrioritized.nNextLicIndex = 0;
    while (TRUE)
    {
        DRM_BOOL fInserted=FALSE;
        DRM_DWORD iList = MAX_PRILIC_LIST;
        DRM_HDS_SLOT_CONTEXT *pslotContext = &pEnum->oSlotContext;


        if( pEnum->List.oPrioritized.nNumLicenses == 0 )
        {
            pslotContext = &slotContextTemp;
            iList = 0;
        }

        dr = DRM_HDS_SlotEnumNext(&pEnum->oHdsEnumContext,
                                pslotContext,
                                &pEnum->List.oPrioritized.oKID,
                                &pEnum->List.oPrioritized.pPrioritizedList[iList].oLID,
                                NULL);

        if ( dr == DRM_E_NOMORE )
        {
            if( pEnum->List.oPrioritized.nNumLicenses == 1 )
            {
                ChkDR(DRM_HDS_CloseSlot(&slotContextTemp));
            }
            
            dr = DRM_SUCCESS;
            break;
        }
        ChkDR(dr);

        ChkDR( DRM_HDS_MakeSlotHint( pslotContext, &(pEnum->List.oPrioritized.pPrioritizedList[iList].slotHint) ) );

        if( pEnum->List.oPrioritized.nNumLicenses == 0 )
        {
            /* In an attempt to optimize the case where we only have 1 license and save reading the
               priority from the slot we will defer the slot read until we have 2 licenses */
            pEnum->List.oPrioritized.nNumLicenses++;
            continue;
        }
        else if( pEnum->List.oPrioritized.nNumLicenses == 1 )
        {
            /* There are more than 1 license.  Read the first license's priority and add it to the list
               and continue with regular processing */
            ChkDR(DRM_HDS_SlotRead(&slotContextTemp,
                                   SIZEOF(DRM_DWORD),
                                   (DRM_BYTE*)&(pEnum->List.oPrioritized.pPrioritizedList[0].dwPriority),
                                   NULL));
            FIX_ENDIAN_DWORD( pEnum->List.oPrioritized.pPrioritizedList[0].dwPriority );
            ChkDR(DRM_HDS_CloseSlot(&slotContextTemp));
        }

        /* load the license's priority */
        ChkDR(DRM_HDS_SlotRead(&pEnum->oSlotContext,
                               SIZEOF(DRM_DWORD),
                               (DRM_BYTE*)&(pEnum->List.oPrioritized.pPrioritizedList[iList].dwPriority),
                               NULL));
        FIX_ENDIAN_DWORD( pEnum->List.oPrioritized.pPrioritizedList[iList].dwPriority );
        ChkDR(DRM_HDS_CloseSlot(&pEnum->oSlotContext));

        /* Merge this license to the list */ 
        for (i=0; i<pEnum->List.oPrioritized.nNumLicenses; i++)
        {
            DRM_DWORD j=0;
            if ( pEnum->List.oPrioritized.pPrioritizedList[MAX_PRILIC_LIST].dwPriority > pEnum->List.oPrioritized.pPrioritizedList[i].dwPriority )
            {
                /* bump the licenses from i to rest and insert the new license to pos i */
                j = (pEnum->List.oPrioritized.nNumLicenses==MAX_PRILIC_LIST)? 
                    pEnum->List.oPrioritized.nNumLicenses-1 : pEnum->List.oPrioritized.nNumLicenses;
                
                for (; j>i; j--)
                {
                    MEMCPY(&pEnum->List.oPrioritized.pPrioritizedList[j], 
                           &pEnum->List.oPrioritized.pPrioritizedList[j-1], 
                           SIZEOF(_PrioritizedLicense));
                }

                MEMCPY(&pEnum->List.oPrioritized.pPrioritizedList[i], 
                       &pEnum->List.oPrioritized.pPrioritizedList[MAX_PRILIC_LIST], 
                       SIZEOF(_PrioritizedLicense));
                
                if ( pEnum->List.oPrioritized.nNumLicenses < MAX_PRILIC_LIST )
                {
                    ++pEnum->List.oPrioritized.nNumLicenses;
                }

                fInserted = TRUE;
                break;
            }
        }

        if ( !fInserted && pEnum->List.oPrioritized.nNumLicenses < MAX_PRILIC_LIST )
        {
            MEMCPY(&pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNumLicenses],
                   &pEnum->List.oPrioritized.pPrioritizedList[MAX_PRILIC_LIST], 
                   SIZEOF(_PrioritizedLicense));

            ++pEnum->List.oPrioritized.nNumLicenses;
        }
    }
    
ErrorExit:
    return dr;
}

/**************************************************************************
** API functions
* *************************************************************************/


DRM_RESULT DRM_API DRM_LST_Open( 
    OUT DRM_LICSTORE_CONTEXT *pbLicContext,
    IN  DRM_HDS_CONTEXT      *poHdsContext )
{
    DRM_RESULT dr=DRM_SUCCESS;
    _LicContext *pLS=(_LicContext*)pbLicContext;
    
    ChkArg(pbLicContext != NULL);
    DRMSIZEASSERT( SIZEOF( _LicContext ), SIZEOF( DRM_LICSTORE_CONTEXT ) );

    /* open the namespace for the LicenseStore */
    ChkDR( DRM_HDS_OpenNamespace( poHdsContext, 
                                  &_oLicenseStoreNamespace, 
                                  eDRM_HDS_CREATE_IF_NEW | eDRM_HDS_LOCKWAIT,
                                  _wLicenseNumChildren,
                                  &pLS->oNamespaceContext ) );                                   
    pLS->fInited = TRUE;
    pLS->pDeviceStoreContext = poHdsContext;

ErrorExit:
   /* if ( DRM_FAILED(dr) && poHdsContext )
        DRM_UninitDeviceStore(); */

    return dr;
}


DRM_RESULT DRM_API DRM_LST_Close( 
    IN DRM_LICSTORE_CONTEXT *pbLicContext)
{
    DRM_RESULT dr=DRM_SUCCESS;
    _LicContext * pLS=(_LicContext *)pbLicContext;
    
    ChkArg(pbLicContext != NULL);

    if( pLS->fInited )
    {
        ChkDR(DRM_HDS_CloseNamespace(&pLS->oNamespaceContext));
    }

ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_LST_AddLicense(
    IN DRM_LICSTORE_CONTEXT *pbLicContext,
    IN DRM_DWORD             cbLicense,
    IN DRM_BYTE             *pbLicense,
    IN DRM_KID              *pkid,
    IN DRM_LID              *plid,
    IN DRM_DWORD             dwPriority )
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_CONST_STRING dstrLicense = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrValue   = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrLIData  = EMPTY_DRM_STRING;
    _LicContext * pLS=(_LicContext *)pbLicContext;
    DRM_HDS_HASHKEY oKID; 
    DRM_HDS_UNIQUEKEY oLID;
    DRM_DWORD nPriority=0;
    DRM_DWORD dwSize = 0;
    DRM_BOOL fCompatWrite = FALSE;

    ChkArg( pbLicContext != NULL
         && pbLicense    != NULL
         && cbLicense    !=0 
         && (cbLicense    < cbLicense+sizeof(DRM_DWORD)) /* Test for wrap around */
         && pLS->fInited );

    nPriority = dwPriority;
    if( pkid == NULL
     || plid == NULL )
    {
        /* To support older clients that just pass in a license buffer with no 
           preparsed data we can extract it.  The preferred method is for the caller
           to pass in this data */
        fCompatWrite = TRUE;
        DSTR_FROM_PB( &dstrLicense, pbLicense, cbLicense );
        ChkDR( DRM_LIC_GetAttribute( &dstrLicense, NULL, DRM_LICENSE_ATTRIB_KID, &dstrLIData, &dstrValue, 0 ) );
        ChkDR( DRM_UTL_DecodeKID( &dstrValue, &oKID ) );
        ChkDR( DRM_LIC_GetAttribute( &dstrLicense, NULL, DRM_LICENSE_ATTRIB_LID, &dstrLIData, &dstrValue, 0 ) );
        ChkDR( DRM_UTL_StringToGuid(&dstrValue, (DRM_GUID*)(oLID.rgb)) );
        ChkDR( DRM_LIC_GetAttribute( &dstrLicense, NULL, DRM_LICENSE_ATTRIB_PRIORITY, &dstrLIData, &dstrValue, 0 ) );    
        ChkDR( wcsntol( dstrValue.pwszString, dstrValue.cchString, (DRM_LONG *) &nPriority ) );
        pkid = &oKID;
        plid = &oLID;
    }
    
    /* allocate a slot in the license store */ 
    dwSize = cbLicense + SIZEOF(DRM_DWORD);
    ChkDR( DRM_HDS_OpenSlot(&pLS->oNamespaceContext, 
                            eDRM_HDS_CREATE_IF_NEW | eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT, 
                            pkid, 
                            plid, 
                            NULL, 
                            &dwSize, 
                            &pLS->oSlotContext) );

    FIX_ENDIAN_DWORD( nPriority );
    if( fCompatWrite )
    {
        ChkDR(DRM_HDS_SlotWrite(&pLS->oSlotContext, SIZEOF(DRM_DWORD), (DRM_BYTE *)&nPriority, NULL));
        ChkDR(DRM_HDS_SlotWrite(&pLS->oSlotContext, cbLicense,         pbLicense,              NULL));
    }
    else
    {
        MEMCPY( pbLicense, &nPriority, SIZEOF(DRM_DWORD) );
        ChkDR(DRM_HDS_SlotWrite(&pLS->oSlotContext, dwSize, pbLicense, NULL));
    }

    ChkDR(DRM_HDS_CloseSlot(&pLS->oSlotContext));

#if !_DATASTORE_WRITE_THRU
    ChkDR(DRM_HDS_CommitStore(pLS->pDeviceStoreContext));
#endif

ErrorExit:    
    return dr;
}


DRM_RESULT DRM_API DRM_LST_GetLicense( 
    IN     DRM_LICSTORE_CONTEXT *pbLicContext,
    IN     DRM_KID              *pKID,      /* key to use for the query */
    IN     DRM_LID              *pLID, 
    IN     DRM_HDS_SLOT_HINT    *pSlotHint,
       OUT DRM_BYTE             *pbLicense, 
    IN OUT DRM_DWORD            *pcbLicense)
{
    DRM_RESULT dr=DRM_SUCCESS;
    _LicContext * pLS=(_LicContext *)pbLicContext;
    DRM_DWORD cbSlotSize=0;
   
    ChkArg( pbLicContext != NULL
         && pcbLicense   != NULL
         && pKID         != NULL
         && pLID         != NULL );

    /* get size of SlotContext */
    ChkDR( DRM_HDS_OpenSlot(&pLS->oNamespaceContext, 
                            eDRM_HDS_OPEN_EXISTING | eDRM_HDS_LOCKWAIT, 
                            pKID, 
                            pLID, 
                            pSlotHint,
                            &cbSlotSize, 
                            &pLS->oSlotContext) );

    /* check size of license buffer */
    if ( *pcbLicense < (cbSlotSize-SIZEOF(DRM_DWORD) ) )
    {
        *pcbLicense = (cbSlotSize-SIZEOF(DRM_DWORD));
        ChkDR(DRM_HDS_CloseSlot(&pLS->oSlotContext));
        ChkDR(DRM_E_BUFFERTOOSMALL);
    }

    /* read the license */
    ChkDR(DRM_HDS_SlotSeek(&pLS->oSlotContext, SIZEOF(DRM_DWORD), eDRM_HDS_SEEKSET, NULL));
    ChkArg(pbLicense);
    ChkDR(DRM_HDS_SlotRead(&pLS->oSlotContext, cbSlotSize, pbLicense, pcbLicense));
    ChkDR(DRM_HDS_CloseSlot(&pLS->oSlotContext));

ErrorExit:    
    return dr;
}


DRM_RESULT DRM_API DRM_LST_InitEnum(
    IN  DRM_LICSTORE_CONTEXT     *pbLicContext, 
    IN  DRM_KID                  *pKID,           /* if NULL, all licenses will be enum'ed */
    IN  DRM_BOOL                  fPrioritized,   /* ignored if pKID is NULL */
    OUT DRM_LICSTOREENUM_CONTEXT *pLicEnumContext)
{
    DRM_RESULT dr=DRM_SUCCESS;  
    _LicContext     *pLS   = (_LicContext *)   pbLicContext;
    _LicEnumContext *pEnum = (_LicEnumContext*)pLicEnumContext;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_LST_InitEnum", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);

    ChkArg( pbLicContext    != NULL
         && pLicEnumContext != NULL
         && pLS->fInited );

    DRMSIZEASSERT(SIZEOF(DRM_LICSTOREENUM_CONTEXT), SIZEOF(_LicEnumContext));

    ZEROMEM(pLicEnumContext, SIZEOF(_LicEnumContext));
    dr = DRM_HDS_InitSlotEnum(
                &pLS->oNamespaceContext, 
                pKID, 
                eDRM_HDS_LOCKSHARED | eDRM_HDS_LOCKWAIT, 
                &pEnum->oHdsEnumContext);
    if( dr == DRM_E_HDSSLOTNOTFOUND )
    {
        /* No licenses found. */
        pEnum->eMode = eLicEnumNone;
        pEnum->List.oPrioritized.nNumLicenses = pEnum->List.oPrioritized.nNextLicIndex = 0;
        dr = DRM_SUCCESS;
    }
    else if( DRM_FAILED( dr ) )
    {
        goto ErrorExit;
    }
    else if ( pKID != NULL )
    {
        MEMCPY(pEnum->List.oNonPrioritized.oCurrKID.rgb, pKID->rgb, DRM_ID_SIZE);
        if ( fPrioritized )
        {
            MEMCPY(pEnum->List.oPrioritized.oKID.rgb, pKID->rgb, DRM_ID_SIZE);
            ChkDR(_LoadPrioritizedList(pEnum));
            pEnum->eMode = eLicEnumPrioritized;
        }
        else
        {
            pEnum->eMode = eLicEnumFiltered;
            pEnum->List.oNonPrioritized.fCurrIsValid = FALSE;
        }
    }
    else
    {
        pEnum->eMode = eLicEnumNatural;
        pEnum->List.oNonPrioritized.fCurrIsValid = FALSE;
    }
    pEnum->fInited = TRUE;
    pEnum->pLS = pLS;

ErrorExit:    

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_LST_InitEnum", g_pwszLeavingFunction);
    return dr;
}


/*
**
*/
DRM_RESULT DRM_API DRM_LST_EnumNext( 
    IN DRM_LICSTOREENUM_CONTEXT *pLicEnumContext,    /* setup by DRM_LST_InitEnum() */
    OUT DRM_KID                 *pKID,               /* if priortized or pKID is given, this is optional */
    OUT DRM_LID                 *pLID,
    OUT DRM_HDS_SLOT_HINT       *pSlotHint,
    OUT DRM_DWORD               *pcbLicense)         /* License size */
{
    DRM_RESULT dr=DRM_SUCCESS;
    _LicEnumContext *pEnum = (_LicEnumContext*)pLicEnumContext;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_LST_EnumNext", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);

    ChkArg( pLicEnumContext != NULL
         && pLID            != NULL
         && pEnum->fInited           );

    DRMSIZEASSERT(SIZEOF(DRM_LICSTOREENUM_CONTEXT), SIZEOF(_LicEnumContext));

    if ( pEnum->eMode == eLicEnumNone )
    {
        ChkDR( DRM_E_NOMORE );
    }

    if ( pcbLicense != NULL )
    {
        *pcbLicense = 0;
    }

    if ( pEnum->eMode == eLicEnumPrioritized )
    {
        if ( pEnum->List.oPrioritized.nNextLicIndex == pEnum->List.oPrioritized.nNumLicenses ) /* end of list reached */
        {
            /* end of prioritized list. start to enum in non-prioritized way if we had filled our priority list */
            if ( pEnum->List.oPrioritized.nNumLicenses == MAX_PRILIC_LIST )
            {
                pEnum->eMode = eLicEnumPostPrioritized;
                dr = DRM_HDS_InitSlotEnum(
                            &pEnum->pLS->oNamespaceContext, 
                            &pEnum->List.oNonPrioritized.oCurrKID, 
                            eDRM_HDS_LOCKSHARED | eDRM_HDS_LOCKWAIT, 
                            &pEnum->oHdsEnumContext);
            }
            else
            {
                ChkDR( DRM_E_NOMORE );
            }
        }
        else
        {
	        /* get license size */
            if ( pcbLicense != NULL )
            {
	            dr = DRM_LST_GetLicense( (DRM_LICSTORE_CONTEXT *)pEnum->pLS, 
	                                    &pEnum->List.oPrioritized.oKID,
	                                    &pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNextLicIndex].oLID, 
	                                    &pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNextLicIndex].slotHint,
                                        NULL, 
	                                    pcbLicense);

                if( DRM_FAILED( dr ) && dr != DRM_E_BUFFERTOOSMALL )
	            {
	                goto ErrorExit;
	            }
                dr = DRM_SUCCESS;
            }

	        if ( pKID != NULL )
	        {
	            MEMCPY(pKID->rgb, pEnum->List.oPrioritized.oKID.rgb, DRM_ID_SIZE);
	        }

            if ( pSlotHint != NULL )
            {
	            MEMCPY(pSlotHint, &(pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNextLicIndex].slotHint), DRM_HDS_SLOT_HINT_LEN);
            }
            
	        MEMCPY(pLID->rgb, 
	               pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNextLicIndex].oLID.rgb, 
	               DRM_ID_SIZE);
	        pEnum->List.oPrioritized.nNextLicIndex++;
	    }
    }

    /* not in prioritize list or the prioritize is exhausted */
    if ( pEnum->eMode != eLicEnumPrioritized )  /* eLicEnumFiltered or eLicEnumNatural */
    {
        DRM_DWORD dwSlot=0;

        pEnum->List.oNonPrioritized.fCurrIsValid = FALSE;
        if ( pEnum->eMode == eLicEnumNatural )
        {
            ChkArg(pKID != NULL);
        }
        
        while (TRUE)
        {
            DRM_BOOL  fSkip = FALSE;
            DRM_DWORD i     = 0;
            
	        dr = DRM_HDS_SlotEnumNext(&pEnum->oHdsEnumContext, 
	                                  &pEnum->oSlotContext, 
	                                  &pEnum->List.oNonPrioritized.oCurrKID, 
	                                  &pEnum->List.oNonPrioritized.oCurrLID, 
	                                  &dwSlot);
	        if ( dr == DRM_E_NOMORE )
	        {
	            goto ErrorExit;
	        }
	        else if ( dr == DRM_E_HDSBLOCKMISMATCH || dr == DRM_E_HDSSLOTNOTFOUND )
	        {
	            ChkDR(DRM_E_LICENSENOTFOUND);   /* license has been changed or removed */
	        }
	        ChkDR(dr);

	        MEMCPY(pLID->rgb, pEnum->List.oNonPrioritized.oCurrLID.rgb, DRM_ID_SIZE);
	        if ( pKID != NULL )     /* optional if in filtered mode */
	        {
	            MEMCPY(pKID->rgb, pEnum->List.oNonPrioritized.oCurrKID.rgb, DRM_ID_SIZE);
	        }

	        pEnum->List.oNonPrioritized.fCurrIsValid = TRUE;

            if ( pcbLicense != NULL )
            {
	            *pcbLicense = dwSlot;
            }

            if ( pSlotHint != NULL )
            {
                DRM_HDS_MakeSlotHint(&pEnum->oSlotContext, pSlotHint);
            }

	        ChkDR(DRM_HDS_CloseSlot(&pEnum->oSlotContext));

            if ( pEnum->eMode != eLicEnumPostPrioritized )
            {
                break;
            }

            fSkip = FALSE;
            /* check if the LID is in the priority list, if so, skip it */
            for (i=0; i<pEnum->List.oPrioritized.nNumLicenses; i++)
            {
                if ( MEMCMP(&pEnum->List.oPrioritized.pPrioritizedList[i].oLID, pLID, DRM_ID_SIZE) == 0 )
                {
                    fSkip = TRUE;
                    break;
                }
            }

            if ( !fSkip )
            {
                break;
            }
        }
    }
    
ErrorExit:

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_LST_EnumNext", g_pwszLeavingFunction);
    
    return dr;
}


/* delete current enumerated license */
DRM_RESULT DRM_API DRM_LST_EnumDelete( 
    IN DRM_LICSTOREENUM_CONTEXT *pLicEnumContext)
{
    DRM_RESULT dr = DRM_SUCCESS;
    _LicEnumContext *pEnum = (_LicEnumContext*)pLicEnumContext;

    ChkArg( pLicEnumContext != NULL
         && pEnum->fInited);

    DRMSIZEASSERT(SIZEOF(DRM_LICSTOREENUM_CONTEXT), SIZEOF(_LicEnumContext));

    if ( pEnum->eMode==eLicEnumNone )
    {
        dr = DRM_E_LICENSENOTFOUND;
        goto ErrorExit;            
    }

    else if ( pEnum->eMode==eLicEnumPrioritized )
    {
        /* no current exist */
        if ( pEnum->List.oPrioritized.nNextLicIndex==0    /* EnumNext not called yet */
         || pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNextLicIndex-1].fDelete == TRUE ) /* already deleted */
        {
            dr = DRM_E_LICENSENOTFOUND;
            goto ErrorExit;            
        }

        /* delete it */
        ChkDR(DRM_HDS_DeleteSlot(&pEnum->pLS->oNamespaceContext, 
                                 &pEnum->List.oPrioritized.oKID, 
                                 &pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNextLicIndex-1].oLID,
                                 &(pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNextLicIndex-1].slotHint),
                                 TRUE));

        pEnum->List.oPrioritized.pPrioritizedList[pEnum->List.oPrioritized.nNextLicIndex-1].fDelete = TRUE;
    }
    else /* eLicEnumFiltered or eLicEnumNatural */
    {
        if ( !pEnum->List.oNonPrioritized.fCurrIsValid )  /* EnumNext not called yet or current license already deleted */
        {
            dr = DRM_E_LICENSENOTFOUND;
            goto ErrorExit;            
        }
        else
        {
            ChkDR(DRM_HDS_DeleteSlot(&pEnum->pLS->oNamespaceContext,
                                     &pEnum->List.oNonPrioritized.oCurrKID, 
                                     &pEnum->List.oNonPrioritized.oCurrLID,
                                     NULL,
                                     TRUE));
            pEnum->List.oNonPrioritized.fCurrIsValid = FALSE;
        }
    }

#if !_DATASTORE_WRITE_THRU
    ChkDR(DRM_HDS_CommitStore(pEnum->pLS->pDeviceStoreContext));
#endif

ErrorExit:
    
    return dr;
}


DRM_RESULT DRM_API DRM_LST_DeleteLicense(
    DRM_LICSTORE_CONTEXT *pcontextLS,
    DRM_KID              *pkid,
    DRM_LID              *plid, 
    DRM_HDS_SLOT_HINT    *pslotHint )
{
    DRM_RESULT   dr   = DRM_SUCCESS;
    _LicContext *pLS  = (_LicContext *) pcontextLS;

    ChkArg( pLS  != NULL
         && pLS->fInited
         && pkid != NULL
         && plid != NULL );

    ChkDR( DRM_HDS_DeleteSlot(&pLS->oNamespaceContext,
                             pkid, 
                             plid,
                             pslotHint,
                             FALSE ));

ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_LST_LicCount( 
    IN  DRM_LICSTOREENUM_CONTEXT *pLicEnumCtxt, 
    OUT DRM_DWORD                *pcLicenses )
{
    DRM_RESULT          dr = DRM_SUCCESS;
    DRM_DWORD           cLicenses = 0;
    _LicEnumContext*    pEnum = (_LicEnumContext*)pLicEnumCtxt;

    /*  Check input */
    ChkArg( pLicEnumCtxt != NULL
         && pcLicenses   != NULL
         && pEnum->fInited       );

    /*  Clear output    */
    *pcLicenses = 0;
    
    DRMSIZEASSERT( SIZEOF(DRM_LICSTOREENUM_CONTEXT),  SIZEOF(_LicEnumContext) );
    
    if ( pEnum->eMode == eLicEnumNone )
    {
        /*  No licenses in store    */
        goto ErrorExit;            
    }
            
    while( TRUE )
    {
        /*  Look for next available license */
        ChkDR(DRM_HDS_SlotEnumNext( &pEnum->oHdsEnumContext, 
                                    &pEnum->oSlotContext, 
                                    &pEnum->List.oNonPrioritized.oCurrKID, 
                                    &pEnum->List.oNonPrioritized.oCurrLID, 
                                    NULL ));

        /*  Increment the license count */
        cLicenses++;

        /*  Close the slot  */
        ChkDR(DRM_HDS_CloseSlot(&pEnum->oSlotContext));
        
    }        
    
ErrorExit:
    if (DRM_E_NOMORE == dr)
    {
        dr = DRM_SUCCESS;
        *pcLicenses = cLicenses;
    }
    return dr;
}





