/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2015/11/13
/// 
/// @brief  スレッド関連。
///
/// ※ std::thread拡張版に修正（2013/06/21）
/// 
/// ****************************************************************************

#pragma once

#include <process.h>

#include <thread>
#include <atomic>

#include "CPL_Core_Define.h"
//#include "CPL_Core_Basic.h"

#include "../../CPL_Setup.h"
#include "CPL_Core_Mutex.h"
//#include "CPL_Core_Buffer.h"
#include "CPL_Core_Time.h"


#define CPL_MULTI_THREAD // この定義を無くすと、マルチスレッド非対応となる

#if _MSC_VER < 1900
#ifndef thread_local

// visual studio
#ifdef _MSC_VER
//#if _MSC_VER <= 1700
#define thread_local __declspec(thread)
//#endif
#endif

// intel compiler
#ifdef __INTEL_COMPILER
#define thread_local __declspec(thread)
#endif

// gcc
#ifdef __GNUC__
#define thread_local __thread
#endif

// clang
#ifdef __clang__
#define thread_local __thread
#endif

#endif // thread_local
#endif // _MSC_VER


namespace CPL {
namespace Core {


// _____________________________________________________________________________
/// @brief ミューテックス用タイマー
/// 
// *****************************************************************************
class MutexTimer
{
  using td_timer_count_type = decltype(std::chrono::high_resolution_clock::now());
public:
  MutexTimer( _in<td_time_msec> in_offs_time )
    : m_time_base( std::chrono::high_resolution_clock::now() )
    , m_time_end( m_time_base + in_offs_time )
  {}

  td_timer_count_type GetTimeBase() const { return m_time_base; }
  td_timer_count_type GetTimeEnd()  const { return m_time_end; }
private:
  td_timer_count_type m_time_base;
  td_timer_count_type m_time_end;
};


//// _____________________________________________________________________________
///// @brief 排他区間オブジェクト
/////
///// 標準C++,Windows,POSIX等でそれぞれ用語・用法が一致していないので、あえて造語。\n
///// 基本的に標準C++に合わせたいところであるが、現状混乱しやすいので将来的に置き換え
///// ることも視野に入れつつ、造語。\n
///// C++11のstd::lock_guardに相当。
//// *****************************************************************************
template<class T_MutexPtr>
class Exction
{
  using td_self = Exction<T_MutexPtr>;
  friend td_self;
public:
  Exction( rvref<td_self> exc )
    : m_ptr_mutex( exc.m_ptr_mutex )
  {
    exc.m_ptr_mutex = nullptr;
  }

  template<class T>
  explicit Exction( std::shared_ptr<T> in_sp_mutex, Priority<2>* pr=UsePriority )
    : m_ptr_mutex( in_sp_mutex )
  {
    CPL_SAFE_PTR(m_ptr_mutex)->lock();
  }
  template<class T>
  explicit Exction( T* in_ptr_mutex, Priority<1>* pr=UsePriority )
    : m_ptr_mutex( in_ptr_mutex )
  {
    CPL_SAFE_PTR(m_ptr_mutex)->lock();
  }
  template<class T>
  explicit Exction( T& in_mutex, Priority<0>* pr=UsePriority )
    : m_ptr_mutex( &in_mutex )
  {
    m_ptr_mutex->lock();
  }
  ~Exction()
  {
    CPL_SAFE_PTR(m_ptr_mutex)->unlock();
  }

  Exction( rvref<T_MutexPtr> ) = delete;
  Exction( cref<td_self> ) = delete;

  operator bool() const { return true; }
private:
  T_MutexPtr m_ptr_mutex;
};



namespace zzz_
{

  template<class T>
  struct CallUnlock_
  {
    T* ptr_mutex;

    CallUnlock_( cref<CallUnlock_> ) = default;
    CallUnlock_( rvref<CallUnlock_> caller_ )
    {
      ptr_mutex = caller_.ptr_mutex;
      caller_.ptr_mutex = nullptr;
    }

    CallUnlock_( T& in_mutex )
      : ptr_mutex( &in_mutex )
    {}
    CallUnlock_( cref<T> in_mutex )
      : ptr_mutex( &in_mutex )
    {}
    CallUnlock_( T* in_ptr_mutex )
      : ptr_mutex(in_ptr_mutex)
    {}

