#ifndef FXHASHTABLE_H
#define FXHASHTABLE_H

#include "tree.h"

namespace fx
{

    template<class Key, class Value> class HashTableRecord
    {
    public:
        typedef HashTableRecord self;
        typedef Key key_type;
        typedef Value value_type;

    public:
        HashTableRecord(const key_type& k = key_type(), value_type& v = value_type()): _key(k), _value(v) {}

        self& operator = (const value_type& v)
        {
            _value = v;
            return *this;
        }

        const key_type& key() const
        {
            return _key;
        }

        value_type& value()
        {
            return _value;
        }

        const value_type& value() const
        {
            return _value;
        }

    private:
        const key_type& _key;
        value_type& _value;
    };

    template<class Key, class Value> class HashTable
    {
    public:
        typedef HashTable self;
        typedef HashTableRecord<Key, Value> record;
        typedef Key key_type;
        typedef Value value_type;
        typedef unsigned int (*hash_function)(const key_type&);
        typedef Tree<key_type, value_type> Tree;
        typedef Node<key_type, value_type> Node;

    private:
        HashTable(const self&) {}
        self& operator = (const self&) {}

    public:
        HashTable(hash_function f, unsigned int max_hash_size)
        {
            _table.resize(max_hash_size);
            _hash = f;
        }

        ~HashTable() {}

        record operator [] (const key_type& k)
        {
            unsigned int hash_code = _hash(k);
            if(!_table[hash_code]) {
                throw fx::exception(1, "There is no such key in the table");
            }

            try {
                Node& node = _table[hash_code]->find(k);
                return record(node.key(), node.value());
            } catch(fx::exception) {
                throw fx::exception(1, "There is no such key in the table");
            }
        }

        const record operator [] (const key_type& k) const
        {
            return const_cast<self&>(*this)[k];
        }

        self& add(const record& r)
        {
            return add(r.key(), r.value());
        }

        self& add(const key_type& k, const value_type& v)
        {
            unsigned int hash_code = _hash(k);
            if(!_table[hash_code]) {
                _table[hash_code] = new Tree();
            }
            _table[hash_code]->add(k, v);
            return *this;
        }

        self& remove(const key_type& k)
        {
            unsigned int hash_code = _hash(k);
            if(_table[hash_code]) {
                _table[hash_code]->remove(k);
            }
            return *this;
        }

        self& clear()
        {
            _table.clear();
            return *this;
        }

        unsigned int size() const
        {
            unsigned int sum = 0;
            for(typename std::vector<Tree*>::const_iterator i = _table.begin(); i != _table.end(); ++i)
            {
                if(*i) {
                    sum += (*i)->count();
                }
            }
            return sum;
        }

        bool empty() const
        {
            return _table.empty();
        }

        bool exist(const key_type& k) const
        {
            try {
                (*this)[k];
            } catch(fx::exception) {
                return false;
            }
            return true;
        }

        double full_percent() const
        {
            unsigned int full = 0;
            for(typename std::vector<Tree*>::const_iterator i = _table.begin(); i != _table.end(); ++i)
            {
                 if(*i) {
                     ++full;
                 }
            }
            return static_cast<double>(full) / _table.size();
        }

        void rehash(hash_function f, unsigned int max_hash_size)
        {
            std::vector< Tree* > table(max_hash_size);
            _table.swap(table);
            _hash = f;
            for(typename std::vector<Tree*>::iterator i = table.begin(); i != table.end(); ++i)
            {
                if(*i) {
                    for(typename Tree::iterator k = (*i)->begin(); k != (*i)->end(); ++k)
                    {
                        add(k->key(), k->value());
                    }
                }
            }
        }

    private:
        std::vector< Tree* > _table;
        hash_function _hash;

    };

}

#endif // FXHASHTABLE_H
