// vim:set ft=cpp: -*- Mode: C++ -*-
/**
 * \file
 *
 */
/*
 * (c) 2017 Alexander Warg <alexander.warg@kernkonzept.com>
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */

#pragma once

#include <l4/sys/capability>

namespace L4 { namespace Detail {

template< typename T, typename IMPL >
class Smart_cap_base : public Cap_base, protected IMPL
{
protected:
  template<typename X>
  static IMPL &impl(Smart_cap_base<X, IMPL> &o) { return o; }

  template<typename X>
  static IMPL const &impl(Smart_cap_base<X, IMPL> const &o) { return o; }

public:
  template<typename X, typename I>
  friend class ::L4::Detail::Smart_cap_base;

  Smart_cap_base(Smart_cap_base const &) = delete;
  Smart_cap_base &operator = (Smart_cap_base const &) = delete;

  Smart_cap_base() noexcept : Cap_base(Invalid) {}

  explicit Smart_cap_base(Cap_base::Cap_type t) noexcept
  : Cap_base(t)
  {}

  template<typename O>
  explicit constexpr Smart_cap_base(Cap<O> c) noexcept
  : Cap_base(c.cap())
  {}

  template<typename O>
  explicit constexpr Smart_cap_base(Cap<O> c, IMPL const &impl) noexcept
  : Cap_base(c.cap()), IMPL(impl)
  {}

  Cap<T> release() noexcept
  {
    l4_cap_idx_t c = this->cap();
    IMPL::invalidate(*this);
    return Cap<T>(c);
  }

  void reset()
  { IMPL::free(*this); }

  Cap<T> operator -> () const noexcept { return Cap<T>(this->cap()); }
  Cap<T> get() const noexcept { return Cap<T>(this->cap()); }
  ~Smart_cap_base() noexcept { IMPL::free(*this); }
};


template< typename T, typename IMPL >
class Unique_cap_impl final :  public Smart_cap_base<T, IMPL>
{
private:
  typedef Smart_cap_base<T, IMPL> Base;

public:
  using Base::Base;
  Unique_cap_impl() noexcept = default;

  Unique_cap_impl(Unique_cap_impl &&o) noexcept
  : Base(o.release(), Base::impl(o))
  {}

  template<typename O>
  Unique_cap_impl(Unique_cap_impl<O, IMPL> &&o) noexcept
  : Base(o.release(), Base::impl(o))
  { Cap<T>::template check_convertible_from<O>(); }

  Unique_cap_impl &operator = (Unique_cap_impl &&o) noexcept
  {
    if (&o == this)
      return *this;

    IMPL::free(*this);
    this->_c = o.release().cap();
    this->IMPL::operator = (Base::impl(o));
    return *this;
  }

  template<typename O>
  Unique_cap_impl &operator = (Unique_cap_impl<O, IMPL> &&o) noexcept
  {
    Cap<T>::template check_convertible_from<O>();

    IMPL::free(*this);
    this->_c = o.release().cap();
    this->IMPL::operator = (Base::impl(o));
    return *this;
  }
};

template<typename T, typename IMPL>
class Shared_cap_impl final : public Smart_cap_base<T, IMPL>
{
private:
  typedef Smart_cap_base<T, IMPL> Base;

public:
  using Base::Base;
  Shared_cap_impl() noexcept = default;

  Shared_cap_impl(Shared_cap_impl &&o) noexcept
  : Base(o.release(), Base::impl(o))
  {}

  template<typename O>
  Shared_cap_impl(Shared_cap_impl<O, IMPL> &&o) noexcept
  : Base(o.release(), Base::impl(o))
  { Cap<T>::template check_convertible_from<O>(); }

  Shared_cap_impl &operator = (Shared_cap_impl &&o) noexcept
  {
    if (&o == this)
      return *this;

    IMPL::free(*this);
    this->_c = o.release().cap();
    this->IMPL::operator = (Base::impl(o));
    return *this;
  }

  template<typename O>
  Shared_cap_impl &operator = (Shared_cap_impl<O, IMPL> &&o) noexcept
  {
    Cap<T>::template check_convertible_from<O>();

    IMPL::free(*this);
    this->_c = o.release().cap();
    this->IMPL::operator = (Base::impl(o));
    return *this;
  }

  Shared_cap_impl(Shared_cap_impl const &o) noexcept
  : Base()
  {
    this->IMPL::operator = (Base::impl(o));
    this->_c = IMPL::copy(o).cap();
  }

  template<typename O>
  Shared_cap_impl(Shared_cap_impl<O, IMPL> const &o) noexcept
  : Base()
  {
    Cap<T>::template check_convertible_from<O>();
    this->IMPL::operator = (Base::impl(o));
    this->_c = IMPL::copy(o).cap();
  }

  Shared_cap_impl &operator = (Shared_cap_impl const &o) noexcept
  {
    if (&o == this)
      return *this;

    IMPL::free(*this);
    this->IMPL::operator = (static_cast<IMPL const &>(o));
    this->_c = this->IMPL::copy(o).cap();
    return *this;
  }

  template<typename O>
  Shared_cap_impl &operator = (Shared_cap_impl<O, IMPL> const &o) noexcept
  {
    Cap<T>::template check_convertible_from<O>();
    IMPL::free(*this);
    this->IMPL::operator = (static_cast<IMPL const &>(o));
    this->_c = this->IMPL::copy(o).cap();
    return *this;
  }
};

}} // L4::Detail
