// file      : odb/lazy-ptr-impl.hxx
// license   : GNU GPL v2; see accompanying LICENSE file

#ifndef ODB_LAZY_PTR_IMPL_HXX
#define ODB_LAZY_PTR_IMPL_HXX

#include <odb/pre.hxx>

#include <utility> // std::move

#include <odb/forward.hxx> // odb::database
#include <odb/traits.hxx>

namespace odb
{
  class lazy_ptr_base
  {
  public:
    typedef odb::database database_type;

    ~lazy_ptr_base ();
    lazy_ptr_base () noexcept;

    // Note: noexcept is not specified since it can throw while allocating
    // id_.
    //
    lazy_ptr_base (const lazy_ptr_base&);
    lazy_ptr_base& operator= (const lazy_ptr_base&);

    // C++11 support.
    //
  public:
    lazy_ptr_base (lazy_ptr_base&&) noexcept;

    lazy_ptr_base&
    operator= (lazy_ptr_base&&) noexcept;

  public:
    // Reset both the id and database.
    //
    void
    reset () noexcept;

    // Reset the id.
    //
    void
    reset_id () noexcept;

    void
    swap (lazy_ptr_base&) noexcept;

    database_type*
    database () const;

    explicit operator bool () const noexcept
    {
      return db_ != 0 ? &lazy_ptr_base::id_ : 0;
    }

  protected:

#ifdef __cpp_noexcept_function_type
    typedef void (*free_func) (void*) noexcept;
#else
    typedef void (*free_func) (void*);
#endif

    typedef void* (*copy_func) (const void*);

    // Makes a copy of id.
    //
    void
    reset_ (database_type*,
            void* loader,
            const void* id,
            free_func, copy_func);

    template <typename T>
    static void
    free (void*) noexcept;

    template <typename T>
    static void*
    copy (const void*);

    template <typename T, typename DB>
    static typename object_traits<T>::pointer_type
    loader (database_type&, const typename object_traits<T>::id_type&);

  protected:
    void* id_;
    database_type* db_;
    void* loader_;

  private:
    free_func free_;
    copy_func copy_;
  };

  template <typename T>
  class lazy_ptr_impl: public lazy_ptr_base
  {
  public:
    lazy_ptr_impl () noexcept;

    template <typename DB, typename ID>
    lazy_ptr_impl (DB&, const ID&);

    // Note: noexcept is not specified since it can throw while calling the
    // base type copy constructor (see lazy_ptr_base for details).
    //
    lazy_ptr_impl (const lazy_ptr_impl&);
    template <typename Y> lazy_ptr_impl (const lazy_ptr_impl<Y>&);

    // Note: noexcept is not specified since it can throw while calling the
    // base type copy assignment operator (see lazy_ptr_base for details).
    //
    lazy_ptr_impl& operator= (const lazy_ptr_impl&);
    template <typename Y> lazy_ptr_impl& operator= (const lazy_ptr_impl<Y>&);

    // C++11 support.
    //
  public:
    lazy_ptr_impl (lazy_ptr_impl&&) noexcept;

    template <typename Y>
    lazy_ptr_impl (lazy_ptr_impl<Y>&&) noexcept;

    lazy_ptr_impl&
    operator= (lazy_ptr_impl&&) noexcept;

    template <typename Y>
    lazy_ptr_impl&
    operator= (lazy_ptr_impl<Y>&&) noexcept;

  public:
    using lazy_ptr_base::reset;
    using lazy_ptr_base::reset_id;

    template <typename DB, typename ID>
    void
    reset (DB&, const ID&);

    // Reset the id and set the database to the new value.
    //
    template <typename DB>
    void
    reset_db (DB&);

    template <typename ID>
    void
    reset_id (const ID&);

    template <typename O /* = T */>
    typename object_traits<O>::pointer_type
    load (bool reset_id);

    template <typename O /* = T */>
    typename object_traits<O>::id_type
    object_id () const;
  };
}

#include <odb/lazy-ptr-impl.ixx>
#include <odb/lazy-ptr-impl.txx>

#include <odb/post.hxx>

#endif // ODB_LAZY_PTR_IMPL_HXX
