#pragma once
/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2015/01/27 - 2015/02/26
/// 
/// @brief  ミューテックス関連
/// 
/// 
/// ****************************************************************************

#include <mutex>
#include <atomic>
#include "CPL_Core_Define.h"
#include "CPL_Core_Time.h"
#include "CPL_Core_Exception.h"

namespace CPL {
namespace Core {

/// @cond
namespace zzz_ {
  typedef size_t(*FuncThreadID)();

  // CorePLiオリジナルのスレッドID返却関数
  inline size_t getThreadID_()
  {
    static thread_local size_t tid = 0;
    return (size_t)&tid;
  }

  inline FuncThreadID& GetFuncThreadID()
  {
    static FuncThreadID func = getThreadID_;
    return func;
  }
  
} // namespace zzz_
/// @endcond

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// スレッドIDの取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inline size_t GetThreadID()
{
  return zzz_::GetFuncThreadID()();
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// スレッドID取得用関数の設定。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inline void   SetFunc_GetThreadID( zzz_::FuncThreadID func_get_thread_id )
{
  zzz_::GetFuncThreadID() = func_get_thread_id;
}


// _____________________________________________________________________________
/// @brief ミューテックス基底クラス
///
/// @note
/// 排他制御オブジェクトは、原則このクラスを継承すること。\n
/// 標準仕様に合わせるためのインターフェイスのみを実装し、
/// 標準インターフェイスの強制と共通化を図る。\n
/// 多能性の実現のため、仮想呼び出し分のオーバーヘッドがかかるが、
/// 同期処理に比べれば微々たるものだと考えられる。
// *****************************************************************************
class MutexBase
{
protected:
  struct duration_conv_
  {
    template<class rep, class period>
    duration_conv_( cref<std::chrono::duration<rep,period>> d_ )
      : duration_( std::chrono::duration_cast<td_time_msec>(d_) )
    {}

    td_time_msec duration_;
  };

  struct timepoint_conv_
  {
    template<class T_Clock, class T_Duration>
    timepoint_conv_( cref<std::chrono::time_point<T_Clock,T_Duration>> t_ )
      : timepoint_( std::chrono::time_point_cast<typename td_time_point::duration>(t_) )
    {}

    td_time_point timepoint_;
  };
public:
  // _____________________________________________________________________________
  /// @brief ロックを取得する
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual void  lock( U32 in_timed_out = -1 ) const = 0;

  // _____________________________________________________________________________
  /// @brief ロックの取得を試みる
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual bool  try_lock() const = 0;

  // _____________________________________________________________________________
  /// @brief ロックを手放す
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	virtual void  unlock() const = 0;

  // _____________________________________________________________________________
  /// @brief ミューテックスのハンドルを取得する
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual void* native_handle() const = 0;

  // _____________________________________________________________________________
  /// @brief タイムアウトする相対時間を指定してロックの取得を試みる
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual bool try_lock_for( duration_conv_ rel_time ) const = 0;

  // _____________________________________________________________________________
  /// @brief タイムアウトする絶対時間を指定してロックの取得を試みる
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  virtual bool try_lock_until( timepoint_conv_ t_ ) const = 0;
};


// _____________________________________________________________________________
/// @brief スレッド用ミューテックス
///
// *****************************************************************************
template<class T_Mutex = std::mutex>
class ThreadMutex
  : public MutexBase
{
  mutable T_Mutex m_mutex;

  template<class T_Mutex, class T_Time>
  static bool call_try_lock_for( T_Mutex& m, T_Time t )
  {
    return m.try_lock_for( t );
  }
  template<class T_Time>
  static bool call_try_lock_for( std::mutex&, T_Time )
  {
    return false;
  }
  template<class T_Time>
  static bool call_try_lock_for( std::recursive_mutex&, T_Time )
  {
    return false;
  }

  template<class T_Mutex, class T_Time>
  static bool call_try_lock_until( T_Mutex& m, T_Time t )
  {
    return m.try_lock_until( t );
  }
  template<class T_Time>
  static bool call_try_lock_until( std::mutex&, T_Time )
  {
    CPL_THROW( "CorePLi Error : try_lock_until function does not exist in std::mutex" );
  }
  template<class T_Time>
  static bool call_try_lock_until( std::recursive_mutex&, T_Time )
  {
    CPL_THROW( "CorePLi Error : try_lock_until function does not exist in std::recursive_mutex" );
  }
public:
  // _____________________________________________________________________________
  /// @brief デフォルトコンストラクタ
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ThreadMutex() noexcept {}
  // _____________________________________________________________________________
  /// @brief 名前付きコンストラクタ（インターフェイス互換用）
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ThreadMutex( _in<td_tstring> in_mutex_name ){}
  // _____________________________________________________________________________
  /// @brief 
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ~ThreadMutex(){}

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

  /// @copydoc MutexBase::lock
  void  lock( U32 in_timed_out = -1 ) const override
  {
    CPL_DBG_OUTPUT_IF
      ( Config::Mutex::ShowMsg_lock
      , SwS("CorePLi:[TID:%Iu]: tmutex(%Ix) - lock\n")
      , GetThreadID()
      , (size_t)this
      );
    m_mutex.lock();
  }
  /// @copydoc MutexBase::try_lock
  bool  try_lock() const override
  {
    CPL_DBG_OUTPUT_IF
      ( Config::Mutex::ShowMsg_try_lock
      , SwS("CorePLi:[TID:%Iu]: tmutex(%Ix) - try_lock\n")
      , GetThreadID()
      , (size_t)this
      );
    return m_mutex.try_lock();
  }
  /// @copydoc MutexBase::unlock
	void  unlock()  const override
  {
    CPL_DBG_OUTPUT_IF
      ( Config::Mutex::ShowMsg_unlock
      , SwS("CorePLi:[TID:%Iu]: tmutex(%Ix) - unlock\n")
      , GetThreadID()
      , (size_t)this
      );
    m_mutex.unlock();
  }
  /// @copydoc MutexBase::native_handle
  void* native_handle() const override
  {
    //return m_mutex.native_handle();
    return nullptr;
  }
  /// @copydoc MutexBase::try_lock_for
  bool try_lock_for( duration_conv_ rel_time ) const override
  {
    CPL_DBG_OUTPUT_IF
      ( Config::Mutex::ShowMsg_try_lock_for
      , SwS("CorePLi:[TID:%Iu]: tmutex(%Ix) - try_lock_for\n")
      , GetThreadID()
      , (size_t)this
      );
    return call_try_lock_for( m_mutex, rel_time.duration_ );
  }
  /// @copydoc MutexBase::try_lock_until
  bool try_lock_until( timepoint_conv_ abs_time ) const override
  {
    CPL_DBG_OUTPUT_IF
      ( Config::Mutex::ShowMsg_try_lock_until
      , SwS("CorePLi:[TID:%Iu]: tmutex(%Ix) - try_lock_until\n")
      , GetThreadID()
      , (size_t)this
      );
    return call_try_lock_until( m_mutex, abs_time.timepoint_ );
  }
};


//// _____________________________________________________________________________
///// @brief スレッド用ミューテックス（同一スレッド再ロック可能仕様）
/////
//// *****************************************************************************
//class ThreadMutexEx
//  : public MutexBase
//{
//  typedef ThreadMutexEx type_this;
//
//  mutable std::atomic_int     m_count;
//  mutable std::atomic_int     m_count_nest; // 同一スレッドから呼ばれた回数。
//  mutable std::atomic_size_t  m_active_thread_ID;
//
//  static auto sm_get_tid_()
//  {
//    static thread_local size_t tid_ = 0;
//    return reinterpret_cast<size_t>(&tid_);
//  }
//
//public:
//  // _____________________________________________________________________________
//  /// @brief デフォルトコンストラクタ
//  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  ThreadMutexEx() noexcept;
//  // _____________________________________________________________________________
//  /// @brief 名前付きコンストラクタ（インターフェイス互換用）
//  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  ThreadMutexEx( _in<td_tstring> in_mutex_name )
//    : ThreadMutexEx()
//  {}
//  // _____________________________________________________________________________
//  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  ~ThreadMutexEx();
//
//  ThreadMutexEx& operator=(ThreadMutexEx const&) = delete;
//
//  void  lock( U32 in_timed_out = -1 ) const override;
//  bool  try_lock() const override;
//	void  unlock()   const override;
//  void* native_handle() const override;
//};


// _____________________________________________________________________________
/// @brief プロセス用ミューテックス
///
// *****************************************************************************
class ProcessMutex
  : public MutexBase
{
  class Entity;
  std::shared_ptr<Entity> m_sp_entity;
public:
  ProcessMutex( _in<td_tstring> in_mutex_name );

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

  void  lock( U32 in_timed_out = -1 ) const override;
  bool  try_lock() const override;
  void  unlock() const override;
  void* native_handle() const override;
  bool  try_lock_for( duration_conv_ rel_time ) const override;
  bool  try_lock_until( timepoint_conv_ abs_time ) const override;
};


// _____________________________________________________________________________
/// @brief ネットワーク用ミューテックス
///
// *****************************************************************************
class NetworkMutex
  : public MutexBase
{
  class Entity;
  std::shared_ptr<Entity> m_sp_entity;
public:
  NetworkMutex( _in<td_tstring> in_name_mutex );

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

  void  lock( U32 in_timed_out = -1 ) const override;
  bool  try_lock() const override;
  void  unlock() const override;
  void* native_handle() const override;
  bool  try_lock_for( duration_conv_ rel_time ) const override;
  bool  try_lock_until( timepoint_conv_ abs_time ) const override;
};


} // namespace Core
} // namespace CPL

