// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2011 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 "bits/list_basics.h"
#include "type_traits"

namespace cxx {

class H_list_item
{
public:
  H_list_item() : _n(nullptr), _pn(nullptr) {}
  ~H_list_item() { l_remove(); }

private:
  H_list_item(H_list_item const &);
  void operator = (H_list_item const &);

  template<typename T, typename P> friend class H_list;
  template<typename T, typename X> friend struct Bits::Basic_list_policy;

  void l_remove()
  {
    if (!_pn)
      return;

    *_pn = _n;
    if (_n) 
      _n->_pn = _pn;

    _pn = nullptr;
  }

  H_list_item *_n, **_pn;
};

template< typename T >
struct H_list_item_t : H_list_item {};

/**
 * General double-linked list of unspecified cxx::H_list_item elements.
 *
 * Most of the time, you want to use H_list_t.
 */
template< typename T, typename POLICY = Bits::Basic_list_policy< T, H_list_item> >
class H_list : public Bits::Basic_list<POLICY>
{
private:
  typedef cxx::conditional_t<
    cxx::is_convertible_v<T, H_list_item_t<T> >,
    H_list_item_t<T>, H_list_item> Item;
  typedef Bits::Basic_list<POLICY> Base;
  H_list(H_list const &);
  void operator = (H_list const &);

public:
  typedef typename Base::Iterator Iterator;

  // BSS allocation
  explicit H_list(bool x) : Base(x) {}
  H_list() : Base() {}

  /**
   *  Return an iterator for an arbitrary list element
   *
   *  \param c  List element to start the iteration.
   *
   *  \return A mutable forward iterator.
   *
   *  \pre The element must be in a list.
   */
  static Iterator iter(T *c) { return Base::__iter(c->Item::_pn); }

  /// Check if the given element is currently part of a list.
  static bool in_list(T const *e) { return e->Item::_pn; }

  /// Add element to the front of the list.
  void add(T *e)
  {
    if (this->_f)
      this->_f->_pn = &e->Item::_n;
    e->Item::_n = this->_f;
    e->Item::_pn = &this->_f;
    this->_f = static_cast<Item*>(e);
  }

  /// Add element to the front of the list.
  void push_front(T *e) { add(e); }

  void insert(T *e, Iterator const &pred)
  {
    H_list_item **x = &this->_f;
    if (pred != Base::end())
      x = &(*pred)->_n;

    e->Item::_n = *x;

    if (*x)
      (*x)->_pn = &(e->Item::_n);

    e->Item::_pn = x;
    *x = static_cast<Item*>(e);
  }

  static void insert_before(T *e, Iterator const &succ)
  {
    H_list_item **x = Base::__get_internal(succ);

    e->Item::_n = *x;
    e->Item::_pn = x;

    if (*x)
      (*x)->_pn = &e->Item::_n;

    *x = static_cast<Item*>(e);
  }

  /**
   * Replace an element in a list with a new element.
   *
   * \param p  Element in list to be replaced.
   * \param e  Replacement element, must not yet be in a list.
   *
   * \pre `p` and `e` must not be NULL.
   *
   * After the operation the p element is no longer in the
   * list and may be reused.
   */
  static void replace(T *p, T *e)
  {
    e->Item::_n = p->Item::_n;
    e->Item::_pn = p->Item::_pn;
    *(p->Item::_pn) = static_cast<Item*>(e);
    if (e->Item::_n)
      e->Item::_n->_pn = &(e->Item::_n);

    p->Item::_pn = nullptr;
  }

  /**
   * Remove the given element from its list.
   *
   * \param e  Element to be removed. Must be in a list.
   */
  static void remove(T *e)
  { e->Item::l_remove(); }

  /**
   * Remove the element at the given iterator position.
   *
   * \param e  Iterator pointing to the element to be removed. Must not
   *           point to end().
   *
   * \return New iterator pointing to the element after the removed one.
   *
   * \note The hlist implementation guarantees that the original iterator
   *       is still valid after the element has been removed. In fact, the
   *       iterator returned is the same as the one supplied in the `e`
   *       parameter.
   */
  static Iterator erase(Iterator const &e)
  { e->Item::l_remove(); return e; }
};

template< typename T >
class H_list_bss : public H_list<T>
{
public:
  H_list_bss() : H_list<T>(true) {}
};

}
