/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
/*****************************************************************************
*
*   $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h
*   
***/

#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTHASH_H
#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h included more than once"
#endif
#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTHASH_H


/****************************************************************************
*
*   Macros
*
***/

// Define a field inside an object that is used to link it into a hash table
#define HASHLINK(object) THashLink< object >

// Define a POINTER to a hash table, not a hash table
#define HASHTABLE(object,key) THashTable< object, key >

// Define a hash table:
// - starts with kSlotMinCount rows
// - can grow to kDefaultSlotMaxCount rows
// (hash table grows when a row contains more than kGrowOnListSize entries
#define HASHTABLEDECL(object,key,link) THashTableDecl< object, key, offsetof(object,link), 0 >

// Define a hash table in situations when a forward reference prevents use of HASHTABLEDECL
// - Size characteristics are identical to HASHTABLEDECL
#define HASHTABLEDYN(object,key) THashTableDyn< object, key >

// Define a hash table with:
// - starts with <size>
// - row table never grows
#define HASHTABLEDECLSIZE(object,key,link,size) THashTableDecl<object, key, offsetof(object,link), size >


/****************************************************************************
*
*   Forward declarations
*
***/

template<class T>
class THashLink;

template<class T>
class TBaseHashTable;


/****************************************************************************
*
*   THashLink
*
***/

template<class T>
class THashLink {
    friend class TBaseHashTable<T>;

private:
    unsigned m_hash;
    LINK(T)  m_linkToFull;
    LINK(T)  m_linkToSlot;

public:
    inline bool IsLinked () const;
    inline T * Next ();
    inline const T * Next () const;
    inline T * Prev ();
    inline const T * Prev () const;
    inline void Unlink ();
};

//===========================================================================
template<class T>
bool THashLink<T>::IsLinked () const {
    return m_linkToFull.IsLinked();
}

//===========================================================================
template<class T>
T * THashLink<T>::Next () {
    return m_linkToFull.Next();
}

//===========================================================================
template<class T>
const T * THashLink<T>::Next () const {
    return m_linkToFull.Next();
}

//===========================================================================
template<class T>
T * THashLink<T>::Prev () {
    return m_linkToFull.Prev();
}

//===========================================================================
template<class T>
const T * THashLink<T>::Prev () const {
    return m_linkToFull.Prev();
}

//===========================================================================
template<class T>
void THashLink<T>::Unlink () {
    m_linkToFull.Unlink();
    m_linkToSlot.Unlink();
}


/****************************************************************************
*
*   TBaseHashTable
*
***/

template<class T>
class TBaseHashTable {

private:
    enum { kSlotMinCount = 8 };
    enum { kDefaultSlotMaxCount = 1024 };
    enum { kGrowOnListSize = 5 };

    LIST(T)            m_fullList;
    int                m_linkOffset;
    FARRAYOBJ(LIST(T)) m_slotListArray;
    unsigned           m_slotMask;  // always set to a power of two minus one
    unsigned           m_slotMaxCount;

    inline bool CheckGrowTable (LIST(T) * slotList);
    inline const THashLink<T> & GetLink (const T * object) const;
    inline THashLink<T> & GetLink (T * object);
    inline void SetSlotCount (unsigned count);

protected:
    inline unsigned GetHash (const T * object) const;
    inline unsigned & GetHash (T * object);
    inline const LIST(T) & GetSlotList (unsigned hash) const;
    inline LIST(T) & GetSlotList (unsigned hash);
    inline void SetLinkOffset (int linkOffset, unsigned maxSize);
    inline void SetSlotMaxCount (unsigned count);

public:
    inline TBaseHashTable ();
    inline TBaseHashTable (const TBaseHashTable<T> & source);
    inline TBaseHashTable<T> & operator= (const TBaseHashTable<T> & source);

    inline void Add (T * object, unsigned hash);
    inline void Clear ();
    inline void Delete (T * object);
    inline T * Head ();
    inline const T * Head () const;
    inline T * Next (const T * object);
    inline const T * Next (const T * object) const;
    inline void Order (T * linkedObject, ELinkType linkType, T * existingObject);
    inline T * Prev (const T * object);
    inline const T * Prev (const T * object) const;
    inline T * Tail ();
    inline const T * Tail () const;
    inline void Unlink (T * object);

};

//===========================================================================
template<class T>
TBaseHashTable<T>::TBaseHashTable () {
    m_slotMask     = 0;
    m_slotMaxCount = kDefaultSlotMaxCount;
    // more initialization done during call to SetLinkOffset()
}

//===========================================================================
template<class T>
TBaseHashTable<T>::TBaseHashTable (const TBaseHashTable<T> & source) {
#ifdef HS_DEBUGGING
    FATAL("No copy constructor");
#endif
    TBaseHashTable();
}

