/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/02/02
/// 
/// @brief  COM用スマートポインタ？
/// 
/// ****************************************************************************

#pragma once

#include "CPL_Core_PointerBase.h"


#ifdef _MSC_VER
  #pragma warning(push)
  #pragma warning( disable : 4521 ) // 複数のコピーコンストラクタの警告を解除
  #pragma warning( disable : 4522 ) // 複数の代入演算子の警告を解除
#endif


namespace CPL {
namespace Core {



// _____________________________________________________________________________
/// @brief COM用スマートポインタ
///
// *****************************************************************************
template<class T_Type>
class ComPtr
{
private:
  using type_ptr       = T_Type*;
  using type_cptr      = T_Type const*;
  using type_ref       = T_Type&;
  using type_cref      = T_Type const&;
  using type_this      = ComPtr<T_Type>;
  using type_this_ptr  = type_this*;
  using type_this_cptr = type_this const*;
  using type_this_ref  = type_this&;
  using type_this_cref = type_this const&;

  template<class T> friend class ComPtr;


  template<class T = T_Type*>
  T get(){ return static_cast<T>(m_ptr); }

  template<class T = T_Type*>
  T get() const { return static_cast<T>(m_ptr); }

protected:
  T_Type* m_ptr = nullptr;

#ifdef CPL_DBGSW_ALLOCATOR
  friend void CPL::Core::Memory::allocate_dump();

  struct dbgId_ {
    int no;
    dbgId_( int x = -1 ) : no(x)
    {}
    dbgId_( dbgId_ const& a ) : no(a.no)
    {}
    operator int() const { return no; }
  };
  using dbgIdMap_   = std::map<void const*,dbgId_>;
  using dbgIdMapSp_ = std::shared_ptr<dbgIdMap_>;
  CPL_SINGLETON_INIT( dbgIdMapSp_, sm_id_bank, new dbgIdMap_ )

  int         m_dbg_ID{-1};
  dbgIdMapSp_ m_sp_id_bank{ sm_id_bank() };

  void zzf_dbgSetID_()
  {
    static int counter_ = 0;
    static int old_id_  = -1;
    static void const* old_key = (void const*)-1;
    auto const key = (void const*)m_ptr;
    if( key == old_key )
    {
      m_dbg_ID = old_id_;
    }
    else
    {
      auto& bank = *sm_id_bank();
      auto& id   = bank[ key ];
      if( id == -1 )
      {
        id = counter_++;
      }
      old_id_  = 
      m_dbg_ID = id;
    }
  }

#else
  void zzf_dbgSetID_()
  {}
#endif


