// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (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/type_traits>
#include <l4/cxx/std_alloc>
#include <l4/cxx/std_ops>

namespace cxx {
/*
 * Classes: List_item, List<D, Alloc>
 */

/**
 * \ingroup cxx_api
 * \brief Basic list item.
 *
 * Basic item that can be member of a doubly linked, cyclic list.
 */
class List_item
{
public:
  /**
   * \brief Iterator for a list of ListItem-s.
   *
   * The Iterator iterates till it finds the first element again.
   */
  class Iter
  {
  public:
    Iter(List_item *c, List_item *f) noexcept : _c(c), _f(f) {}
    Iter(List_item *f = 0) noexcept : _c(f), _f(f) {}

    List_item *operator * () const noexcept { return _c; }
    List_item *operator -> () const noexcept { return _c; }
    Iter &operator ++ () noexcept
    {
      if (!_f)
	_c = 0;
      else
        _c = _c->get_next_item();

      if (_c == _f)
	_c = 0;

      return *this;
    }

    Iter operator ++ (int) noexcept
    { Iter o = *this; operator ++ (); return o; }

    Iter &operator -- () noexcept
    {
      if (!_f)
	_c = 0;
      else
        _c = _c->get_prev_item();

      if (_c == _f)
	_c = 0;

      return *this;
    }

    Iter operator -- (int) noexcept
    { Iter o = *this; operator -- (); return o; }

    /** Remove item pointed to by iterator, and return pointer to element. */
    List_item *remove_me() noexcept
    {
      if (!_c)
	return 0;

      List_item *l = _c;
      operator ++ ();
      l->remove_me();

      if (_f == l)
	_f = _c;

      return l;
    }

  private:
    List_item *_c, *_f;
  };

  /**
   * \brief Iterator for derived classes from ListItem.
   *
   * Allows direct access to derived classes by * operator.
   *
   * Example:
   * class Foo : public ListItem
   * {
   * public:
   *   typedef T_iter<Foo> Iter;
   *   ...
   * };
   */
  template< typename T, bool Poly = false>
  class T_iter : public Iter
  {
  private:
    static bool const P = !Conversion<const T*, const List_item *>::exists
      || Poly;

    static List_item *cast_to_li(T *i, Int_to_type<true>) noexcept
    { return dynamic_cast<List_item*>(i); }

    static List_item *cast_to_li(T *i, Int_to_type<false>) noexcept
    { return i; }

    static T *cast_to_type(List_item *i, Int_to_type<true>) noexcept
    { return dynamic_cast<T*>(i); }

    static T *cast_to_type(List_item *i, Int_to_type<false>) noexcept
    { return static_cast<T*>(i); }

  public:

    template< typename O >
    explicit T_iter(T_iter<O> const &o) noexcept
    : Iter(o) { dynamic_cast<T*>(*o); }

    //TIter(CListItem *f) : Iter(f) {}
    T_iter(T *f = 0) noexcept : Iter(cast_to_li(f, Int_to_type<P>())) {}
    T_iter(T *c, T *f) noexcept
    : Iter(cast_to_li(c, Int_to_type<P>()),
	cast_to_li(f, Int_to_type<P>()))
    {}

    inline T *operator * () const noexcept
    { return cast_to_type(Iter::operator * (),Int_to_type<P>()); }
    inline T *operator -> () const noexcept
    { return operator * (); }

    T_iter<T, Poly> operator ++ (int) noexcept
    { T_iter<T, Poly> o = *this; Iter::operator ++ (); return o; }
    T_iter<T, Poly> operator -- (int) noexcept
    { T_iter<T, Poly> o = *this; Iter::operator -- (); return o; }
    T_iter<T, Poly> &operator ++ () noexcept
    { Iter::operator ++ (); return *this; }
    T_iter<T, Poly> &operator -- () noexcept
    { Iter::operator -- (); return *this; }
    inline T *remove_me() noexcept;
  };

  List_item() noexcept : _n(this), _p(this) {}

protected:
  List_item(List_item const &) noexcept : _n(this), _p(this) {}

public:
  /** Get previous item. */
  List_item *get_prev_item() const noexcept { return _p; }

  /** Get next item. */
  List_item *get_next_item() const noexcept { return _n; }

  /** Insert item p before this item. */
  void insert_prev_item(List_item *p) noexcept
  {
    p->_p->_n = this;
    List_item *pr = p->_p;
    p->_p = _p;
    _p->_n = p;
    _p = pr;
  }

  /** Insert item p after this item. */
  void insert_next_item(List_item *p) noexcept
  {
    p->_p->_n = _n;
    p->_p = this;
    _n->_p = p;
    _n = p;
  }

  /** Remove this item from the list. */
  void remove_me() noexcept
  {
    if (_p != this)
      {
        _p->_n = _n;
        _n->_p = _p;
      }
    _p = _n = this;
  }

  /**
   * \brief Append item to a list.
   *
   * Convenience function for empty-head corner case.
   * \param head  Pointer to the current list head.
   * \param p     Pointer to new item.
   * \return the pointer to the new head.
   */
  template< typename C, typename N >
  static inline C *push_back(C *head, N *p) noexcept;