    ~CallUnlock_()
    {
      CPL_SAFE_PTR(ptr_mutex)->unlock();
    }

    operator bool() const { return true; }
  };

  template<class T>
  struct CallAutoLock_ : CallUnlock_<T>
  {
    CallAutoLock_( T& mutex )
      : CallUnlock_<T>( &mutex )
    {}

    CallAutoLock_( T* ptr_mutex )
      : CallUnlock_<T>( ptr_mutex )
    {
      ptr_mutex->lock();
    }
    CallAutoLock_( CallAutoLock_&& caller_ )
      : CallUnlock_<T>( std::move(caller_) )
    {}
  };


  // ロックはstd::lockにまかせ、デストラクタでunlockのみ呼び出す。
  template<class Tx, class... Ty>
  struct MutexPack
  {
    MutexPack() = delete;
    MutexPack( MutexPack&& mp )
      : ptr_mutex( mp.ptr_mutex )
      , pack_mutex( std::move(mp.pack_mutex) )
    {
      mp.ptr_mutex = nullptr;
    }

    MutexPack( Tx& x, Ty&... y )
      : ptr_mutex( &x )
      , pack_mutex( y... )
    {}

    MutexPack( nullptr_t )
      : ptr_mutex( nullptr )
      , pack_mutex( nullptr )
    {}

    ~MutexPack()
    {
      CPL_SAFE_PTR(ptr_mutex)->unlock();
    }

    operator bool() const { return true; }

    std::remove_reference_t<Tx>* ptr_mutex;
    MutexPack<Ty...>             pack_mutex;
  };

  template<class Tx>
  struct MutexPack<Tx>
  {
    MutexPack( MutexPack&& mp )
      : ptr_mutex( mp.ptr_mutex )
    {
      mp.ptr_mutex = nullptr;
    }
    MutexPack( Tx& x )
      : ptr_mutex( &x )
    {}
    MutexPack( nullptr_t )
      : ptr_mutex( nullptr )
    {}

    ~MutexPack()
    {
      CPL_SAFE_PTR(ptr_mutex)->unlock();
    }

    operator bool() const { return true; }

