// vim: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 "type_traits"
#include <stddef.h>
#include <l4/sys/compiler.h>

namespace cxx {

template< typename T >
struct Default_ref_counter
{
  void h_drop_ref(T *p) noexcept
  {
    if (p->remove_ref() == 0)
      delete p;
  }

  void h_take_ref(T *p) noexcept
  {
    p->add_ref();
  }
};

struct Ref_ptr_base
{
  enum Default_value
  { Nil = 0 };
};

template<typename T, template< typename X > class CNT = Default_ref_counter>
class Weak_ptr;

/**
 * A reference-counting pointer with automatic cleanup.
 *
 * \tparam T   Type of object the pointer points to.
 * \tparam CNT Type of management class that manages the life time of
 *             the object.
 *
 * This pointer is similar to the standard C++-11 shared_ptr but it does
 * the reference counting directly in the object being pointed to, so that
 * no additional management structures need to be allocated from the heap.
 *
 * Classes that use this pointer type must implement two functions:
 *
 *     int remove_ref()
 *
 * is called when a reference is removed and must return 0 when there are no
 * further references to the object.
 *
 *     void add_ref()
 *
 * is called when another ref_ptr to the object is created.
 *
 * Ref_obj provides a simple implementation of this interface from which
 * classes may inherit.
 */
template <
  typename T = void,
  template< typename X > class CNT = Default_ref_counter
>
class Ref_ptr : public Ref_ptr_base, private CNT<T>
{
private:
  typedef decltype(nullptr) Null_type;
  typedef Weak_ptr<T, CNT> Wp;

public:
  /// Default constructor creates a pointer with no managed object.
  Ref_ptr() noexcept : _p(0) {}

  Ref_ptr(Ref_ptr_base::Default_value v)
  : _p(reinterpret_cast<T*>(static_cast<unsigned long>(v))) {}

  /**
   * Create a shared pointer from a weak pointer.
   *
   * Increases references.
   */
  Ref_ptr(Wp const &o) noexcept : _p(o.ptr())
  { __take_ref(); }

  /// allow creation from `nullptr`
  Ref_ptr(decltype(nullptr) n) noexcept : _p(n) {}

  /**
   * Create a shared pointer from a raw pointer.
   *
   * In contrast to C++11 shared_ptr it is safe to use this constructor
   * multiple times and have the same reference counter.
   */
  template<typename X>
  explicit Ref_ptr(X *o) noexcept : _p(o)
  { __take_ref(); }

  /**
   * Create a shared pointer from a raw pointer without creating a new
   * reference.
   *
   * \param o  Pointer to the object.
   * \param d  Dummy parameter to select this constructor at compile time.
   *           The value may be true or false.
   *
   * This is the counterpart to release().
   */
  Ref_ptr(T *o, [[maybe_unused]] bool d) noexcept : _p(o) { }

  /**
   * Return a raw pointer to the object this shared pointer points to.
   *
   * This does not release the pointer or decrease the reference count.
   */
  T *get() const noexcept
  {
    return _p;
  }

  /** \copydoc get() */
  T *ptr() const noexcept
  {
    return _p;
  }

  /**
   * Release the shared pointer without removing the reference.
   *
   * \return A raw pointer to the managed object.
   *
   */
  T *release() noexcept
  {
    T *p = _p;
    _p = 0;
    return p;
  }

  ~Ref_ptr() noexcept
  { __drop_ref(); }

  template<typename OT>
  Ref_ptr(Ref_ptr<OT, CNT> const &o) noexcept
  {
    _p = o.ptr();
    __take_ref();
  }

  Ref_ptr(Ref_ptr<T> const &o) noexcept
  {
    _p = o._p;
    __take_ref();
  }

  template< typename OT >
  void operator = (Ref_ptr<OT> const &o) noexcept
  {
    __drop_ref();
    _p = o.ptr();
    __take_ref();
  }

  void operator = (Ref_ptr<T> const &o) noexcept
  {
    if (&o == this)
      return;

    __drop_ref();
    _p = o._p;
    __take_ref();
  }

  void operator = (Null_type) noexcept
  {
    __drop_ref();
    _p = 0;
  }

  template<typename OT>
  Ref_ptr(Ref_ptr<OT, CNT> &&o) noexcept
  { _p = o.release(); }

  Ref_ptr(Ref_ptr<T> &&o) noexcept
  { _p = o.release(); }

  template< typename OT >
  void operator = (Ref_ptr<OT> &&o) noexcept
  {
    __drop_ref();
    _p = o.release();
  }

