/*++                 

Copyright (c) 1997-1999 Microsoft Corporation

Module Name:

    main.c

Abstract:
    
    Implements WMI GUID handle management code

Author:

    16-Jan-1997 AlanWar

Revision History:

--*/

#include "wmiump.h"
#include <rpc.h>

BOOLEAN WmipIsNumber(
    LPCWSTR String
    )
{
    while (*String != UNICODE_NULL)
    {
        if ((*String < L'0') || 
            (*String++ > L'9'))
        {
            return(FALSE);
        }
    }
    return(TRUE);
}


#define WmipWStringSizeInBytes(string) \
    ( ( (wcslen((string)) + 1) * sizeof(WCHAR) ) )


ULONG WmipBaseCookieIndex;
LIST_ENTRY WmipCookieHead = {&WmipCookieHead, &WmipCookieHead};


PNOTIFYCOOKIE WmipFindCookieByGuid(
    LPGUID Guid,
    PNOTIFYCOOKIECHUNK *Chunk
    )
/*+++

Routine Description:

    This routine assumes the PM critical section is held
        
Arguments:


Return Value:

---*/
{
    PLIST_ENTRY CookieList;
    PNOTIFYCOOKIECHUNK CookieChunk;
    PNOTIFYCOOKIE Cookie;
    ULONG i;
    
    CookieList = WmipCookieHead.Flink;
    while (CookieList != &WmipCookieHead)
    {
        CookieChunk = CONTAINING_RECORD(CookieList,
                                        NOTIFYCOOKIECHUNK,
                                        Next);

        for (i = 0; i < NOTIFYCOOKIESPERCHUNK; i++)
        {
            Cookie = &CookieChunk->Cookies[i];
            if ((Cookie->InUse) &&
                (IsEqualGUID(Guid, &Cookie->Guid)))
            {
                *Chunk = CookieChunk;
                return(Cookie);
            }
        }
        
        CookieList = CookieList->Flink;
    }
    return(NULL);
}

ULONG WmipAllocateCookie(
    PVOID DeliveryInfo,
    PVOID DeliveryContext,
    LPGUID Guid
    )
{
    PLIST_ENTRY CookieList;
    PNOTIFYCOOKIECHUNK CookieChunk;
    PNOTIFYCOOKIE Cookie;
    ULONG i;
    ULONG CheckSlot, FreeSlot;
    
    WmipEnterPMCritSection();

    while (1)
    {
        CookieList = WmipCookieHead.Flink;
        while (CookieList != &WmipCookieHead)
        {
            CookieChunk = CONTAINING_RECORD(CookieList,
                                            NOTIFYCOOKIECHUNK,
                                            Next);
            if (! CookieChunk->Full)
            {
                FreeSlot = CookieChunk->FreeSlot;
                for (i = 0; i < NOTIFYCOOKIESPERCHUNK; i++)
                {
                    CheckSlot = (FreeSlot + i) % NOTIFYCOOKIESPERCHUNK;
                    if (! CookieChunk->Cookies[CheckSlot].InUse)
                    {
                        //
                        // We found a free cookie slot
                        Cookie = &CookieChunk->Cookies[CheckSlot];
                        Cookie->InUse = TRUE;
                        CookieChunk->FreeSlot = (USHORT)((CheckSlot + 1) % NOTIFYCOOKIESPERCHUNK);
                        WmipLeavePMCritSection();
                        Cookie->DeliveryInfo = DeliveryInfo;
                        Cookie->DeliveryContext = DeliveryContext;
                        Cookie->Guid = *Guid;
                        return(CookieChunk->BaseSlot + CheckSlot + 1);
                    }
                }
            
                //
                // All slots were full so mark as such
                CookieChunk->Full = TRUE;
            }
            CookieList = CookieList->Flink;
        }
    
        //
        // No free cookie slots, allocate a new chunk
        CookieChunk = WmipAlloc(sizeof(NOTIFYCOOKIECHUNK));
        if (CookieChunk == NULL)
        {
            WmipLeavePMCritSection();
            return(0);
        }
        
        memset(CookieChunk, 0, sizeof(NOTIFYCOOKIECHUNK));
        CookieChunk->BaseSlot = WmipBaseCookieIndex;
        WmipBaseCookieIndex += NOTIFYCOOKIESPERCHUNK;
        InsertHeadList(&WmipCookieHead, &CookieChunk->Next);
    }
}

