/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2013/X/X - 2014/01/14
/// 
/// @brief  タイマー等。
/// 
/// ****************************************************************************

#pragma once

#include "CPL_Core_Define.h"


namespace CPL {

// タイム型？
using td_time_nsec  = std::chrono::nanoseconds;
using td_time_usec  = std::chrono::microseconds;
using td_time_msec  = std::chrono::milliseconds;
using td_time_sec   = std::chrono::seconds;
using td_time_min   = std::chrono::minutes;
using td_time_hour  = std::chrono::hours;
using td_time_point = decltype(std::chrono::high_resolution_clock::now());


inline td_time_nsec operator""_nsec( unsigned long long int val ){ return td_time_nsec(val); }
inline td_time_usec operator""_usec( unsigned long long int val ){ return td_time_usec(val); }
inline td_time_msec operator""_msec( unsigned long long int val ){ return td_time_msec(val); }
inline td_time_sec  operator""_sec ( unsigned long long int val ){ return td_time_sec (val); }
inline td_time_min  operator""_min ( unsigned long long int val ){ return td_time_min (val); }
inline td_time_hour operator""_hour( unsigned long long int val ){ return td_time_hour(val); }
inline td_time_nsec operator""_usec( long double val ){ return td_time_nsec(static_cast<unsigned long long>(val * (10^3))); }
inline td_time_nsec operator""_msec( long double val ){ return td_time_nsec(static_cast<unsigned long long>(val * (10^6))); }
inline td_time_nsec operator""_sec ( long double val ){ return td_time_nsec(static_cast<unsigned long long>(val * (10^12))); }
inline td_time_nsec operator""_min ( long double val ){ return td_time_nsec(static_cast<unsigned long long>(val * (60*10^12))); }
inline td_time_nsec operator""_hour( long double val ){ return td_time_nsec(static_cast<unsigned long long>(val * (60*60*10^12))); }

// _____________________________________________________________________________
/// @brief オフセット時間を追加したタイムポイントを取得。
/// 
/// @param[in] in_offset_time
///   : オフセット時間を指定。\n
///   : オフセット時間に-1相当を指定すると、実質無限に近い未来の時間とする。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T>
inline td_time_point TimePointOffset( T in_offset_time )
{
  return std::chrono::high_resolution_clock::now() + in_offset_time;
}
/// @copydoc TimePointOffset
template<>
inline td_time_point TimePointOffset( nullptr_t in_offset_time )
{
  return (td_time_point::max)();
}

// _____________________________________________________________________________
/// @brief タイムアウトチェック
/// 
/// 指定したタイムポイントと現在のタイムポイントを比較
/// 
/// @param[in] in_check_time_point
///   : 比較するタイムポイントを指定。
/// 
/// @retval true  : 現在のタイムポイントが指定したタイムポイントを超えている。
/// @retval false : 現在のタイムポイントが指定したタイムポイントを超えていない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inline bool IsTimeout( td_time_point in_check_time_point )
{
  return in_check_time_point <= std::chrono::high_resolution_clock::now();
}

// _____________________________________________________________________________
/// @brief 拡張タイムポイント
///
// *****************************************************************************
class TimePoint : public td_time_point
{
public:
  TimePoint( cref<TimePoint> ) = default;
  TimePoint( rvref<TimePoint> ) = default;
  TimePoint() = default;
  ~TimePoint() = default;

  TimePoint( cref<td_time_point> tp )
    : td_time_point( tp )
  {}
  TimePoint( rvref<td_time_point> tp )
    : td_time_point( std::move(tp) )
  {}

  template<class T, class U>
  TimePoint( std::chrono::duration<T,U> t )
    : td_time_point( TimePointOffset(t) )
  {}

  TimePoint( nullptr_t )
    : td_time_point( (max)() )
  {}

  TimePoint& operator = ( cref<TimePoint> t )
  {
    *static_cast<td_time_point*>(this) = td_time_point(t);
    return *this;
  }

  static td_time_point Max(){ return (td_time_point::max)(); }
  static td_time_point Min(){ return (td_time_point::min)(); }
  static td_time_point Now(){ return std::chrono::high_resolution_clock::now(); }
};



namespace Core {

// _____________________________________________________________________________
/// @brief タイムカウンター
/// 
/// 構築した時からの経過時間を管理する。
///
// *****************************************************************************
class TimeCounter {
  typedef decltype(std::chrono::high_resolution_clock::now()) TypeTime;
  TypeTime time_start;

public:
  TimeCounter()
    : time_start( std::chrono::high_resolution_clock::now() )
  {
  }

  template<class T_TypeTime>
  T_TypeTime GetTime() const
  {
    auto ts = std::chrono::high_resolution_clock::now() - time_start;
    return std::chrono::duration_cast< T_TypeTime >( ts );
  }

  td_time_nsec GetTime_nsec() const { return GetTime<td_time_nsec>(); }
  td_time_usec GetTime_usec() const { return GetTime<td_time_usec>(); }
  td_time_msec GetTime_msec() const { return GetTime<td_time_msec>(); }
  td_time_sec  GetTime_sec()  const { return GetTime<td_time_sec >(); }
  td_time_min  GetTime_min()  const { return GetTime<td_time_min >(); }
  td_time_hour GetTime_hour() const { return GetTime<td_time_hour>(); }


  void Reset()
  {
    time_start = std::chrono::high_resolution_clock::now();
  }

  static auto StaticPoint()
  {
    static auto t_ = std::chrono::high_resolution_clock::now();
    return (std::chrono::high_resolution_clock::now() - t_);
  }
};


// _____________________________________________________________________________
/// @brief 賢いスリーパー？
/// 
/// タイムスライスか現タイムスライスの最後尾へのスリープを指定回数で切り替える。
///
// *****************************************************************************
class SmartSleeper
{
public:
  SmartSleeper( _in<int> in_count )
    : m_counter( in_count )
    , m_count_max( in_count )
  {}

  // _____________________________________________________________________________
  /// @brief Sleepの呼び出し。
/// コンストラクタで設定した回数までは
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void Sleep()
  {
    if( m_counter )
    {
      --m_counter;
      CPL_SleepAfterTimeSlice();
    }
    else
    {
      CPL_SleepNextTimeSlice();
    }
  }

  // _____________________________________________________________________________
  /// @brief 
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void Reset( int in_max_new = -1 )
  {
    if( in_max_new >= 0 )
    {
      m_count_max = in_max_new;
    }
    m_counter = m_count_max;
  }

protected:
  int m_counter;
  int m_count_max;
};



} // namespace Core
} // namespace CPL


// _____________________________________________________________________________
/// @brief タイムループマクロ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define CPL_TIME_LOOP( time )\
  for( auto t_out = CPL::TimePointOffset(time); !CPL::IsTimeout(t_out);)

