/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2015/03/24 - 2015/11/13
/// 
/// @brief  プロセス用共有フラグ
/// 
/// ****************************************************************************

#include "CPL_Core_SharedFlag.h"

#ifdef CPL_WINDOWS
#include <winsock2.h> // windows.hより先に読み込んでおかないと、マズイ場合がある
//#include <Windows.h>
#endif


namespace CPL {
namespace Core {

// _____________________________________________________________________________
/// @brief ProcessSharedFlag実体
///
// *****************************************************************************
class ProcessSharedFlag::Entity
{
  HANDLE     m_handle;
  td_tstring m_name;
public:
  Entity
  ( _in<bool      > in_flag = false
  , _in<td_tstring> in_name = SwS("")
  )
  {
    td_tstring name_new = in_name + SwS("_ProcessSharedFlag");
    m_handle = OpenEvent( EVENT_ALL_ACCESS, in_flag, name_new.c_str() );
    if( m_handle == nullptr )
    {
      m_handle =
        CreateEvent
          ( nullptr // セキュリティはデフォルト
          , true    // マニュアルリセット
          , in_flag
          , name_new.c_str()
          );
    }

    CPL_THROW_IF( m_handle == nullptr, "Failed to create of ProcessSharedFlag.\n" )
  }

  ~Entity()
  {
    CloseHandle( m_handle );
  }

  /// @brief SharedFlagBase::GetFlag
  bool GetFlag() const
  {
    return WaitForSingleObject( m_handle, 0 ) == WAIT_OBJECT_0; // 判定が正確ではないけど、まぁ良しとしよう。
  }

  /// @brief SharedFlagBase::SetFlag
  bool
    SetFlag
      ( _in<bool     > in_flag
      , _in<TimePoint> in_time_out
      )
  {
    if( in_flag )
    {
      SetEvent( m_handle );
    }
    else
    {
      ResetEvent( m_handle );
    }
    return true;
  }

  /// @brief SharedFlagBase::GetName
  td_tstring GetName() const
  {
    return m_name;
  }

  bool WaitOn ( _in<TimePoint> in_time_point_end )
  {
    auto tm_ = in_time_point_end - TimePoint::Now();
    if( tm_ < td_time_nsec(0) )
    {
      tm_ = td_time_nsec(0);
    }
    auto wt_ = std::chrono::duration_cast<td_time_msec>(tm_);
    if( WaitForSingleObject( m_handle, static_cast<U32>(wt_.count()) ) == WAIT_OBJECT_0 )
    {
      return true;
    }
    return false;
  }

  bool WaitOff( _in<TimePoint> in_time_point_end )
  {
    return false;
  }
};


// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @param[in] in_flag : 設定するbool値。
/// @param[in] in_name : プロセス間で共有する共有フラグの識別名。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ProcessSharedFlag::ProcessSharedFlag
  ( _in<bool      > in_flag
  , _in<td_tstring> in_name
  ) : m_sp_entity( CPL_MAKE_SHARED( Entity, in_flag, in_name) )
{
}

/// @brief SharedFlagBase::GetFlag
bool ProcessSharedFlag::GetFlag() const
{
  return m_sp_entity->GetFlag();
}

/// @brief SharedFlagBase::SetFlag
bool
  ProcessSharedFlag::SetFlag
    ( _in<bool     > in_flag
    , _in<TimePoint> in_time_out
    )
{
  return m_sp_entity->SetFlag( in_flag, in_time_out );
}

/// @brief SharedFlagBase::GetName
td_tstring ProcessSharedFlag::GetName() const
{
  return m_sp_entity->GetName();
}

/// @brief SharedFlagBase::WaitOn
bool ProcessSharedFlag::WaitOn( _in<TimePoint> in_time_point_end )
{
  return m_sp_entity->WaitOn( in_time_point_end );
}

/// @brief SharedFlagBase::WaitOff
bool ProcessSharedFlag::WaitOff( _in<TimePoint> in_time_point_end )
{
  return m_sp_entity->WaitOff( in_time_point_end );
}



} // namespace Core
} // namespace CPL