PNOTIFYCOOKIE WmipFindCookie(
    ULONG CookieSlot,
    LPGUID Guid,
    PNOTIFYCOOKIECHUNK *Chunk
    )
/*+++

Routine Description:

    This routine assumes the PM critical section is held
        
Arguments:


Return Value:

---*/
{
    PLIST_ENTRY CookieList;
    PNOTIFYCOOKIE Cookie;
    PNOTIFYCOOKIECHUNK CookieChunk;
    
    WmipAssert(CookieSlot != 0);
    
    CookieSlot--;
    
    CookieList = WmipCookieHead.Flink;
    while (CookieList != &WmipCookieHead)
    {
        CookieChunk = CONTAINING_RECORD(CookieList,
                                        NOTIFYCOOKIECHUNK,
                                        Next);
                                    
        if ((CookieSlot >= CookieChunk->BaseSlot) &&
            (CookieSlot < (CookieChunk->BaseSlot + NOTIFYCOOKIESPERCHUNK)))
        {
            Cookie = &CookieChunk->Cookies[CookieSlot - CookieChunk->BaseSlot];
            if (Guid != NULL)
            {
                if ((! Cookie->InUse) ||
                    (! IsEqualGUID(&Cookie->Guid, Guid)))
                {
                    Cookie = WmipFindCookieByGuid(Guid, &CookieChunk);
                }
            } else {
                if (! (Cookie->InUse) )
                    return NULL;
            }
            
            *Chunk = CookieChunk;
            return(Cookie);
        }
        
        CookieList = CookieList->Flink;
    }
    
    if (Guid != NULL)
    {
        Cookie = WmipFindCookieByGuid(Guid, &CookieChunk);
    } else {
        Cookie = NULL;
    }
    
    return(Cookie);
}

void WmipFreeCookie(
    ULONG CookieSlot
    )
{
    PNOTIFYCOOKIE Cookie;
    PNOTIFYCOOKIECHUNK CookieChunk;
    
    WmipEnterPMCritSection();
    Cookie = WmipFindCookie(CookieSlot, NULL, &CookieChunk);
    if (Cookie != NULL)
    {
        Cookie->InUse = FALSE;
        CookieChunk->Full = FALSE;
        CookieChunk->FreeSlot = (USHORT)(CookieSlot - CookieChunk->BaseSlot - 1);
    }
    WmipLeavePMCritSection();
}

void
WmipGetGuidInCookie(
    ULONG CookieSlot,
    LPGUID Guid
    )
{
    PNOTIFYCOOKIE Cookie;
    PNOTIFYCOOKIECHUNK CookieChunk;

    WmipEnterPMCritSection();
    Cookie = WmipFindCookie(CookieSlot, NULL, &CookieChunk);
    if (Cookie != NULL)
    {
        *Guid = Cookie->Guid;
    }
    WmipLeavePMCritSection();

    return;
}



BOOLEAN WmipLookupCookie(
    ULONG CookieSlot,
    LPGUID Guid,
    PVOID *DeliveryInfo,
    PVOID *DeliveryContext
    )
{
    PNOTIFYCOOKIE Cookie;
    PNOTIFYCOOKIECHUNK CookieChunk;    
    
    WmipEnterPMCritSection();
    Cookie = WmipFindCookie(CookieSlot, Guid, &CookieChunk);
    if (Cookie != NULL)
    {
        *DeliveryInfo = Cookie->DeliveryInfo;
        *DeliveryContext = Cookie->DeliveryContext;
    }
    WmipLeavePMCritSection();
    
    return(Cookie != NULL);
}


#if DBG
PCHAR GuidToStringA(
    PCHAR s,
    LPGUID piid
    )
{
    GUID XGuid = *piid;
    
    sprintf(s, "%x-%x-%x-%x%x%x%x%x%x%x%x",
               XGuid.Data1, XGuid.Data2, 
               XGuid.Data3,
               XGuid.Data4[0], XGuid.Data4[1],
               XGuid.Data4[2], XGuid.Data4[3],
               XGuid.Data4[4], XGuid.Data4[5],
               XGuid.Data4[6], XGuid.Data4[7]);

    return(s);
}
#endif