#ifndef __MAP_KV_H__
#define __MAP_KV_H__

#include    <memapi.hxx>

/////////////////////////////////////////////////////////////////////////////
// class CMapKeyToValue - a mapping from 'KEY's to 'VALUE's, passed in as
// pv/cb pairs.  The keys can be variable length, although we optmizize the
// case when they are all the same.
//
/////////////////////////////////////////////////////////////////////////////

STDAPI_(UINT) MKVDefaultHashKey(LPVOID pKey, UINT cbKey);

#ifdef WIN32
DECLARE_HANDLE(HMAPKEY);
#else
DECLARE_HANDLE32(HMAPKEY);
#endif

typedef UINT (STDAPICALLTYPE FAR* LPFNHASHKEY)(LPVOID, UINT);

class FAR CMapKeyToValue : public CPrivAlloc
{
public:
        CMapKeyToValue(UINT cbValue, UINT cbKey = 0,
                int nBlockSize=10,
                LPFNHASHKEY lpfnHashKey = NULL,
                UINT nHashSize = 17);
        ~CMapKeyToValue();

        // number of elements
        int     GetCount() const { return m_nCount; }
        BOOL    IsEmpty() const { return m_nCount == 0; }

        // Lookup; return FALSE if not found
        BOOL    Lookup(LPVOID pKey, UINT cbKey, LPVOID pValue) const;
        BOOL    LookupHKey(HMAPKEY hKey, LPVOID pValue) const;
        BOOL    LookupAdd(LPVOID pKey, UINT cbKey, LPVOID pValue) const;

        // add a new (key, value) pair; return FALSE if out of memory
        BOOL    SetAt(LPVOID pKey, UINT cbKey, LPVOID pValue);
        BOOL    SetAtHKey(HMAPKEY hKey, LPVOID pValue);

        // removing existing (key, ?) pair; return FALSE if no such key
        BOOL    RemoveKey(LPVOID pKey, UINT cbKey);
        BOOL    RemoveHKey(HMAPKEY hKey);
        void    RemoveAll();

        // iterating all (key, value) pairs
        POSITION GetStartPosition() const
                        { return (m_nCount == 0) ? (POSITION)NULL : BEFORE_START_POSITION; }
        void    GetNextAssoc(POSITION FAR* pNextPosition, LPVOID pKey,
                                UINT FAR* pcbKey, LPVOID pValue) const;

        // return HMAPKEY for given key; returns NULL if not currently in map
        HMAPKEY GetHKey(LPVOID pKey, UINT cbKey) const;

        void    AssertValid() const;

private:
        // abstracts, somewhat, variable and fixed sized keys; size is really
        // m_cbKeyInAssoc.
        union CKeyWrap
        {
                BYTE rgbKey[sizeof(LPVOID) + sizeof(UINT)];
                struct
                {
                        LPVOID pKey;
                        UINT cbKey;
                };
        };

        // Association of one key and one value; NOTE: even though in general
        // the size of the key and value varies, for any given map,
        // the size of an assoc is fixed.
        struct CAssoc
        {
                CAssoc  FAR* pNext;
                UINT    nHashValue; // needed for efficient iteration
                CKeyWrap key;           // size is really m_cbKeyInAssoc
                // BYTE rgbValue[m_cbValue];
        };

        UINT    SizeAssoc() const
                { return sizeof(CAssoc)-sizeof(CKeyWrap) + m_cbKeyInAssoc + m_cbValue; }
        CAssoc  FAR* NewAssoc(UINT hash, LPVOID pKey, UINT cbKey, LPVOID pValue);
        void    FreeAssoc(CAssoc FAR* pAssoc);
        BOOL    CompareAssocKey(CAssoc FAR* pAssoc, LPVOID pKey, UINT cbKey) const;
        CAssoc  FAR* GetAssocAt(LPVOID pKey, UINT cbKey, UINT FAR& nHash) const;

        BOOL    SetAssocKey(CAssoc FAR* pAssoc, LPVOID pKey, UINT cbKey) const;
        void    GetAssocKeyPtr(CAssoc FAR* pAssoc, LPVOID FAR* ppKey,UINT FAR* pcbKey) const;
        void    FreeAssocKey(CAssoc FAR* pAssoc) const;
        void    GetAssocValuePtr(CAssoc FAR* pAssoc, LPVOID FAR* ppValue) const;
        void    GetAssocValue(CAssoc FAR* pAssoc, LPVOID pValue) const;
        void    SetAssocValue(CAssoc FAR* pAssoc, LPVOID pValue) const;

        BOOL    InitHashTable();

        UINT    m_cbValue;
        UINT    m_cbKey;                        // variable length if 0
        UINT    m_cbKeyInAssoc;         // always non-zero

        CAssoc  FAR* FAR* m_pHashTable;
        UINT    m_nHashTableSize;
        LPFNHASHKEY m_lpfnHashKey;

        int     m_nCount;
        CAssoc  FAR* m_pFreeList;
        struct CPlex FAR* m_pBlocks;
        int     m_nBlockSize;
};


#endif // !__MAP_KV_H__