    std::remove_reference_t<Tx>* ptr_mutex;
  };
}


// _____________________________________________________________________________
/// @brief mutex lock helper function(timeout)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Mutex>
auto LockMutex( TimePoint in_time, T_Mutex& in_mutex )
{
  return
    std::unique_lock<std::remove_reference_t<T_Mutex>>
      ( in_mutex
      , static_cast<td_time_point>(in_time)
      );
}
// 本来TimePointが暗黙キャストで対応しているが、オーバーロードの優先度が低いので仕方なく用意...
template<class R_, class P_, class T_Mutex>
auto LockMutex( std::chrono::duration<R_, P_> in_time, T_Mutex& in_mutex )
{
  return LockMutex( TimePoint(in_time), in_mutex );
}
// _____________________________________________________________________________
template<class T_Mutex>
auto LockMutex( TimePoint in_time, T_Mutex* in_ptr_mutex )
{
  return
    in_ptr_mutex
      ? std::unique_lock<T_Mutex>
          ( *in_ptr_mutex
          , static_cast<td_time_point>(in_time)
          )
      : std::unique_lock<T_Mutex>();
}
// 本来TimePointが暗黙キャストで対応しているが、オーバーロードの優先度が低いので仕方なく用意...
template<class R_, class P_, class T_Mutex>
auto LockMutex( std::chrono::duration<R_, P_> in_time, T_Mutex* in_ptr_mutex )
{
  return LockMutex( TimePoint(in_time), in_ptr_mutex );
}
// _____________________________________________________________________________
template<class T_Mutex>
auto LockMutex( TimePoint in_time, std::shared_ptr<T_Mutex> in_sp_mutex )
{
  return
    in_sp_mutex
      ? std::unique_lock<T_Mutex>
          ( *in_sp_mutex
          , static_cast<td_time_point>(in_time)
          )
      : std::unique_lock<T_Mutex>();
}
// 本来TimePointが暗黙キャストで対応しているが、オーバーロードの優先度が低いので仕方なく用意...
template<class R_, class P_, class T_Mutex>
auto
  LockMutex
    ( std::chrono::duration<R_, P_> in_time
    , std::shared_ptr<T_Mutex>      in_sp_mutex
    )
{
  return LockMutex( TimePoint(in_time), in_sp_mutex );
}

// _____________________________________________________________________________
/// @brief mutex lock helper function
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Mutex>
auto LockMutex( T_Mutex& in_mutex )
{
  return std::unique_lock<std::remove_reference_t<T_Mutex>>( in_mutex );
}
// _____________________________________________________________________________
template<class T_Mutex>
auto LockMutex( T_Mutex* in_ptr_mutex )
{
  return
    in_ptr_mutex
      ? std::unique_lock<T_Mutex>( *in_ptr_mutex )
      : std::unique_lock<T_Mutex>();
}
// _____________________________________________________________________________
template<class T_Mutex>
auto LockMutex( std::shared_ptr<T_Mutex> in_sp_mutex )
{
  return
    in_sp_mutex
      ? std::unique_lock<T_Mutex>( *in_sp_mutex )
      : std::unique_lock<T_Mutex>();
}


// _____________________________________________________________________________
/// @brief multi mutex lock helper function (timeout)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Mutex0, class T_Mutex1, class... T_Mutexs>
auto
  LockMutex
    ( TimePoint    in_time_point_end
    , T_Mutex0&    in_mutex0
    , T_Mutex1&    in_mutex1
    , T_Mutexs&... mutexs
    )
{
  bool flag_wait = 0;
  while
    ( std::try_lock<T_Mutex0,T_Mutex1,T_Mutexs...>
        ( in_mutex0
        , in_mutex1
        , mutexs...
        ) != -1
    )
  {
    if( IsTimeout( in_time_point_end ) )
    {
      return zzz_::MutexPack< T_Mutex0, T_Mutex1, T_Mutexs...>( nullptr );
    }
    CPL_Sleep( td_time_msec(flag_wait) );
    flag_wait = !flag_wait;
  }

  return
    zzz_::MutexPack< T_Mutex0, T_Mutex1, T_Mutexs...>
      ( in_mutex0
      , in_mutex1
      , std::forward<T_Mutexs>(mutexs)...
      );
}
// _____________________________________________________________________________
/// @brief multi mutex lock helper function (timeout)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class R_, class P_, class T_Mutex0, class T_Mutex1, class... T_Mutexs>
auto
  LockMutex
    ( std::chrono::duration<R_, P_> in_time_out
    , T_Mutex0&    in_mutex0
    , T_Mutex1&    in_mutex1
    , T_Mutexs&... mutexs
    )
{
  TimePoint tp_end(in_time_out);
  bool flag_wait = 0;
  while
    ( std::try_lock<T_Mutex0,T_Mutex1,T_Mutexs...>
        ( in_mutex0
        , in_mutex1
        , mutexs...
        ) != -1
    )
  {
    if( IsTimeout( tp_end ) )
    {
      return zzz_::MutexPack< T_Mutex0, T_Mutex1, T_Mutexs...>( nullptr );
    }
    CPL_Sleep( td_time_msec(flag_wait) );
    flag_wait = !flag_wait;
  }

  return
    zzz_::MutexPack< T_Mutex0, T_Mutex1, T_Mutexs...>
      ( in_mutex0
      , in_mutex1
      , std::forward<T_Mutexs>(mutexs)...
      );
}

// _____________________________________________________________________________
/// @brief multi mutex lock helper function
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T_Mutex0, class T_Mutex1, class... T_Mutexs>
auto
  LockMutex
    ( T_Mutex0& in_mutex0
    , T_Mutex1& in_mutex1
    , T_Mutexs&... mutexs
    )
{
  std::lock( in_mutex0, in_mutex1, mutexs... );

  return
    zzz_::MutexPack< T_Mutex0, T_Mutex1, T_Mutexs...>
      ( in_mutex0
      , in_mutex1
      , mutexs...
      );
}




#define CPL_EXCTION_(...) if( auto zzz_lock_ = CPL::Core::LockMutex(__VA_ARGS__) )

// _____________________________________________________________________________
/// @brief ロック優先度クラス？
/// 
/// ロックレベルを管理？
/// ロックオブジェクトに優先レベル？を割り当て、ロック順序優先度を管理する。
/// ロック中のレベル以下のロックのみ許可する。
///
// *****************************************************************************
template<class T>
class LockPriority
{
private:
  int m_old_level;

protected:
  static int& sm_lock_level()
  {
    static thread_local int obj(0x7fffffff);
    return obj;
  }