//===========================================================================
template<class T>
TBaseHashTable<T> & TBaseHashTable<T>::operator= (const TBaseHashTable<T> & source) {
#ifdef HS_DEBUGGING
    FATAL("No assignment operator");
#endif
    return *this;
}

//===========================================================================
template<class T>
void TBaseHashTable<T>::Add (T * object, unsigned hash) {
    GetHash(object) = hash;

    LIST(T) * list = &GetSlotList(hash);
    if (CheckGrowTable(list))
        list = &GetSlotList(hash);

    m_fullList.Link(object);
    list->Link(object);
}

//===========================================================================
template<class T>
bool TBaseHashTable<T>::CheckGrowTable (LIST(T) * list) {

    unsigned nextCount = (m_slotMask + 1) * 2;
    if (nextCount > m_slotMaxCount)
        return false;

    unsigned listCount = 0;
    for (T * curr = list->Head(); curr; curr = list->Next(curr))
        ++listCount;

    if (listCount + 1 < kGrowOnListSize)
        return false;

    SetSlotCount(nextCount);

    return true;
}

//===========================================================================
template<class T>
void TBaseHashTable<T>::Clear () {
    m_fullList.Clear();
}

//===========================================================================
template<class T>
void TBaseHashTable<T>::Delete (T * object) {
    DEL(object);
}

//===========================================================================
template<class T>
unsigned TBaseHashTable<T>::GetHash (const T * object) const {
    return GetLink(object).m_hash;
}

//===========================================================================
template<class T>
unsigned & TBaseHashTable<T>::GetHash (T * object) {
    return GetLink(object).m_hash;
}

//===========================================================================
template<class T>
const THashLink<T> & TBaseHashTable<T>::GetLink (const T * object) const {
    return *(const THashLink<T> *)((const byte *)object + m_linkOffset);
}

//===========================================================================
template<class T>
THashLink<T> & TBaseHashTable<T>::GetLink (T * object) {
    return *(THashLink<T> *)((byte *)object + m_linkOffset);
}

//===========================================================================
template<class T>
const LIST(T) & TBaseHashTable<T>::GetSlotList (unsigned hash) const {
    return m_slotListArray[hash & m_slotMask];
}

//===========================================================================
template<class T>
LIST(T) & TBaseHashTable<T>::GetSlotList (unsigned hash) {
    return m_slotListArray[hash & m_slotMask];
}

//===========================================================================
template<class T>
T * TBaseHashTable<T>::Head () {
    return m_fullList.Head();
}

//===========================================================================
template<class T>
const T * TBaseHashTable<T>::Head () const {
    return m_fullList.Head();
}

//===========================================================================
template<class T>
T * TBaseHashTable<T>::Next (const T * object) {
    return m_fullList.Next(object);
}

//===========================================================================
template<class T>
const T * TBaseHashTable<T>::Next (const T * object) const {
    return m_fullList.Next(object);
}

//===========================================================================
template<class T>
void TBaseHashTable<T>::Order (T * linkedObject, ELinkType linkType, T * existingObject) {
    THashLink<T> & link = GetLink(linkedObject);
    ASSERT(link.m_linkToFull.IsLinked());
    m_fullList.Link(linkedObject, linkType, existingObject);
}

//===========================================================================
template<class T>
T * TBaseHashTable<T>::Prev (const T * object) {
    return m_fullList.Prev(object);
}

//===========================================================================
template<class T>
const T * TBaseHashTable<T>::Prev (const T * object) const {
    return m_fullList.Prev(object);
}

//===========================================================================
template<class T>
void TBaseHashTable<T>::SetLinkOffset (int linkOffset, unsigned maxSize) {
    ASSERT(!m_fullList.Head());
    ASSERT(!m_slotListArray.Count());
    ASSERT(!m_slotMask);

    m_linkOffset = linkOffset;
    m_fullList.SetLinkOffset(m_linkOffset + offsetof(THashLink<T>, m_linkToFull));

    if (!m_slotMask)
        SetSlotCount(max(kSlotMinCount, MathNextPow2(maxSize)));
}

//===========================================================================
template<class T>
void TBaseHashTable<T>::SetSlotCount (unsigned count) {
    ASSERT(!(count & (count - 1)));  // power of two
    ASSERT(count >= 2);
    
    if (count == m_slotMask + 1)
        return;
    m_slotMask = count - 1;

    m_slotListArray.ZeroCount();
    m_slotListArray.SetCount(count);
    for (unsigned loop = 0; loop < count; ++loop)
        m_slotListArray[loop].SetLinkOffset(m_linkOffset + offsetof(THashLink<T>, m_linkToSlot));

    for (T * curr = Head(); curr; curr = Next(curr))
        GetSlotList(GetHash(curr)).Link(curr);           
}

