#ifndef __MAP_KV_H__
#define __MAP_KV_H__

/////////////////////////////////////////////////////////////////////////////
// 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:
   CMapKeyToValue(DWORD memctx, UINT cbValue, UINT cbKey = 0, 
        int nBlockSize=10, LPFNHASHKEY lpfnHashKey = &MKVDefaultHashKey,
        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;
   DWORD        m_memctx;
};


#endif // !__MAP_KV_H__
