#ifndef WidthCache_h
#define WidthCache_h
#include "platform/text/TextRun.h"
#include "wtf/Forward.h"
#include "wtf/HashFunctions.h"
#include "wtf/HashSet.h"
#include "wtf/HashTableDeletedValueType.h"
#include "wtf/StringHasher.h"
namespace WebCore {
struct GlyphOverflow;
class WidthCache {
private:
    
    class SmallStringKey {
    public:
        static unsigned capacity() { return s_capacity; }
        SmallStringKey()
            : m_length(s_emptyValueLength)
        {
        }
        SmallStringKey(WTF::HashTableDeletedValueType)
            : m_length(s_deletedValueLength)
        {
        }
        template<typename CharacterType> SmallStringKey(CharacterType* characters, unsigned short length)
            : m_length(length)
        {
            ASSERT(length <= s_capacity);
            StringHasher hasher;
            bool remainder = length & 1;
            length >>= 1;
            unsigned i = 0;
            while (length--) {
                m_characters[i] = characters[i];
                m_characters[i + 1] = characters[i + 1];
                hasher.addCharactersAssumingAligned(characters[i], characters[i + 1]);
                i += 2;
            }
            if (remainder) {
                m_characters[i] = characters[i];
                hasher.addCharacter(characters[i]);
            }
            m_hash = hasher.hash();
        }
        const UChar* characters() const { return m_characters; }
        unsigned short length() const { return m_length; }
        unsigned hash() const { return m_hash; }
        bool isHashTableDeletedValue() const { return m_length == s_deletedValueLength; }
        bool isHashTableEmptyValue() const { return m_length == s_emptyValueLength; }
    private:
        static const unsigned s_capacity = 15;
        static const unsigned s_emptyValueLength = s_capacity + 1;
        static const unsigned s_deletedValueLength = s_capacity + 2;
        unsigned m_hash;
        unsigned short m_length;
        UChar m_characters[s_capacity];
    };
    struct SmallStringKeyHash {
        static unsigned hash(const SmallStringKey& key) { return key.hash(); }
        static bool equal(const SmallStringKey& a, const SmallStringKey& b) { return a == b; }
        static const bool safeToCompareToEmptyOrDeleted = true; 
    };
    struct SmallStringKeyHashTraits : WTF::SimpleClassHashTraits<SmallStringKey> {
        static const bool hasIsEmptyValueFunction = true;
        static bool isEmptyValue(const SmallStringKey& key) { return key.isHashTableEmptyValue(); }
        static const bool needsDestruction = false;
        static const unsigned minimumTableSize = 16;
    };
    friend bool operator==(const SmallStringKey&, const SmallStringKey&);
public:
    WidthCache()
        : m_interval(s_maxInterval)
        , m_countdown(m_interval)
    {
    }
    float* add(const TextRun& run, float entry)
    {
        if (static_cast<unsigned>(run.length()) > SmallStringKey::capacity())
            return 0;
        if (m_countdown > 0) {
            --m_countdown;
            return 0;
        }
        return addSlowCase(run, entry);
    }
    void clear()
    {
        m_singleCharMap.clear();
        m_map.clear();
    }
private:
    float* addSlowCase(const TextRun& run, float entry)
    {
        int length = run.length();
        bool isNewEntry;
        float *value;
        if (length == 1) {
            SingleCharMap::AddResult addResult = m_singleCharMap.add(run[0], entry);
            isNewEntry = addResult.isNewEntry;
            value = &addResult.storedValue->value;
        } else {
            SmallStringKey smallStringKey;
            if (run.is8Bit())
                smallStringKey = SmallStringKey(run.characters8(), length);
            else
                smallStringKey = SmallStringKey(run.characters16(), length);
            Map::AddResult addResult = m_map.add(smallStringKey, entry);
            isNewEntry = addResult.isNewEntry;
            value = &addResult.storedValue->value;
        }
        
        if (!isNewEntry) {
            m_interval = s_minInterval;
            return value;
        }
        
        if (m_interval < s_maxInterval)
            ++m_interval;
        m_countdown = m_interval;
        if ((m_singleCharMap.size() + m_map.size()) < s_maxSize)
            return value;
        
        m_singleCharMap.clear();
        m_map.clear();
        return 0;
    }
    typedef HashMap<SmallStringKey, float, SmallStringKeyHash, SmallStringKeyHashTraits> Map;
    typedef HashMap<uint32_t, float, DefaultHash<uint32_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint32_t> > SingleCharMap;
    static const int s_minInterval = -3; 
    static const int s_maxInterval = 20; 
    static const unsigned s_maxSize = 500000; 
    int m_interval;
    int m_countdown;
    SingleCharMap m_singleCharMap;
    Map m_map;
};
inline bool operator==(const WidthCache::SmallStringKey& a, const WidthCache::SmallStringKey& b)
{
    if (a.length() != b.length())
        return false;
    return WTF::equal(a.characters(), b.characters(), a.length());
}
} 
#endif