//===========================================================================
template<class T>
void TBaseHashTable<T>::SetSlotMaxCount (unsigned count) {
    if (count)
        m_slotMaxCount = max(kSlotMinCount, count);
}

//===========================================================================
template<class T>
T * TBaseHashTable<T>::Tail () {
    return m_fullList.Tail();
}

//===========================================================================
template<class T>
const T * TBaseHashTable<T>::Tail () const {
    return m_fullList.Tail();
}

//===========================================================================
template<class T>
void TBaseHashTable<T>::Unlink (T * object) {
    THashLink<T> & link = GetLink(object);
    link.Unlink();
}


/****************************************************************************
*
*   THashTable
*
***/

template<class T, class K>
class THashTable : public TBaseHashTable<T> {

public:
    inline void Add (T * object);
    inline void Add (T * object, unsigned hash);
    inline T * Find (const K & key);
    inline T * FindNext (const K & key, T * object);
    inline const T * Find (const K & key) const;
    inline const T * FindNext (const K & key, const T * object) const;
    inline T * Unduplicate (T * object, const K & key);
    
};

//===========================================================================
template<class T, class K>
inline void THashTable<T,K>::Add (T * object) {
    TBaseHashTable<T>::Add(object, object->GetHash());
}

//===========================================================================
template<class T, class K>
inline void THashTable<T,K>::Add (T * object, unsigned hash) {
    TBaseHashTable<T>::Add(object, hash);
}

//===========================================================================
template<class T, class K>
T * THashTable<T,K>::Find (const K & key) {
    return (T *)((const THashTable<T,K> *)this)->Find(key);
}

//===========================================================================
template<class T, class K>
T * THashTable<T,K>::FindNext (const K & key, T * object) {
    return (T *)((const THashTable<T,K> *)this)->FindNext(key, object);
}

//===========================================================================
template<class T, class K>
const T * THashTable<T,K>::Find (const K & key) const {
    unsigned        hash     = key.GetHash();
    const LIST(T) & slotList = GetSlotList(hash);
    for (const T * curr = slotList.Head(); curr; curr = slotList.Next(curr))
        if ((GetHash(curr) == hash) && (*curr == key))
            return curr;
    return nil;
}

//===========================================================================
template<class T, class K>
const T * THashTable<T,K>::FindNext (const K & key, const T * object) const {
    unsigned        hash     = key.GetHash();
    const LIST(T) & slotList = GetSlotList(hash);
    for (const T * curr = slotList.Next(object); curr; curr = slotList.Next(curr))
        if ((GetHash(curr) == hash) && (*curr == key))
            return curr;
    return nil;
}

//===========================================================================
template<class T, class K>
T * THashTable<T,K>::Unduplicate (T * object, const K & key) {
    T * existing = Find(key);
    if (existing) {
        DEL(object);
        return existing;
    }
    else {
        Add(object);
        return object;
    }
}


/****************************************************************************
*
*   THashTableDecl
*
***/

template<class T, class K, int linkOffset, unsigned maxSize>
class THashTableDecl : public THashTable<T,K> {

public:
    inline THashTableDecl ();

};

//===========================================================================
template<class T, class K, int linkOffset, unsigned maxSize>
THashTableDecl<T,K,linkOffset,maxSize>::THashTableDecl () {
    SetLinkOffset(linkOffset, maxSize);
    SetSlotMaxCount(maxSize);
}


/****************************************************************************
*
*   THashTableDyn
*
***/

template<class T, class K>
class THashTableDyn : public THashTable<T,K> {

public:
    void Initialize (int linkOffset, unsigned maxSize = 0);

};

//===========================================================================
template<class T, class K>
void THashTableDyn<T,K>::Initialize (int linkOffset, unsigned maxSize) {
    SetLinkOffset(linkOffset, maxSize);
    SetSlotMaxCount(maxSize);
}


/****************************************************************************
*
*   THashKeyVal
*
***/

template <class T>
class THashKeyVal {
public:
    THashKeyVal () : m_value(0) { }
    THashKeyVal (const T & value) : m_value(value) { }
    bool operator== (const THashKeyVal & rhs) const {
        return m_value == rhs.m_value;
    }
    unsigned GetHash () const {
        CHashValue hash(&m_value, sizeof(m_value));
        return hash.GetHash();
    }
    const T & GetValue () const { return m_value; }
    void SetValue (const T & value) { m_value = value; }

protected:
    T m_value;
};


/****************************************************************************
*
*   CHashKeyStrPtr / CHashKeyStrPtrI
*
***/

//===========================================================================
template<class C>
class THashKeyStrBase {
public:
    const C * GetString () const {
        return m_str;
    }

protected:
    THashKeyStrBase () : m_str(nil) { }
    THashKeyStrBase (const C str[]) : m_str(str) { }
    virtual ~THashKeyStrBase () { }