  /**
   * \brief Prepend item to a list.
   *
   * Convenience function for empty-head corner case.
   * \param head pointer to the current list head.
   * \param p pointer to new item.
   * \return the pointer to the new head.
   */
  template< typename C, typename N >
  static inline C *push_front(C *head, N *p) noexcept;

  /**
   * \brief Remove item from a list.
   *
   * Convenience function for remove-head corner case.
   * \param head pointer to the current list head.
   * \param p pointer to the item to remove.
   * \return the pointer to the new head.
   */
  template< typename C, typename N >
  static inline C *remove(C *head, N *p) noexcept;

private:
  List_item *_n, *_p;
};


/* IMPLEMENTATION -----------------------------------------------------------*/
template< typename C, typename N >
C *List_item::push_back(C *h, N *p) noexcept
{
  if (!p)
    return h;
  if (!h)
    return p;
  h->insert_prev_item(p);
  return h;
}

template< typename C, typename N >
C *List_item::push_front(C *h, N *p) noexcept
{
  if (!p)
    return h;
  if (h)
    h->insert_prev_item(p);
  return p;
}

template< typename C, typename N >
C *List_item::remove(C *h, N *p) noexcept
{
  if (!p)
    return h;
  if (!h)
    return 0;
  if (h == p)
    {
      if (p == p->_n)
	h = 0;
      else
        h = static_cast<C*>(p->_n);
    }
  p->remove_me();

  return h;
}

template< typename T, bool Poly >
inline
T *List_item::T_iter<T, Poly>::remove_me() noexcept
{ return cast_to_type(Iter::remove_me(), Int_to_type<P>()); }


template< typename T >
class T_list_item : public List_item
{
public:
  T *next() const { return static_cast<T*>(List_item::get_next_item()); }
  T *prev() const { return static_cast<T*>(List_item::get_prev_item()); }
};


template< typename LI >
class L_list
{
private:
  LI *_h;

public:

  L_list() : _h(0) {}

  void push_front(LI *e) { _h = LI::push_front(_h, e); }
  void push_back(LI *e) { _h = LI::push_back(_h, e); }
  void insert_before(LI *e, LI *p)
  {
    p->insert_prev_item(e);
    if (_h == p)
      _h = e;
  }
  void insert_after(LI *e, LI *p) { p->insert_next_item(e); }

  void remove(LI *e)
  { _h = LI::remove(_h, e); }

  LI *head() const { return _h; }
};

/**
 * Doubly linked list, with internal allocation.
 * Container for items of type D, implemented by a doubly linked list.
 * Alloc defines the allocator policy.
 */
template< typename D, template<typename A> class Alloc = New_allocator >
class List
{
private:
  class E : public List_item
  {
  public:
    E(D const &d) noexcept : data(d) {}
    D data;
  };

public:
  class Node : private E
  {};

  typedef Alloc<Node> Node_alloc;

  /**
   * Iterator.
   * Forward and backward iteratable.
   */
  class Iter
  {
  private:
    List_item::T_iter<E> _i;

  public:
    Iter(E *e) noexcept : _i(e) {}

    D &operator * () const noexcept { return (*_i)->data; }
    D &operator -> () const noexcept { return (*_i)->data; }

    Iter operator ++ (int) noexcept
    { Iter o = *this; operator ++ (); return o; }
    Iter operator -- (int) noexcept
    { Iter o = *this; operator -- (); return o; }
    Iter &operator ++ () noexcept { ++_i; return *this; }
    Iter &operator -- () noexcept { --_i; return *this; }

    /** operator for testing validity (syntactically equal to pointers) */
    operator E* () const noexcept { return *_i; }
  };

  List(Alloc<Node> const &a = Alloc<Node>()) noexcept : _h(0), _l(0), _a(a) {}

  /** Add element at the end of the list. */
  void push_back(D const &d) noexcept
  {
    void *n = _a.alloc();
    if (!n) return;
    _h = E::push_back(_h, new (n) E(d));
    ++_l;
  }

  /** Add element at the beginning of the list. */
  void push_front(D const &d) noexcept
  {
    void *n = _a.alloc();
    if (!n) return;
    _h = E::push_front(_h, new (n) E(d));
    ++_l;
  }

  /** Remove element pointed to by the iterator. */
  void remove(Iter const &i) noexcept
  { E *e = i; _h = E::remove(_h, e); --_l; _a.free(e); }

  /** Get the length of the list. */
  unsigned long size() const noexcept { return _l; }

  /** Random access. Complexity is O(n). */
  D const &operator [] (unsigned long idx) const noexcept
  { Iter i = _h; for (; idx && *i; ++i, --idx) { } return *i; }

  /** Random access. Complexity is O(n). */
  D &operator [] (unsigned long idx) noexcept
  { Iter i = _h; for (; idx && *i; ++i, --idx) { } return *i; }

  /** Get iterator for the list elements. */
  Iter items() noexcept { return Iter(_h); }

private:
  E *_h;
  unsigned _l;
  Alloc<Node> _a;
};


};

