// vi:set ft=cpp: -*- Mode: C++ -*-
/**
 * \file
 * \brief AVL map
 */
/*
 * (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
 *     economic rights: Technische Universität Dresden (Germany)
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */

#pragma once

#include <l4/cxx/std_alloc>
#include <l4/cxx/std_ops>
#include <l4/cxx/pair>
#include <l4/cxx/avl_set>

namespace cxx {
namespace Bits {

/// Key-getter for Avl_map
template<typename KEY_TYPE>
struct Avl_map_get_key
{
  typedef KEY_TYPE Key_type;
  template<typename NODE>
  static Key_type const &key_of(NODE const *n)
  { return n->item.first; }
};
}

/**
 * AVL tree based associative container.
 *
 * \tparam KEY_TYPE   Type of the key values.
 * \tparam DATA_TYPE  Type of the data values.
 * \tparam COMPARE    Type comparison functor for the key values.
 * \tparam ALLOC      Type of the allocator used for the nodes.
 */
template< typename KEY_TYPE, typename DATA_TYPE,
  template<typename A> class COMPARE = Lt_functor,
  template<typename B> class ALLOC = New_allocator >
class Avl_map :
  public Bits::Base_avl_set<Pair<KEY_TYPE, DATA_TYPE>,
                            COMPARE<KEY_TYPE>, ALLOC,
                            Bits::Avl_map_get_key<KEY_TYPE> >
{
private:
  typedef Pair<KEY_TYPE, DATA_TYPE> Local_item_type;
  typedef Bits::Base_avl_set<Local_item_type, COMPARE<KEY_TYPE>, ALLOC,
                             Bits::Avl_map_get_key<KEY_TYPE> > Base_type;

public:
  /// Type of the comparison functor.
  typedef COMPARE<KEY_TYPE> Key_compare;
  /// Type of the key values.
  typedef KEY_TYPE Key_type;
  /// Type of the data values.
  typedef DATA_TYPE Data_type;
  /// Return type for find.
  typedef typename Base_type::Node Node;
  /// Type of the allocator
  typedef typename Base_type::Node_allocator Node_allocator;

  typedef typename Base_type::Iterator Iterator;
  typedef typename Base_type::Iterator iterator;
  typedef typename Base_type::Const_iterator Const_iterator;
  typedef typename Base_type::Const_iterator const_iterator;
  typedef typename Base_type::Rev_iterator Rev_iterator;
  typedef typename Base_type::Rev_iterator reverse_iterator;
  typedef typename Base_type::Const_rev_iterator Const_rev_iterator;
  typedef typename Base_type::Const_rev_iterator const_reverse_iterator;

  /**
   * \brief Create an empty AVL-tree based map.
   * \param alloc The node allocator.
   */
  Avl_map(Node_allocator const &alloc = Node_allocator())
    : Base_type(alloc)
  {}

  /**
   * Insert a <key, data> pair into the map.
   *
   * \param key   The key value.
   * \param data  The data value to insert.
   *
   * \return A pair of iterator (`first`) and return value (`second`).
   *         `second` will be 0 if the element was inserted into the set
   *         and `-#E_exist` if the key was already in the set and the
   *         set was therefore not updated.
   *         In both cases, `first` contains an iterator that points to
   *         the element.
   *         `second` may also be `-#E_nomem` when memory for the new node
   *         could not be allocated. `first` is then invalid.
   */
  cxx::Pair<Iterator, int> insert(Key_type const &key, Data_type const &data)
  { return Base_type::insert(Pair<Key_type, Data_type>(key, data)); }

  template<typename... Args>
  cxx::Pair<Iterator, int> emplace(Args &&...args)
  { return Base_type::emplace(cxx::forward<Args>(args)...); }

  /**
   * \brief Get the data for the given key.
   * \param key The key value to use for lookup.
   * \pre A <key, data> pair for the given key value must exist.
   */
  Data_type const &operator [] (Key_type const &key) const
  { return this->find_node(key)->second; }

  /**
   * Get or insert data for the given key.
   *
   * \param key The key value to use for lookup.
   *
   * \return If the item already exists, a reference to the data item.
   *         Otherwise a new data item is default-constructed and inserted
   *         under the given key before a reference is returned.
   */
  Data_type &operator [] (Key_type const &key)
  {
    Node n = this->find_node(key);
    if (n)
      return const_cast<Data_type&>(n->second);
    else
      return insert(key, Data_type()).first->second;
  }
};

}