  LockPriority( int in_level )
  {
    CPL_BREAK( in_level > sm_lock_level() );
    m_old_level = sm_lock_level();
    sm_lock_level() = in_level;
  }
  ~LockPriority()
  {
    sm_lock_level() = m_old_level;
  }

  //static const int DEFAULt_LEVEL_USING = 0x80000000;
public:
};



// _____________________________________________________________________________
/// @brief スレッド単位での自動ロッククラス（型識別仕様）
///
// *****************************************************************************
template<class T, int N = 1, class T_Mutex = std::mutex>
class ExctionT
  : public LockPriority<T>
  , public Exction<ThreadMutex<T_Mutex>*>
{
private:
  using td_base_lockpriority = LockPriority<T>;
  using td_base_exction      = Exction<ThreadMutex<T_Mutex>*>;
  CPL_SINGLETON( ThreadMutex<T_Mutex>, sm_mutex )

#ifdef CPL_DEBUG
  str_cptr m_unlock_msg;
#endif


public:
  ExctionT( int in_lock_level = 0 )
    :
#ifdef CPL_DEBUG
      m_unlock_msg( nullptr ),
#endif
      td_base_lockpriority( in_lock_level )
    , td_base_exction( &sm_mutex(), UsePriority )
  {
  }

  ExctionT
  ( str_cptr in_lock_msg
  , str_cptr in_unlock_msg = nullptr
  , int      in_lock_level = 0
  ) :
#ifdef CPL_DEBUG
      m_unlock_msg( in_unlock_msg ? in_unlock_msg : in_lock_msg ),
#endif
      td_base_lockpriority( in_lock_level )
    , td_base_exction( &sm_mutex() )
  {
#ifdef CPL_DEBUG
    if( in_lock_msg  &&  sm_mutex().GetCount() == 1 )
    {
      CPL_OUTPUT( SwS("%s auto_lock\n"), in_lock_msg );
    }
    else
    {
      m_unlock_msg = nullptr;
    }
#endif
  }

  ~ExctionT()
  {
#ifdef CPL_DEBUG
    if( m_unlock_msg )
    {
      CPL_OUTPUT( SwS("%s: auto_unlock\n"), m_unlock_msg );
    }
#endif
  }
};





// _____________________________________________________________________________
/// @brief スレッド単位での自動ロッククラス（固定レベル指定付）
///
// *****************************************************************************
template<class T, int LV>
class ExctionTLv : public ExctionT< T >
{
  using td_base = ExctionT< T >;
public:
  ExctionTLv()
    : td_base( LV )
  {}
  ExctionTLv
  ( str_cptr in_lock_msg
  , str_cptr in_unlock_msg = nullptr
  ) : td_base( in_lock_msg, in_unlock_msg, LV )
  {}

  ~ExctionTLv()
  {}
};



// _____________________________________________________________________________
/// @brief スレッド単位での自動ロッククラス（ID識別仕様）
///
// *****************************************************************************
template<int N>
class ExctionN : public ExctionT< ExctionN<N> >
{
  using td_base = ExctionT< ExctionN<N> >;
public:
  ExctionN( int in_lock_level = 0 )
    : td_base( in_lock_level )
  {}
  ExctionN
  ( str_cptr in_lock_msg
  , str_cptr in_unlock_msg = nullptr
  , int      in_lock_level = 0
  ) : td_base( in_lock_msg, in_unlock_msg, in_lock_level )
  {}

  ~ExctionN()
  {}
};


// _____________________________________________________________________________
/// @brief スレッド単位での自動ロッククラス（ID識別仕様 ＆ 固定レベル指定付）
///
// *****************************************************************************
template<int N, int LV>
class ExctionNLv : public ExctionN< N >
{
  using td_base = ExctionN< N >;
public:
  ExctionNLv( int in_lock_level = 0 )
    : td_base( in_lock_level )
  {}
  ExctionNLv
  ( str_cptr in_lock_msg
  , str_cptr in_unlock_msg = nullptr
  ) : td_base( in_lock_msg, in_unlock_msg, LV )
  {}

  ~ExctionNLv()
  {}
};


//typedef ExctionN<0> Exction;




} // namespace Core
} // namespace CPL
