/* Copyright (C) 2010 Szilárd Pfeiffer <mailbox@pfeifferszilard.hu>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

_DEFS(glibmm,glib)

#include <glibmm/refptr.h>
#include <glibmm/ustring.h>
#include <glibmm/error.h>
#include <glibmm/arrayhandle.h>
#include <glib/gtree.h>

namespace Glib
{
/** Balanced Binary Trees — a sorted collection of key/value pairs optimized for searching and traversing in order
 * The BalancedTree structure and its associated functions provide a sorted
 * collection of key/value pairs optimized for searching and traversing in
 * order.
 *
 * To insert a key/value pair into a BalancedTree use insert().
 *
 * To lookup the value corresponding to a given key, use lookup().
 *
 * To find out the number of nodes in a BalancedTree, use nnodes(). To get the height of a BalancedTree, use height().
 *
 * To traverse a BalancedTree, calling a function for each node visited in the traversal, use foreach().
 *
 * To remove a key/value pair use remove().
 *
 * Any type to be used with this template must implement copy constructor.
 * Compiler-generated implementations are OK, provided they do the right
 * thing for the type. Both keys and values are stored in the balanced binary
 * tree as copies, created by copy contructors.
 *
 * Type of key to be used with this template must implement:
 * - less than operator
 * - greater than operator
 *
 * @newin2p24
 */
template <typename K, typename V>
class BalancedTree
{
  _CLASS_GENERIC(BalancedTree, GTree)
public:
  typedef sigc::slot<bool, const K&, const V&> TraverseFunc;
  typedef sigc::slot<int, const K&, const K&> CompareFunc;

protected:
  BalancedTree() :
    key_compare_slot(sigc::ptr_fun(key_compare))
  {
    gobject_ = g_tree_new_full(on_compare_tree, &key_compare_slot, on_destroy_key, on_destroy_value);
  }

  BalancedTree(const CompareFunc &key_compare_slot_) :
    key_compare_slot(key_compare_slot_)
  {
    gobject_ = g_tree_new_full(on_compare_tree, &key_compare_slot, on_destroy_key, on_destroy_value);
  }

public:
  static Glib::RefPtr< BalancedTree<K, V> > create()
  {
    return Glib::RefPtr< BalancedTree<K, V> >(new BalancedTree());
  }

  static Glib::RefPtr< BalancedTree<K, V> > create(const CompareFunc &key_compare_slot)
  {
    return Glib::RefPtr< BalancedTree<K, V> >(new BalancedTree(key_compare_slot));
  }

  ~BalancedTree()
  {
    g_tree_destroy(gobject_);
  }
  _IGNORE(g_tree_destroy)

  /// Provides access to the underlying C GObject.
  inline GTree* gobj()
  {
    return gobject_;
  }

  /// Provides access to the underlying C GObject.
  inline const GTree* gobj() const
  {
    return gobject_;
  }

/**
 * reference:
 *
 * Increments the reference count of tree by one.  It is safe to call
 * this function from any thread.
 **/
  void reference()
  {
    g_tree_ref(gobject_);
  }
  _IGNORE(g_tree_ref)

/**
 * unreference:
 *
 * Decrements the reference count of tree by one. If the reference count
 * drops to 0, all keys and values will be destroyed and all memory allocated
 * by tree will be released.
 *
 * It is safe to call this function from any thread.
 **/
  void unreference()
  {
    g_tree_unref(gobject_);
  }
  _IGNORE(g_tree_unref)

/**
 * insert:
 * @key: the key to insert.
 * @value: the value corresponding to the key.
 *
 * Inserts a key/value pair into a #BalancedTree. If the given key already
 * exists in the #BalancedTree its corresponding value is set to the new
 * value.
 *
 * The tree is automatically 'balanced' as new key/value pairs are added,
 * so that the distance from the root to every leaf is as small as possible.
 **/
  void insert(const K &key, const V &value)
  {
    g_tree_insert(gobj(), reinterpret_cast<void *>(new K(key)), reinterpret_cast<void *>(new V(value)));
  }
  _IGNORE(g_tree_insert)

/**
 * remove:
 * @key: the key to remove.
 *
 * Removes a key/value pair from a #BalancedTree.
 *
 * If the key does not exist in the #BalancedTree, the function does nothing.
 *
 * Returns: %TRUE if the key was found (prior to 2.8, this function returned
 *   nothing)
 **/
  bool remove(const K &key)
  {
    return g_tree_remove(const_cast<GTree*>(gobj()), &key);
  }
  _IGNORE(g_tree_remove)

/**
 * lookup:
 * @key: the key to look up.
 *
 * Gets the value corresponding to the given key. Since a #BalancedTree is
 * automatically balanced as key/value pairs are added, key lookup is very
 * fast.
 *
 * Return value: the value corresponding to the key, or %NULL if the key was
 * not found.
 **/
  V* lookup(const K &key)
  {
    return reinterpret_cast<V *>(g_tree_lookup(gobj(), &key));
  }
  _IGNORE(g_tree_lookup)

