
/**********************************************************//**
 **
 ** @file base/sharedimpl.h
 **
 ** Copyright (C) 2010  Xpace, LLC.  All rights reserved
 **
 ** www.xpace.net
 **
 **************************************************************/


#ifndef XPACE_SHAREDIMPL_H
#define XPACE_SHAREDIMPL_H

#ifdef _MSC_VER
// MS warns when you "export" a template definition
#pragma warning(disable: 4251)
#endif

#if TRACE_SHARE
#include <cstdio>
#include <typeinfo>
#endif

#include "sharedvoid.h"

namespace Xpace
{
  /// A pointer to a shared class object
  /// Copying the pointer calls T::clone()
  /// T must inherit SharedImpl
  template <typename T, bool cached = false>
  class SharedImplPointer
  {
  public:
    /// @param p the object to be wrapped
    explicit SharedImplPointer
	    (T* p);
    SharedImplPointer
	    ();
    SharedImplPointer
	    (const SharedImplPointer<T, cached> &rhs);
    SharedImplPointer& operator=
  	  (const SharedImplPointer<T, cached> &rhs);
    ~SharedImplPointer
      ();

    SharedImplPointer& operator=
      (T* p);

    /// @return true if pointer is null
    bool operator!
      ()
      const;

    /// @return a writable pointer
    T* data
	    (); 

    /// @return a non-writable pointer
    const T* data
	    ()
      const;

    /// @return a non-writable pointer (even if object is non-const)
    const T* constData
	    () 
      const;

    /// @return a writable pointer
    T* operator->
      ()
    { 
      return data(); 
    };

    /// @return a non-writable pointer
    const T* operator->
      ()
      const
    { 
      return constData(); 
    };

    #ifndef NDEBUG
    int getRefCount
      ()
    const;
    #endif

  private:
    SharedVoidPointer v;

    void _trace 
      (const char*) 
    #ifndef TRACE_SHARE
    {
    }
    #endif
    ;
  };

  /// A wrapper around a shared class object
  /// Used to store objects in containers (e.g. QCache)
  template<typename T>
  class WrapImplPointer
  {
  public:
    WrapImplPointer
      (SharedImplPointer<T, true> s) :
        si(s)
    {
    }

    operator SharedImplPointer<T, true>
      ()
    {
      return si;
    }

  private:
    SharedImplPointer<T, true> si;
  };

  // ============================================================
  // ============================================================
  // ============================================================

  #if TRACE_SHARE
  extern FILE* _trace_file;

  template <typename T>
  void SharedImplPointer<T>::_trace
    (const char* str)
  {
    if (!_trace_file)
      fopen_s(&_trace_file, "_TRACE_SHARE_", "wt");
    fprintf(_trace_file, "Impl %s: (%s) %p %u\n", str, typeid(T).name(), constData(), getRefCount());
    fflush(_trace_file);
  }
  #endif

  template <typename T, bool cached>
  inline
  SharedImplPointer<T, cached>::SharedImplPointer
    (T* p) :
      v(p)
  {
    _trace("Ctor");
  }

  template <typename T, bool cached>
  inline
  SharedImplPointer<T, cached>::SharedImplPointer
    ()
  {
  }

  template <typename T, bool cached>
  inline
  SharedImplPointer<T, cached>::SharedImplPointer
    (const SharedImplPointer<T, cached> &rhs) :
      v(rhs.v)
  {
    _trace("Copy");
  }

  template <typename T, bool cached>
  inline
  SharedImplPointer<T, cached>::~SharedImplPointer
    ()
  {
    _trace("Dtor");
  }

  template <typename T, bool cached>
  inline
  SharedImplPointer<T, cached>& SharedImplPointer<T, cached>::operator=
    (const SharedImplPointer<T, cached> &rhs)
  {
    _trace("operator=");
    v = rhs.v;
    return *this;
  }

  template <typename T, bool cached>
  inline
  SharedImplPointer<T, cached>& SharedImplPointer<T, cached>::operator=
    (T* p)
  {
    _trace("operator=");
    v = p;
    return *this;
  }

  template <typename T, bool cached>
  inline
  bool SharedImplPointer<T, cached>::operator!
    ()
    const
  {
    return !v;
  }

  template <typename T, bool cached>
  inline  
  T* SharedImplPointer<T, cached>::data
    ()
  { 
    return static_cast<T*>(v.data(cached));
  }