  void operator = (Ref_ptr<T> &&o) noexcept
  {
    if (&o == this)
      return;

    __drop_ref();
    _p = o.release();
  }

  [[nodiscard]] explicit operator bool () const noexcept { return _p; }

  T *operator -> () const noexcept
  { return _p; }

  [[nodiscard]] bool operator == (Ref_ptr const &o) const noexcept
  { return _p == o._p; }

  [[nodiscard]] bool operator != (Ref_ptr const &o) const noexcept
  { return _p != o._p; }

  [[nodiscard]] bool operator < (Ref_ptr const &o) const noexcept
  { return _p < o._p; }

  [[nodiscard]] bool operator <= (Ref_ptr const &o) const noexcept
  { return _p <= o._p; }

  [[nodiscard]] bool operator > (Ref_ptr const &o) const noexcept
  { return _p > o._p; }

  [[nodiscard]] bool operator >= (Ref_ptr const &o) const noexcept
  { return _p >= o._p; }

  [[nodiscard]] bool operator == (T const *o) const noexcept
  { return _p == o; }

  [[nodiscard]] bool operator < (T const *o) const noexcept
  { return _p < o; }

  [[nodiscard]] bool operator <= (T const *o) const noexcept
  { return _p <= o; }

  [[nodiscard]] bool operator > (T const *o) const noexcept
  { return _p > o; }

  [[nodiscard]] bool operator >= (T const *o) const noexcept
  { return _p >= o; }

private:
  void __drop_ref() noexcept
  {
    if (_p)
      static_cast<CNT<T>*>(this)->h_drop_ref(_p);
  }

  void __take_ref() noexcept
  {
    if (_p)
      static_cast<CNT<T>*>(this)->h_take_ref(_p);
  }

  T *_p;
};


template<typename T, template< typename X > class CNT>
class Weak_ptr
{
private:
  struct Null_type;
  typedef Ref_ptr<T, CNT> Rp;

public:
  Weak_ptr() = default;
  Weak_ptr(decltype(nullptr)) : _p(nullptr) {}
  // backwards 0 ctor
  explicit Weak_ptr(int x) noexcept
  L4_DEPRECATED("Use initialization from 'nullptr'")
  : _p(nullptr)
  { if (x != 0) __builtin_trap(); }

  Weak_ptr(Rp const &o) noexcept : _p(o.ptr()) {}
  explicit Weak_ptr(T *o) noexcept : _p(o) {}

  template<typename OT>
  Weak_ptr(Weak_ptr<OT, CNT> const &o) noexcept : _p(o.ptr()) {}

  Weak_ptr(Weak_ptr<T, CNT> const &o) noexcept : _p(o._p) {}

  Weak_ptr<T, CNT> &operator = (const Weak_ptr<T, CNT> &o) = default;

  T *get() const noexcept { return _p; }
  T *ptr() const noexcept { return _p; }

  T *operator -> () const noexcept { return _p; }
  operator Null_type const * () const noexcept
  { return reinterpret_cast<Null_type const*>(_p); }

private:
  T *_p;
};

template<typename OT, typename T> inline
Ref_ptr<OT> ref_ptr_static_cast(Ref_ptr<T> const &o)
{ return ref_ptr(static_cast<OT*>(o.ptr())); }

template< typename T >
inline Ref_ptr<T> ref_ptr(T *t)
{ return Ref_ptr<T>(t); }

template< typename T >
inline Weak_ptr<T> weak_ptr(T *t)
{ return Weak_ptr<T>(t); }


class Ref_obj
{
private:
  mutable int _ref_cnt;

public:
  Ref_obj() : _ref_cnt(0)  {}
  void add_ref() const noexcept { ++_ref_cnt; }
  int remove_ref() const noexcept { return --_ref_cnt; }
};

template< typename T, typename... Args >
Ref_ptr<T>
make_ref_obj(Args &&... args)
{ return cxx::Ref_ptr<T>(new T(cxx::forward<Args>(args)...)); }

template<typename T, typename U>
Ref_ptr<T>
dynamic_pointer_cast(Ref_ptr<U> const &p) noexcept
{
  // our constructor from a naked pointer increments the counter
  return Ref_ptr<T>(dynamic_cast<T *>(p.get()));
}

template<typename T, typename U>
Ref_ptr<T>
static_pointer_cast(Ref_ptr<U> const &p) noexcept
{
  // our constructor from a naked pointer increments the counter
  return Ref_ptr<T>(static_cast<T *>(p.get()));
}

}