    const C * m_str;
};

//===========================================================================
template<class C>
class THashKeyStrCmp : public THashKeyStrBase<C> {
public:
    bool operator== (const THashKeyStrCmp & rhs) const {
        return StrCmp(m_str, rhs.m_str) == 0;
    }
    unsigned GetHash () const {
        return StrHash(m_str);
    }

protected:
    THashKeyStrCmp () { }
    THashKeyStrCmp (const C str[]) : THashKeyStrBase<C>(str) { }
};

//===========================================================================
template<class C>
class THashKeyStrCmpI : public THashKeyStrBase<C> {
public:
    bool operator== (const THashKeyStrCmpI & rhs) const {
        return StrCmpI(m_str, rhs.m_str) == 0;
    }
    unsigned GetHash () const {
        return StrHashI(m_str);
    }
protected:

    THashKeyStrCmpI () { }
    THashKeyStrCmpI (const C str[]) : THashKeyStrBase<C>(str) { }
};


/****************************************************************************
*
*   THashKeyStrPtr
*
***/

template <class C, class T>
class THashKeyStrPtr : public T {
public:
    THashKeyStrPtr () { }
    THashKeyStrPtr (const C str[]) : T(str) { }
    void SetString (const C str[]) {
        m_str = str;
    }
};

typedef THashKeyStrPtr< wchar, THashKeyStrCmp<wchar> >  CHashKeyStrPtr;
typedef THashKeyStrPtr< wchar, THashKeyStrCmpI<wchar> > CHashKeyStrPtrI;
typedef THashKeyStrPtr< char, THashKeyStrCmp<char> >    CHashKeyStrPtrChar;
typedef THashKeyStrPtr< char, THashKeyStrCmpI<char> >   CHashKeyStrPtrCharI;


/****************************************************************************
*
*   THashKeyStr
*
***/

template <class C, class T>
class THashKeyStr : public T {
public:
    THashKeyStr () { }
    THashKeyStr (const C str[]) { SetString(str); }
    THashKeyStr (const THashKeyStr &);              // intentionally unimplemented
    THashKeyStr & operator= (const THashKeyStr &);  // intentionally unimplemented
    ~THashKeyStr () {
        SetString(nil);
    }
    void SetString (const C str[]) {  // deprecated
        if (m_str)
            FREE(const_cast<C *>(m_str));
        if (str)
            m_str = StrDup(str);
        else
            m_str = nil;
    }
};

typedef THashKeyStr< wchar, THashKeyStrCmp<wchar> >  CHashKeyStr;
typedef THashKeyStr< wchar, THashKeyStrCmpI<wchar> > CHashKeyStrI;
typedef THashKeyStr< char, THashKeyStrCmp<char> >    CHashKeyStrChar;
typedef THashKeyStr< char, THashKeyStrCmpI<char> >   CHashKeyStrCharI;


/****************************************************************************
*
*   CHashValue
*
***/

class CHashValue {
private:
    static const dword s_hashTable[];

    dword m_result;

    inline void Construct () { m_result = 0x325d1eae; }

public:
    static dword LookupHashBits (unsigned value) { ASSERT(value < 0x100); return s_hashTable[value]; }

    inline CHashValue () { Construct() ; }
    inline CHashValue (const CHashValue & source) { m_result = source.m_result; }
    inline CHashValue (const void * data, unsigned bytes) { Construct(); Hash(data, bytes); }
    inline CHashValue & operator= (const CHashValue & source) { m_result = source.m_result; return *this; }
    inline bool operator== (const CHashValue & source) const { return (m_result == source.m_result); }

    inline dword GetHash () const { return m_result; }

    __forceinline void Hash   (const void * data, unsigned bytes);
    __forceinline void Hash8  (unsigned data);
    __forceinline void Hash16 (unsigned data);
    __forceinline void Hash32 (unsigned data);

};

//===========================================================================
void CHashValue::Hash (const void * data, unsigned bytes) {
    for (const byte * curr = (const byte *)data, * term = curr + bytes; curr != term; ++curr)
        Hash8(*curr);
}

//===========================================================================
void CHashValue::Hash8 (unsigned data) {
    m_result += s_hashTable[m_result >> 24] ^ (m_result >> 6) ^ s_hashTable[data & 0xff];
}

//===========================================================================
void CHashValue::Hash16 (unsigned data) {
    Hash8(data);
    Hash8(data >> 8);
}

//===========================================================================
void CHashValue::Hash32 (unsigned data) {
    Hash8(data);
    Hash8(data >> 8);
    Hash8(data >> 16);
    Hash8(data >> 24);
}