  const V* lookup(const K &key) const
  {
    return reinterpret_cast<const V *>(g_tree_lookup(gobj(), &key));
  }
  _IGNORE(g_tree_lookup)

/**
 * height:
 *
 * Gets the height of a #BalancedTree.
 *
 * If the #BalancedTree contains no nodes, the height is 0.
 * If the #BalancedTree contains only one root node the height is 1.
 * If the root node has children the height is 2, etc.
 *
 * Return value: the height of the #BalancedTree.
 **/
  gint height() const
  {
    return g_tree_height(const_cast<GTree*>(gobj()));
  }
  _IGNORE(g_tree_height)

/**
 * nnodes:
 *
 * Gets the number of nodes in a #BalancedTree.
 *
 * Return value: the number of nodes in the #BalancedTree.
 **/
  gint nnodes() const
  {
    return g_tree_nnodes(const_cast<GTree*>(gobj()));
  }
  _IGNORE(g_tree_nnodes)

/**
 * foreach:
 * @func: the function to call for each node visited. If this function
 *   returns true, the traversal is stopped.
 *
 * Calls the given function for each of the key/value pairs in the
 * #BalancedTree.
 * The function is passed the key and value of each pair. The tree is
 * traversed in sorted order.
 *
 * The tree may not be modified while iterating over it (you can't
 * add/remove items). To remove all items matching a predicate, you need
 * to add each item to a list in your #TraverseFunc as you walk over
 * the tree, then walk the list and remove each item.
 **/
  void foreach(const TraverseFunc& func) const
  {
    TraverseFunc func_copy = func;
    g_tree_foreach(const_cast<GTree*>(gobj()), c_callback_traverse, reinterpret_cast<gpointer>(&func_copy));
  }
  _IGNORE(g_tree_foreach);

/**
 * search:
 * @search_func: a function used to search the #BalancedTree.
 *
 * Searches a #BalancedTree using @search_func.
 *
 * The @search_func is called with a reference to the key of a key/value pair
 * in the tree. If @search_func returns 0 for a key/value pair, then search()
 * will return the value of that pair. If @search_func returns -1,  searching
 * will proceed among the key/value pairs that have a smaller key; if
 * @search_func returns 1, searching will proceed among the key/value pairs
 * that have a larger key.
 *
 * Return value: the value corresponding to the found key, or %NULL if the key
 * was not found.
 **/
  V* search(const CompareFunc &search_func, const K& key)
  {
    sigc::slot<int, const K&, const CompareFunc&, const K&> real_slot = sigc::ptr_fun(on_compare_key);
    sigc::slot<int, const K&> bound_slot = sigc::bind(real_slot, search_func, key);
    gpointer value = g_tree_search(gobj(), c_callback_search, reinterpret_cast<gconstpointer>(&bound_slot));

    return reinterpret_cast<V*>(value);
  }

 /**
 * search:
 * @search_func: a function used to search the #BalancedTree.
 *
 * Searches a #BalancedTree using @search_func.
 *
 * The @search_func is called with a reference to the key of a key/value pair
 * in the tree. If @search_func returns 0 for a key/value pair, then search()
 * will return the value of that pair. If @search_func returns -1,  searching
 * will proceed among the key/value pairs that have a smaller key; if
 * @search_func returns 1, searching will proceed among the key/value pairs
 * that have a larger key.
 *
 * Return value: the value corresponding to the found key, or %NULL if the key
 * was not found.
 **/
 const V* search(const CompareFunc &search_func, const K& key) const
  {
    return const_cast<BalancedTree<K, V>*>(this)->search(search_func, key);
  }
  _IGNORE(g_tree_search)

  /* the following functions don't make sense for the C++ wrapper */
  _IGNORE(g_tree_steal)
  _IGNORE(g_tree_lookup_extended)
  _IGNORE(g_tree_replace)
  _IGNORE(g_tree_traverse)

private:
  /// Method for comparing keys by func (Internal use).
  static gint on_compare_key(const K& key_a, const CompareFunc& func, const K& key_b)
  {
    return func(key_a, key_b);
  }

  /// Wrapper for invoking GCompareFunc.
  static gint c_callback_search(gconstpointer a, gconstpointer b)
  {
    const sigc::slot<int, const K&>* slot = reinterpret_cast<const sigc::slot<int, const K&> *>(b);
    return (*slot)(*reinterpret_cast<const K*>(a));
  }

  /// Wrapper for invoking GTraverseFunc.
  static gboolean c_callback_traverse(gpointer key, gpointer value, gpointer slot)
  {
    const TraverseFunc* tf = reinterpret_cast<const TraverseFunc*>(slot);
    return (*tf)(*reinterpret_cast<const K*>(key), *reinterpret_cast<const V*>(value));
  }

  /// Method for comparing key values (Internal use).
  static gint on_compare_tree(gconstpointer a, gconstpointer b, gpointer data)
  {
    const K& key_a = *reinterpret_cast<const K*>(a);
    const K& key_b = *reinterpret_cast<const K*>(b);
    const CompareFunc& func = *reinterpret_cast<const CompareFunc*>(data);

    return func(key_a, key_b);
  }

  /// Method for destroying keys (Internal use).
  static void on_destroy_key(gpointer data)
  {
    K* key = reinterpret_cast<K*>(data);
    delete key;
  }

  /// Method for destroying values (Internal use).
  static void on_destroy_value(gpointer data)
  {
    V* value = reinterpret_cast<V*>(data);
    delete value;
  }

  /// Key compare function when it is not by the user (Internal use).
  static int key_compare(const K& key_a, const K& key_b)
  {
    if(key_a < key_b)
      return -1;

    if(key_a > key_b)
      return 1;

    return 0;
  }

  GTree* gobject_;
  CompareFunc key_compare_slot;
};

} // namespace Glib
