/*
 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */

#ifndef BU_SYNCHRO_HASH_H
#define BU_SYNCHRO_HASH_H

#include <memory>
#include "bu/hash.h"
#include "bu/readwritemutex.h"

namespace Bu
{
    /**
     * Libbu++ Thread Safe Template Hash Table.  This is your average hash
     * table, that uses
     * template functions in order to do fast, efficient, generalized hashing.
     * It's pretty easy to use, and works well with all other libbu++ types so
     * far.
     *
     * In order to use it, I recommend the following for all basic usage:
     *@code
     // Define a Hash typedef with strings as keys and ints as values.
     typedef Bu::SynchroHash<Bu::String, int> StrIntHash;

     // Create one
     StrIntHash hInts;

     // Insert some integers
     hInts["one"] = 1;
     hInts["forty-two"] = 42;
     hInts.insert("forty two", 42 );

     // Get values out of the hash, the last two options are the most explicit,
     // and must be used if the hash's value type does not match what you're
     // comparing to exactly.
     if( hInts["one"] == 1 ) doSomething();
     if( hInts["forty-two"].value() == 42 ) doSomething();
     if( hInts.get("forty two") == 42 ) doSomething();

     // Iterate through the Hash
     for( StrIntHash::iterator i = hInts.begin(); i != hInts.end(); i++ )
     {
        // i.getValue() works too
        print("'%s' = %d\n", i.getKey().getStr(), (*i) );
     }
                                                    
     @endcode
     *@param key (typename) The datatype of the hashtable keys
     *@param value (typename) The datatype of the hashtable data
     *@param sizecalc (typename) Functor to compute new table size on rehash
     *@param keyalloc (typename) Memory allocator for hashtable keys
     *@param valuealloc (typename) Memory allocator for hashtable values
     *@param challoc (typename) Byte allocator for bitflags
     *@ingroup Containers
     */
    template<typename key, typename value,
        typename sizecalc = __calcNextTSize_fast,
        typename keyalloc = std::allocator<key>,
        typename valuealloc = std::allocator<value>,
        typename challoc = std::allocator<uint32_t>
        >
    class SynchroHash
    {
    private:
        typedef class SynchroHash<key, value, sizecalc, keyalloc, valuealloc, challoc> MyType;
        typedef class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> HashType;
    
    public:
        SynchroHash()
        {
        }
        
        virtual ~SynchroHash()
        {
        }

        /**
         * Get the current hash table capacity. (Changes at re-hash)
         *@returns (uint32_t) The current capacity.
         */
        uint32_t getCapacity() const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            return hCore.getCapacity();
        }

        /**
         * Get the number of hash locations spoken for. (Including 
         * not-yet-cleaned-up deleted items.)
         *@returns (uint32_t) The current fill state.
         */
        uint32_t getFill() const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            return hCore.getFill();
        }

        /**
         * Get the number of items stored in the hash table.
         *@returns (uint32_t) The number of items stored in the hash table.
         */
        uint32_t getSize() const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            return hCore.getSize();
        }

        bool isEmpty() const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            return hCore.isEmpty();
        }

        /**
         * Get the number of items which have been deleted, but not yet
         * cleaned up.
         *@returns (uint32_t) The number of deleted items.
         */
        uint32_t getDeleted() const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            return hCore.getDeleted();
        }

        /**
         * Insert a value (v) under key (k) into the hash table
         *@param k (key_type) Key to list the value under.
         *@param v (value_type) Value to store in the hash table.
         */
        void insert( const key &k, const value &v )
        {
            ReadWriteMutex::WriteLocker wl( mCore );
            hCore.insert( k, v );
        }

        /**
         * Remove a value from the hash table.
         *@param k (key_type) The data under this key will be erased.
         */
        void erase( const key &k )
        {
            ReadWriteMutex::WriteLocker wl( mCore );
            hCore.erase( k );
        }

        /**
         * Remove all data from the hash table.
         */
        virtual void clear()
        {
            ReadWriteMutex::WriteLocker wl( mCore );
            hCore.clear();
        }

        /**
         * Get a const item of data from the hash table.
         *@param k (key_type) Key pointing to the data to be retrieved.
         *@returns (const value_type &) A const version of the data pointed
         *      to by (k).
         */
        value get( const key &k ) const
        {
            ReadWriteMutex::WriteLocker wl( mCore );
            return hCore.get( k );
        }

        /**
         * Does the hash table contain an item under key (k).
         *@param k (key_type) The key to check.
         *@returns (bool) Whether there was an item in the hash under key (k).
         */
        bool has( const key &k ) const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            return hCore.has( k );
        }

        /**
         * Get a list of all the keys in the hash table.
         *@returns (std::list<key_type>) The list of keys in the hash table.
         */
        Bu::List<key> getKeys() const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            return hCore.getKeys();
        }
        
        Bu::List<value> getValues() const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            return hCore.getValues();
        }

        bool operator==( const MyType &rhs ) const
        {
            ReadWriteMutex::ReadLocker rl( mCore );
            ReadWriteMutex::ReadLocker rl2( rhs.mCore );
            return hCore == rhs.hCore;
        }

        bool operator!=( const MyType &rhs ) const
        {
            return !(*this == rhs);
        }

        MyType &operator+=( const MyType &rhs )
        {
            ReadWriteMutex::WriteLocker wl( mCore );
            ReadWriteMutex::ReadLocker rl( rhs.mCore );
            hCore += rhs.hCore;
            return *this;
        }

    private:
        HashType hCore;
        mutable ReadWriteMutex mCore;
    };
}

#endif
