//
// SimpleHashTable.h
//
// $Id: //poco/1.2/Foundation/include/Poco/SimpleHashTable.h#2 $
//
// Library: Foundation
// Package: Core
// Module:  SimpleHashTable
//
// Definition of the SimpleHashTable class.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
// 
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//


#ifndef Foundation_SimpleHashTable_INCLUDED
#define Foundation_SimpleHashTable_INCLUDED


#include "Poco/Foundation.h"
#include "Poco/Exception.h"
#include "Poco/HashFunction.h"
#include "Poco/HashStatistic.h"
#include <vector>
#include <map>
#include <stddef.h>


namespace Poco {


template< class Key, class Value, class KeyHashFunction = HashFunction< Key> >
class SimpleHashTable
	/// A SimpleHashTable stores a key value pair that can be looked up via a hashed key.
	///
	/// In comparision to a HashTable, this class handles collisions by sequentially searching the next
	/// free location. This also means that the maximum size of this table is limited, i.e. if the hash table
	/// is full, it will throw an exception and that this class does not support remove operations.
	/// On the plus side it is faster than the HashTable.
	///
	/// This class is NOT thread safe.
{
public:
	class HashEntry
	{
	public:
		Key key;
		Value value;
		HashEntry(const Key k, const Value v): 
			key(k), 
			value(v)
		{
		}
	};

	typedef HashEntry** HashTableVector;

	SimpleHashTable(UInt32 initialSize = 251): _entries(0), _size(0), _maxCapacity(initialSize)
		/// Creates the SimpleHashTable.
	{
		_entries = new HashEntry*[initialSize];
		memset(_entries, '\0', sizeof(HashEntry*)*initialSize);
	}

	SimpleHashTable(const SimpleHashTable& ht):
		_entries (new HashEntry*[ht._maxCapacity]),
		_size(ht._size),
		_maxCapacity(ht._maxCapacity)
	{
		for (int i = 0; i < _maxCapacity; ++i)
		{
			if (ht._entries[i])
				_entries[i] = new HashEntry(*ht._entries[i]);
			else
				_entries[i] = 0;
		}
	}

	~SimpleHashTable()
		/// Destroys the SimpleHashTable.
	{
		clear();
	}

	SimpleHashTable& operator = (const SimpleHashTable& ht)
	{
		if (this != &ht)
		{
			clear();
			_maxCapacity = ht._maxCapacity;
			delete[] _entries;
			_entries = new HashEntry*[_maxCapacity];
			_size = ht._size;

			for (int i = 0; i < _maxCapacity; ++i)
			{
				if (ht._entries[i])
					_entries[i] = new HashEntry(*ht._entries[i]);
				else
					_entries[i] = 0;
			}
		}
		return *this;
	}

	void clear()
	{
		if (!_entries)
			return;
		for (int i = 0; i < _maxCapacity; ++i)
		{
			if (_entries[i])
				delete _entries[i];
		}
		delete[] _entries;
		_entries     = 0;
		_size        = 0;
		_maxCapacity = 0;
	}

	UInt32 insert(const Key& key, const Value& value)
		/// Returns the hash value of the inserted item.
		/// Throws an exception if the entry was already inserted
	{
		UInt32 hsh = hash(key);
		insertRaw(key, hsh, value);
		return hsh;
	}

	void insertRaw(const Key& key, UInt32 hsh, const Value& value)
		/// Returns the hash value of the inserted item.
		/// Throws an exception if the entry was already inserted
	{
		if (!_entries[hsh])
			_entries[hsh] = new HashEntry(key, value);
		else
		{
			UInt32 origHash = hsh;
			while (_entries[hsh % _maxCapacity])
			{
				poco_assert_dbg(_entries[hsh % _maxCapacity]->key != key);
				if (hsh - origHash > _maxCapacity)
					throw PoolOverflowException("SimpleHashTable full");
				hsh++;
			}
			_entries[hsh % _maxCapacity] = new HashEntry(key, value);
		}
		_size++;
	}

	UInt32 update(const Key& key, const Value& value)
		/// Returns the hash value of the inserted item.
		/// Replaces an existing entry if it finds one
	{
		UInt32 hsh = hash(key);
		updateRaw(key, hsh, value);
		return hsh;
	}

	void updateRaw(const Key& key, UInt32 hsh, const Value& value)
		/// Returns the hash value of the inserted item.
		/// Replaces an existing entry if it finds one
	{
		if (!_entries[hsh])
			_entries[hsh] = new HashEntry(key, value);
		else
		{
			UInt32 origHash = hsh;
			while (_entries[hsh % _maxCapacity])
			{
				if (_entries[hsh % _maxCapacity]->key == key)
				{
					_entries[hsh % _maxCapacity]->value = value;
					return;
				}
				if (hsh - origHash > _maxCapacity)
					throw PoolOverflowException("SimpleHashTable full");
				hsh++;
			}
			_entries[hsh % _maxCapacity] = new HashEntry(key, value);
		}
		_size++;
	}

	UInt32 hash(const Key& key) const
	{
		return KeyHashFunction::hash(key, _maxCapacity);
	}

	const Value& get(const Key& key) const
		/// Throws an exception if the value does not exist
	{
		UInt32 hsh = hash(key);
		return getRaw(key, hsh);
	}

	const Value& getRaw(const Key& key, UInt32 hsh) const
		/// Throws an exception if the value does not exist
	{
		UInt32 origHash = hsh;
		while (true)
		{
			if (_entries[hsh % _maxCapacity])
			{
				if (_entries[hsh % _maxCapacity]->key == key)
				{
					return _entries[hsh % _maxCapacity]->value;
				}
			}
			else
				throw InvalidArgumentException("value not found");
			if (hsh - origHash > _maxCapacity)
				throw InvalidArgumentException("value not found");
			hsh++;
		}
	}

	const Key& getKeyRaw(const Key& key, UInt32 hsh)
		/// Throws an exception if the key does not exist. returns a reference to the internally
		/// stored key. Useful when someone does an insert and wants for performance reason only to store
		/// a pointer to the key in another collection
	{
		UInt32 origHash = hsh;
		while (true)
		{
			if (_entries[hsh % _maxCapacity])
			{
				if (_entries[hsh % _maxCapacity]->key == key)
				{
					return _entries[hsh % _maxCapacity]->key;
				}
			}
			else
				throw InvalidArgumentException("key not found");

			if (hsh - origHash > _maxCapacity)
				throw InvalidArgumentException("key not found");
			hsh++;
		}
	}

	bool get(const Key& key, Value& v) const
		/// Sets v to the found value, returns false if no value was found
	{
		UInt32 hsh = hash(key);
		return getRaw(key, hsh, v);
	}

	bool getRaw(const Key& key, UInt32 hsh, Value& v) const
		/// Sets v to the found value, returns false if no value was found
	{
		UInt32 origHash = hsh;
		while (true)
		{
			if (_entries[hsh % _maxCapacity])
			{
				if (_entries[hsh % _maxCapacity]->key == key)
				{
					v = _entries[hsh % _maxCapacity]->value;
					return true;
				}
			}
			else
				return false;
			if (hsh - origHash > _maxCapacity)
				return false;
			hsh++;
		}
	}

	bool exists(const Key& key)
	{
		UInt32 hsh = hash(key);
		return existsRaw(key, hsh);
	}

	bool existsRaw(const Key& key, UInt32 hsh)
	{
		UInt32 origHash = hsh;
		while (true)
		{
			if (_entries[hsh % _maxCapacity])
			{
				if (_entries[hsh % _maxCapacity]->key == key)
				{
					return true;
				}
			}
			else
				return false;
			if (hsh - origHash > _maxCapacity)
				return false;
			hsh++;
		}
	}

	size_t size() const
		/// Returns the number of elements already inserted into the SimpleHashTable
	{
		return _size;
	}
	
	UInt32 maxCapacity() const
	{
		return _maxCapacity;
	}

	void resize(UInt32 newSize)
		/// Resizes the hashtable, rehashes all existing entries. Expensive!
	{
		if (_maxCapacity != newSize)
		{
			HashTableVector cpy = _entries;
			_entries = 0;
			UInt32 oldSize = _maxCapacity;
			_maxCapacity = newSize;
			_entries = new HashEntry*[_maxCapacity];
			memset(_entries, '\0', sizeof(HashEntry*)*_maxCapacity);
			
			if (_size == 0)
			{
				// no data was yet inserted
				delete[] cpy;
				return;
			}
			_size = 0;
			for (int i=0; i < oldSize; ++i)
			{
				if (cpy[i])
				{
					insert(cpy[i]->key, cpy[i]->value);
					delete cpy[i];
				}
			}
			delete[] cpy;
		}
	}

	HashStatistic currentState(bool details = false) const
		/// Returns the current internal state
	{
		UInt32 numberOfEntries = (UInt32)_size;
		UInt32 numZeroEntries = 0;
		UInt32 maxEntriesPerHash = 0;
		std::vector<UInt32> detailedEntriesPerHash;
	#ifdef _DEBUG
		UInt32 totalSize = 0;
	#endif
		for (int i=0; i < _maxCapacity; ++i)
		{
			if (_entries[i])
			{
				maxEntriesPerHash = 1;
				UInt32 size = 1;
				if (details)
					detailedEntriesPerHash.push_back(size);
	#ifdef DEBUG
				totalSize += size;
	#endif
			}
			else
			{
				numZeroEntries++;
				if (details)
					detailedEntriesPerHash.push_back(0);
			}
		}
	#ifdef DEBUG
		poco_assert_dbg(totalSize == numberOfEntries);
	#endif
		return HashStatistic(_maxCapacity, numberOfEntries, numZeroEntries, maxEntriesPerHash, detailedEntriesPerHash);
	}

private:
	HashTableVector _entries;
	size_t _size;
	UInt32 _maxCapacity;
};


} // namespace Poco


#endif // Foundation_HashTable_INCLUDED
