/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef IDBKeyTree_h
#define IDBKeyTree_h

#if ENABLE(INDEXED_DATABASE)

#include "IDBKey.h"
#include <wtf/AVLTree.h>
#include <wtf/Vector.h>

namespace WebCore {

template <typename ValueType>
class IDBKeyTree : public RefCounted<IDBKeyTree<ValueType> > {
public:
    static PassRefPtr<IDBKeyTree> create()
    {
        return adoptRef(new IDBKeyTree());
    }
    ~IDBKeyTree();

    ValueType* get(IDBKey* key);
    void put(IDBKey* key, ValueType* value);
    void remove(IDBKey* key);

private:
    struct TreeNode {
        RefPtr<ValueType> value;
        RefPtr<IDBKey> key;

        TreeNode* less;
        TreeNode* greater;
        int balanceFactor;
    };

    struct AVLTreeAbstractor {
        typedef TreeNode* handle;
        typedef size_t size;
        typedef IDBKey* key;

        handle get_less(handle h) { return h->less; }
        void set_less(handle h, handle lh) { h->less = lh; }
        handle get_greater(handle h) { return h->greater; }
        void set_greater(handle h, handle gh) { h->greater = gh; }
        int get_balance_factor(handle h) { return h->balanceFactor; }
        void set_balance_factor(handle h, int bf) { h->balanceFactor = bf; }

        static handle null() { return 0; }

        int compare_key_key(key va, key vb);
        int compare_key_node(key k, handle h) { return compare_key_key(k, h->key.get()); }
        int compare_node_node(handle h1, handle h2) { return compare_key_key(h1->key.get(), h2->key.get()); }
    };

    IDBKeyTree();

    typedef WTF::AVLTree<AVLTreeAbstractor> TreeType;
    TreeType m_tree;
};

template <typename ValueType>
IDBKeyTree<ValueType>::IDBKeyTree()
{
}

template <typename ValueType>
IDBKeyTree<ValueType>::~IDBKeyTree()
{
    typename TreeType::Iterator iter;
    iter.start_iter_least(m_tree);
    for (; *iter; ++iter)
        delete *iter;
    m_tree.purge();
}

template <typename ValueType>
int IDBKeyTree<ValueType>::AVLTreeAbstractor::compare_key_key(key va, key vb)
{
    if (va->type() != vb->type())
        return vb->type() - va->type();

    switch (va->type()) {
    case IDBKey::NullType:
        return 0;
    case IDBKey::NumberType:
        return vb->number() - va->number();
    case IDBKey::StringType:
        return codePointCompare(va->string(), vb->string());
    // FIXME: Handle dates.  Oh, and test this thoroughly.
    default:
        ASSERT_NOT_REACHED();
        return 0;
    }
}

template <typename ValueType>
ValueType* IDBKeyTree<ValueType>::get(IDBKey* key)
{
    TreeNode* node = m_tree.search(key);
    if (!node)
        return 0;
    return node->value.get();
}

template <typename ValueType>
void IDBKeyTree<ValueType>::put(IDBKey* key, ValueType* value)
{
    TreeNode* node = m_tree.search(key);
    if (!node) {
        node = new TreeNode();
        node->key = key;
        m_tree.insert(node);
    }
    node->value = value;
}

template <typename ValueType>
void IDBKeyTree<ValueType>::remove(IDBKey* key)
{
    TreeNode* node = m_tree.remove(key);
    if (node)
        delete node;
}

}

#endif // ENABLE(INDEXED_DATABASE)

#endif // IDBKeyTree_h