  type_this_ptr  zzf_this_()       noexcept { return this; }
  type_this_cptr zzf_this_() const noexcept { return this; }

public:
  ComPtr(){}
  explicit
  ComPtr( type_ptr in_ptr )
    : m_ptr( in_ptr )
  {
    AddRef();
  }
  template<class T> explicit
  ComPtr( T* in_ptr )
    : m_ptr( SmartCast<type_ptr>(in_ptr) )
  {
    AddRef();
  }
  ComPtr( cref<ComPtr> in_sptr )
  {
    *this = in_sptr; // AddRefを呼び出すため。
  }
  ComPtr( rvref<ComPtr> in_sptr )
  {
    m_ptr = in_sptr.m_ptr;
    in_sptr.m_ptr = nullptr;
  }
  template<class T>
  ComPtr( _in<ComPtr<T>> in_sptr )
  {
    *this = in_sptr; // AddRefを呼び出すため。
  }
  ~ComPtr()
  {
    Release();
  }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void AddRef() noexcept
  {
    if( m_ptr )
    {
#ifdef CPL_DBGSW_DUMP_COMPTR_COUNT
      auto cnt = m_ptr->AddRef();
#else
      m_ptr->AddRef();
#endif

#ifdef CPL_DBGSW_ALLOCATOR

      zzf_dbgSetID_();

#ifdef CPL_DBGSW_DUMP_COMPTR_COUNT
#if defined CPL_PLATFORM_64
      CPL_DBG_OUTPUT_A
      ( "[T:%8d]:ComPtr::AddRef ( adr(0x%016I64x) ref_cnt(%04d) call_ID(%d) )\n",
        (int)GetThreadID(),
        (U64)m_ptr,
        (int)cnt,
        this->m_dbg_ID );
#else
      CPL_DBG_OUTPUT_A
      ( "[T:%8d]:ComPtr::AddRef ( adr(0x%08x) ref_cnt(%04d)  call_ID(%d) )\n",
        (int)GetThreadID(),
        (U32)m_ptr,
        (int)cnt,
        this->m_dbg_ID );
#endif
#endif
#endif
    }
  }


  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  T_Type* _get() const { return m_ptr; }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void Attach( type_ptr in_ptr )
  {
    if( m_ptr == in_ptr ) return;
    Release();
    m_ptr = in_ptr;
    zzf_dbgSetID_();
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void Release()
  {
    if( m_ptr )
    {
#ifdef CPL_DBGSW_DUMP_COMPTR_COUNT
      auto cnt = m_ptr->Release();
#else
      m_ptr->Release();
#endif

#ifdef CPL_DBGSW_ALLOCATOR
      if( m_dbg_ID == -1 )
      {
        zzf_dbgSetID_();
      }
#ifdef CPL_DBGSW_DUMP_COMPTR_COUNT
#if defined CPL_PLATFORM_64
      CPL_DBG_OUTPUT_A
      ( "[T:%8d]:ComPtr::Release( adr(0x%016I64x) ref_cnt(%04d) call_ID(%d) )\n",
        (int)GetThreadID(),
        (U64)m_ptr,
        (int)cnt,
        this->m_dbg_ID );
#else
      CPL_DBG_OUTPUT_A
      ( "[T:%8d]:ComPtr::Release( adr(0x%08x) ref_cnt(%04d)  call_ID(%d) )\n",
        (int)GetThreadID(),
        (U32)m_ptr,
        (int)cnt,
        this->m_dbg_ID );
#endif
#endif
#endif
      m_ptr = nullptr;
    }
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  type_this_ref operator = ( type_this&& in_sptr )
  {
    if( m_ptr != in_sptr.m_ptr )
    {
      Release();
      m_ptr = in_sptr.m_ptr;
    }
    in_sptr.m_ptr = nullptr;

    zzf_dbgSetID_();
    return *this;
  }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  type_this_ref operator = ( ComPtr<T>&& in_sptr )
  {
    if( m_ptr != in_sptr.m_ptr )
    {
      Release();
      m_ptr = in_sptr.m_ptr;
    }
    in_sptr.m_ptr = nullptr;

    zzf_dbgSetID_();

    return *this;
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  type_this_ref operator = ( type_this_cref in_sptr )
  {
    if( this == in_sptr.zzf_this_() )
    {
      return *this;
    }
    if( in_sptr.m_ptr != m_ptr )
    {
      Release();
      m_ptr = in_sptr.m_ptr;
    }
    AddRef();
    return *this;
  }
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  type_this_ref operator = ( ComPtr<T> const& in_sptr )
  {
    if( in_sptr.m_ptr != m_ptr )
    {
      Release();
      m_ptr = in_sptr.m_ptr;
    }
    AddRef();
    return *this;
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  type_this_ref operator = ( type_ptr in_ptr )
  {
    if( in_ptr != m_ptr )
    {
      Release();
      m_ptr = in_ptr;
    }
    AddRef();
    return *this;
  }
    
  //void operator = ( const int null ) noexcept
  //{
  //  Release();
  //}

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  type_ptr  operator ->() const noexcept { return get<type_ptr>(); }
  type_ref  operator  *() const noexcept { return *get<type_ptr>(); }
  type_ptr*       operator &()       noexcept { return &(type_ptr&)(m_ptr); }
  type_ptr const* operator &() const noexcept { return &(type_ptr&)(m_ptr); }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // ==
  bool operator == ( type_this_cref in_sptr ) const noexcept
  {
    return m_ptr == in_sptr.m_ptr;
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  bool operator == ( _in<T> in_ptr ) const noexcept
  {
    return m_ptr == static_cast<const T_Type*>(in_ptr);
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool operator == ( nullptr_t ) const noexcept
  {
    return m_ptr == nullptr;
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // !=
  bool operator != ( type_this_cref in_sptr ) const noexcept
  {
    return m_ptr != in_sptr.m_ptr;
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  template<class T>
  bool operator != ( _in<T> in_ptr ) const noexcept
  {
    return m_ptr == static_cast<const T_Type*>(in_ptr);
  }


  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool operator != ( nullptr_t ) const noexcept
  {
    return m_ptr != nullptr;
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool operator !() const noexcept
  {
    return m_ptr == nullptr;
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  operator bool() const noexcept
  {
    return !(operator!());
  }

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  operator type_ptr() const { return get<type_ptr>(); }
};


//namespace sp {
//
//
//// =============================================================================
//// COM用
//// =============================================================================
//template<class T_Type>
//class Com {
//  typedef T_Type*       PTR;
//  typedef T_Type const* CPTR;
//
//  friend class Com;
//  PTR m_ptr;
//    
//  Com*       zzf_this_()       noexcept { return this; }
//  Com const* zzf_this_() const noexcept { return this; }
//
//public:
//  Com() : m_ptr( nullptr ) {}
//  explicit
//  Com( PTR ptr ) : m_ptr( ptr )
//  {
//    // 渡されるポインタは既にAddRef()されているから、ここでAddRefを呼び出してはいけない
//    // …という仕様であったが、暗黙の変換を許可するように修正。
//    // それに伴い、このコンストラクタでAddRefを呼び出すように修正。
//    // 変わりに、参照カウンタを増加しないポインタ渡し用SetPointer()関数を追加。
//    // 修正：2012/03/01
//    // AddRef();
//    // ↑という修正をまた修正 : 2012/03/13
//  }
//  template<class T> explicit
//  Com( T* ptr ) : m_ptr( SmartCast<PTR>(ptr) ) {}
//  Com( const Com& sptr ) : m_ptr( nullptr )
//  {
//    *this = sptr; // AddRefを呼び出すため。
//  }
//  //Com( nullptr const& in_null )
//  //  : m_ptr( nullptr ){}
//    
//  ~Com() noexcept
//  {
//    Release();
//  }
//
//  //void SetPointer( PTR in_pointer ) noexcept
//  //{
//  //  if( m_ptr != in_pointer )
//  //  {
//  //    CPL_SAFE_PTR(m_ptr)->Release();
//  //    m_ptr = in_pointer;
//  //  }
//  //}
//
//  void AddRef() noexcept
//  {
//    CPL_SAFE_PTR(m_ptr)->AddRef();
//  }
//    
//  void Release() noexcept
//  {
//    RELEASE( m_ptr );
//  }
//
//  void operator = ( const Com& in_sptr ) noexcept
//  {
//    if( this == in_sptr.zzf_this_() )
//    {
//      return;
//    }
//    if( in_sptr.m_ptr != m_ptr )
//    {
//      Release();
//      m_ptr = in_sptr.m_ptr;
//    }
//    AddRef();
//  }
//
//  void operator = ( PTR in_ptr ) noexcept
//  {
//    if( in_ptr != m_ptr )
//    {
//      Release();
//      m_ptr = in_ptr;
//    }
//    AddRef();
//  }
//    
//  //void operator = ( const int null ) noexcept
//  //{
//  //  Release();
//  //}
//
//  PTR        operator ->() const noexcept { return m_ptr; }
//  PTR*       operator  &()       noexcept { return &m_ptr; }
//  PTR const* operator  &() const noexcept { return &m_ptr; }
//  T_Type&    operator  *() const noexcept { return *m_ptr; }
//
//  // ==
//  bool operator == ( const Com& in_sptr ) const noexcept
//  {
//    return m_ptr == in_sptr.m_ptr;
//  }
//
//  template<class T>
//  bool operator == ( const T& in_ptr ) const noexcept
//  {
//    return m_ptr == static_cast<const T_Type*>(in_ptr);
//  }
//
//  bool operator == ( int null ) const noexcept
//  {
//    return m_ptr == nullptr;
//  }
//  // !=
//  bool operator != ( const Com& in_sptr ) const noexcept
//  {
//    return m_ptr != in_sptr.m_ptr;
//  }
//
//  template<class T>
//  bool operator != ( const T& in_ptr ) const noexcept
//  {
//    return m_ptr == static_cast<const T_Type*>(in_ptr);
//  }
//    
//  bool operator != ( int null ) const noexcept
//  {
//    return m_ptr != nullptr;
//  }
//
//  bool operator !() const noexcept
//  {
//    return m_ptr == nullptr;
//  }
//    
//  operator bool() const noexcept
//  {
//    return !(operator!());
//  }
//
//  operator PTR() const { return m_ptr; }
//};
//
//
//
//} // namespace sp
} // namespace Core
} // namespace CPL


#ifdef _MSC_VER
  #pragma warning(pop) // 元へ
#endif