  template <typename T, bool cached>
  inline
  const T* SharedImplPointer<T, cached>::data
    ()
    const
  { 
    return static_cast<const T*>(v.constData()); 
  }

  template <typename T, bool cached>
  inline
  const T* SharedImplPointer<T, cached>::constData
    () 
    const
  { 
    return static_cast<const T*>(v.constData()); 
  }

  #ifndef NDEBUG
  template <typename T, bool cached>
  inline
  int SharedImplPointer<T, cached>::getRefCount
    ()
    const
  {
    return v.getRefCount();
  }
  #endif

  class XPACE_EXPORT _impl_ref
  {
  public :
    _impl_ref
      () :
        ref(0)
    {
    };

    _impl_ref
      (const _impl_ref& /*rhs*/) :
        ref(0)
    {
    };

    int ref;
  };

  class XPACE_EXPORT SharedImpl : public _impl_ref
  {
  public :
    virtual ~SharedImpl
      ()
    = 0;
    virtual SharedImpl* clone
      ()
      const
    = 0;

    #ifndef NDEBUG
    static
    void test
      ();
    #endif
  };

  #define DECLARE_IMPL_COMMON(className, cached)                   \
  public :                                                         \
    class Impl;                                                    \
    className() {};                                                \
    className& operator=(const className& rhs)                     \
      { si = rhs.si; return *this; };                              \
    friend class Impl;                                             \
    SharedImplPointer<Impl, cached> si;                            \
    className(Impl* i) : si(i) {};                                 \
    Impl* impl()                                                   \
      { return static_cast<Impl*>(si.data()); };             \
    const Impl* impl() const                                       \
      { return static_cast<const Impl*>(si.data()); };       \
    const Impl* constImpl() const                                  \
      { return static_cast<const Impl*>(si.constData()); };

  #define DECLARE_IMPL(className)                       \
    DECLARE_IMPL_COMMON(className, false)               \
    className(const className& rhs) :                   \
      si(rhs.si) {}

  #define DECLARE_IMPL_BASE(className, base)            \
    DECLARE_IMPL_COMMON(className, false)               \
    className(const className& rhs) :                   \
      base(), si(rhs.si) {}

  #define DECLARE_CACHED_IMPL(className)                \
    DECLARE_IMPL_COMMON(className, true)                \
    className(const className& rhs) :                   \
      si(rhs.si) {}

  #define DECLARE_CACHED_IMPL_BASE(className, base)     \
    DECLARE_IMPL_COMMON(className, true)                \
    className(const className& rhs) :                   \
      base(), si(rhs.si) {}

  #define DECLARE_IMPL_NAME(className, imp)                         \
  public :                                                          \
    class Impl;                                                     \
  private :                                                         \
    friend class Impl;                                              \
    SharedImplPointer<Impl> imp##_Impl;                             \
    Impl* imp##_impl()                                              \
      { return (Impl*) imp##_Impl.data(); };                        \
    const Impl* imp##_impl() const                                  \
      { return static_cast<const Impl*>(imp##_Impl.data()); };      \
    const Impl* imp##_constImpl() const                             \
      { return static_cast<const Impl*>(imp##_Impl.constData()); }  

  #define ADD_IMPL_NAME(className, imp)                             \
  private :                                                         \
    SharedImplPointer<Impl> imp##_Impl;                             \
    Impl* imp##_impl()                                              \
      { return static_cast<Impl*>(imp##_Impl.data()); };            \
    const Impl* imp##_impl() const                                  \
      { return static_cast<const Impl*>(imp##_Impl.data()); };      \
    const Impl* imp##_constImpl() const                             \
      { return static_cast<const Impl*>(imp##_Impl.constData()); }  

  #define ADD_CONST_IMPL_NAME(className, imp)                       \
  private :                                                         \
    const SharedImplPointer<Impl> imp##_Impl;                       \
    Impl* imp##_impl()                                              \
      { return static_cast<Impl*>(imp##_Impl.data()); };            \
    const Impl* imp##_impl() const                                  \
      { return static_cast<const Impl*>(imp##_Impl.data()); };      \
    const Impl* imp##_constImpl() const                             \
      { return static_cast<const Impl*>(imp##_Impl.constData()); }  
};

#endif
