/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2002/X/X - 2015/11/13
/// 
/// @brief  Windowsの基本的なやつら？
/// 
/// ****************************************************************************

#pragma once

#ifndef NOMINMAX
#define NOMINMAX // min/max定義すんな！
#endif

struct IUnknown;
#include <winsock2.h> // ←なんでwindows.hより前におかないといけないですか！（怒ｗ
//#include <windows.h>
#include <commdlg.h>
#include <windowsx.h>
#include <commctrl.h>
#include <mmsystem.h>
#include <shlobj.h> // 
#include <richedit.h>
#include <stdarg.h>


#include "../../Core/CPL_Core.h"
//#include "../../Core/Base/CPL_Core_Define.h"
//#include "../../Core/Base/CPL_Core_Mutex.h"
//#include "../../Core/Base/CPL_Core_Exception.h"
//#include "../../Core/String/CPL_Core_String.h"



// 必要なライブラリの読み込み
#ifdef _MSC_VER
  #pragma comment(lib, "imm32.lib")
  #pragma comment(lib, "Comctl32.lib")
  #pragma comment(lib, "winmm.lib")
  #pragma comment(lib, "user32.lib")
  #pragma comment(lib, "gdi32.lib")
  #pragma comment(lib, "comdlg32.lib")
  #pragma comment(lib, "shell32.lib")
  #pragma comment(lib, "advapi32.lib")
  #pragma comment(lib, "ole32.lib")
#endif





namespace CPL {

/// Windows関連が含まれます。\n
/// フレームワーク、簡単な入出力、ごく一部のマルチメディア、簡単なネットワークをサポートしています。
namespace WIN {

  
#define CPL_WIN_MSG_NOT_CREATE_MENU        "メニュー作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_DC          "デバイスコンテキスト作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_WINDOW      "ウィンドウ作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_MDICW       "MDIクライアントウィンドウ作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_MDIWIN      "MDIウィンドウ作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_IMGBOX      "イメージボックス作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_IMGWINDOW   "イメージウィンドウ作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_LISTBOX     "リストボックス作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_LISTWINDOW  "リストウィンドウ作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_TEXTBOX     "テキストボックス作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_EDITBOX     "エディットボックス作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_EDITWINDOW  "エディットウィンドウ作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_LABEL       "ラベル作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_BUTTON      "ボタン作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_SCROLLBAR   "スクロールバー作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_COMBOBOX    "コンボボックス作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_TABCONTROL  "タブウィンドウ作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_FONT        "フォント作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_DIB         "DIB作成失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_TOOLTIP     "ツールチップ作成に失敗\n"
#define CPL_WIN_MSG_NOT_CREATE_TREEVIEW    "ツリービュー作成に失敗\n"
#define CPL_WIN_MSG_NOT_BMP_FORMAT         "未対応のビットマップ形式\n"
#define CPL_WIN_MSG_NOT_OPEN_CRIPBOARD     "クリップノードのオープンに失敗しました\n"


// ツリービューなどの展開ボタンイメージ（＋−）
extern unsigned char expandImage[];


#ifndef WM_MBUTTONDOWN
  #define WM_MBUTTONDOWN 0x0207
#endif
#ifndef WM_MBUTTONUP
  #define WM_MBUTTONUP 0x0208
#endif
#ifndef WM_MBUTTONDBLCLK
  #define WM_MBUTTONDBLCLK 0x0209
#endif
#ifndef WM_MOUSEWHEEL
  #define WM_MOUSEWHEEL 0x020A
#endif

#ifndef GET_KEYSTATE_WPARAM
  #define GET_KEYSTATE_WPARAM(wParam)  (LOWORD(wParam))
#endif
#ifndef GET_WHEEL_DELTA_WPARAM
  #define GET_WHEEL_DELTA_WPARAM(wParam)  ((short)HIWORD(wParam))
#endif


//#ifdef _WINDOWS
#ifdef CPL_WINDOWS
// ※ ↓元のマクロ拡張により、機能が重複していたので削除。(2013/11/15)
//      ただし、一応しばらく残しておく
//// 例外拡張
//#define CPL_THROW(text) {\
//  OutputDebugString(SwS(text));\
//  CPL_THROW(text)}
//#define CPL_WIN_THROW_T(type,text) {\
//  OutputDebugString(SwS(text));\
//  CPL_THROW(type,text)}
//
//#ifdef CPL_DEBUG
//  #define CPL_WIN_THROW2(text) {\
//    OutputDebugString(SwS(text));\
//    CPL_THROW2(text)}
//  #define CPL_WIN_THROW2_T(type,text) {\
//    OutputDebugString(SwS(text));\
//    CPL_THROW2_T(type,text)}
//#else
//  #define CPL_WIN_THROW2(text) {\
//    CPL_THROW2(text)}
//  #define CPL_WIN_THROW2_T(type,text) {\
//    CPL_THROW2_T(type,text)}
//#endif

#endif


const int kID_CPL_UNIT_VALUE = 0x800;
const int kID_CPL = (WM_APP+kID_CPL_UNIT_VALUE); // 一応、既にIDを使用していた場合を考えて+kWM_CPL_UNIT_VALUEしてる
const int kID_RESERVE_MAX = kID_CPL_UNIT_VALUE * 4;

// CPLオリジナル定義メッセージＩＤ
typedef enum
{ kWM_CPL_BTN_CLICK = kID_CPL // wparam（true : マウスダウン時　false:マウスアップ時）
, kWM_CPL_GROUPBOX_EXPAND    // グループボックスの展開
, kWM_CPL_CHILD_SIZE         // 子どもコンポーネントがリサイズされた時の親への通知。ただし、必ず送られるものではない
, kWM_CPL_SOCKET             // ソケットイベント通知

, kIDM_EDIT_CUT
, kIDM_EDIT_COPY
, kIDM_EDIT_PASTE
, kIDM_EDIT_DELETE

, kID_CPL_END
, kID_CPL_USER = kID_CPL + kID_RESERVE_MAX // CorePLiを使用してさらにオリジナルメッセージIDを使用するときは、基本的にこの値以上のものを使うことを推奨
} eCPL_WIN_ID;


// _____________________________________________________________________________
/// @brief COM初期化用クラス
///
// *****************************************************************************
class ComInitializer {
  CPL_SINGLETON_INIT( int, sm_counter, 0 )
  CPL_SINGLETON_INIT( int, sm_flag_security, 0 )
  CPL_SINGLETON( Core::ThreadMutex<>, sm_mutex )
public:
  //typedef Core::ThreadLockT<ComInitializer> AutoThreadLock_Com;

  ComInitializer();
  ~ComInitializer();

  HRESULT
    Security
      ( PSECURITY_DESCRIPTOR          pSecDesc        = nullptr
      , LONG                          cAuthSvc        = -1
      , SOLE_AUTHENTICATION_SERVICE*  asAuthSvc       = nullptr
      , void*                         pReserved1      = nullptr
      , DWORD                         dwAuthnLevel    = RPC_C_AUTHN_LEVEL_CONNECT
      , DWORD                         dwImpLevel      = RPC_C_IMP_LEVEL_IDENTIFY
      , SOLE_AUTHENTICATION_LIST*     pAuthList       = nullptr
      , DWORD                         dwCapabilities  = EOAC_NONE
      , void*                         pReserved3      = 0
      );
};

// _____________________________________________________________________________
/// @brief COM Security? 初期化クラス
///
// *****************************************************************************
class ComSecurityInitializer
  : public ComInitializer
{
public:
  ComSecurityInitializer();
  ~ComSecurityInitializer();
};

// _____________________________________________________________________________
/// @brief COM インスタンスマネージャ？
///
// *****************************************************************************
class ComInstanceBase {
protected:
  void* m_ppv;

  ComInstanceBase
  ( REFCLSID    in_rclsid
  , LPUNKNOWN   io_pUnkOuter
  , DWORD       in_dwClsContext
  , REFIID      in_riid
  );
  ~ComInstanceBase();
};

// _____________________________________________________________________________
/// @brief COM インスタンスマネージャ？ テンプレート仕様
///
// *****************************************************************************
template<class T>
class ComInstance : public ComInstanceBase
{
public:
  ComInstance
  ( REFCLSID    in_rclsid,
    LPUNKNOWN   io_pUnkOuter,
    DWORD       in_dwClsContext,
    REFIID      in_riid )
    : ComInstanceBase( in_rclsid, io_pUnkOuter, in_dwClsContext, in_riid, &m_ppv )
  {}
  ~ComInstance()
  {
    SAFE_INTERFACE_RELEASE( (T*)m_ppv );
  }
};




// _____________________________________________________________________________
/// @brief RECT 拡張
///
// *****************************************************************************
class Rect : public RECT
{
public:
  Rect( long in_left, long in_top, long in_right, long in_bottom )
  {
    this->left   = in_left;
    this->top    = in_top;
    this->right  = in_right;
    this->bottom = in_bottom;
  }
  Rect( cref<RECT> in_rect )
  {
    this->left   = in_rect.left;
    this->right  = in_rect.right;
    this->top    = in_rect.top;
    this->bottom = in_rect.bottom;
  }
  template<class T>
  Rect( cref<Core::Point4<T>> in_p4 )
  {
    this->left   = static_cast<long>(in_p4.x);
    this->top    = static_cast<long>(in_p4.y);
    this->right  = static_cast<long>(in_p4.z);
    this->bottom = static_cast<long>(in_p4.w);
  }
  Rect(){}

  //operator RECT&      ()       { return *this; }
  //operator RECT const&() const { return *this; }
  Rect operator +( cref<Rect> in_rect )
  {
    return
      Rect( left   + in_rect.left
          , top    + in_rect.top
          , right  + in_rect.right
          , bottom + in_rect.bottom
          );
  }
  Rect operator -( cref<Rect> in_rect )
  {
    return
      Rect( left   - in_rect.left
          , top    - in_rect.top
          , right  - in_rect.right
          , bottom - in_rect.bottom
          );
  }
  Rect operator *( cref<Rect> in_rect )
  {
    return
      Rect( left   * in_rect.left
          , top    * in_rect.top
          , right  * in_rect.right
          , bottom * in_rect.bottom
          );
  }
  Rect operator /( cref<Rect> in_rect )
  {
    return
      Rect( left   / in_rect.left
          , top    / in_rect.top
          , right  / in_rect.right
          , bottom / in_rect.bottom
          );
  }
  void operator +=( cref<Rect> in_rect ){ *this = *this + in_rect; }
  void operator -=( cref<Rect> in_rect ){ *this = *this - in_rect; }
  void operator *=( cref<Rect> in_rect ){ *this = *this * in_rect; }
  void operator /=( cref<Rect> in_rect ){ *this = *this / in_rect; }

  Rect operator +( long add ) const
  {
    return Rect( left+add, top+add, right+add, bottom+add );
  }
  Rect operator -( long sub ) const
  {
    return Rect( left-sub, top-sub, right-sub, bottom-sub );
  }
  Rect operator *( long mul ) const
  {
    return Rect( left*mul, top*mul, right*mul, bottom*mul );
  }
  Rect operator /( long div ) const
  {
    return Rect( left/div, top/div, right/div, bottom/div );
  }
  void operator +=( long add ){ *this = *this + add; }
  void operator -=( long sub ){ *this = *this - sub; }
  void operator *=( long mul ){ *this = *this * mul; }
  void operator /=( long div ){ *this = *this / div; }

  bool operator == ( cref<Rect> in_rect ) const
  {
    return
      (left   == in_rect.left)  &&
      (top    == in_rect.top)  &&
      (right  == in_rect.right)  &&
      (bottom == in_rect.bottom);
  }
  bool operator != ( cref<Rect> in_rect ) const
  {
    return !(*this == in_rect);
  }
  Rect& operator = (cref<Rect> in_rect )
  {
    left   = in_rect.left;
    top    = in_rect.top;
    right  = in_rect.right;
    bottom = in_rect.bottom;
    return *this;
  }

  bool Hit( long in_X, long in_Y ) const
  {
    return ((left <= in_X  &&  in_X <= right)  &&  (top <= in_Y  &&  in_Y <= bottom));
  }
  long GetWidth() const { return right - left; }
  long GetHeight() const { return bottom - top; }
  void Set( long in_left, long in_top, long in_right, long in_bottom )
  {
    this->left   = in_left;
    this->top    = in_top;
    this->right  = in_right;
    this->bottom = in_bottom;
  }
};


// UNICODE <-> MultiByte変換用関数
//template<class T0, class T1>
//inline T0 const& ChangeCodeStrings( T0& str0, const T1& str1 )
//{
//  if( &str0 != &str1 )
//  {
//    str0 = str1;
//  }
//  return str1;
//}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int GetCpuNum();


/// @cond
namespace zzz_ {
DWORD OutputLastErrorMessage_( DWORD in_error_code );
}
/// @endcond
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DWORD OutputLastErrorMessage();


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t GetModuleName( char_ptr out_name, size_t in_buf_size );

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t GetModuleName( wchar_t_ptr out_name, size_t in_buf_size );

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t GetModuleName( Core::StringA& out_string );

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t GetModuleName( Core::StringW& out_string );

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t GetModuleName( std::string& out_string );

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t GetModuleName( std::wstring& out_string );

// _____________________________________________________________________________
/// @brief モジュール名を取得。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<class T>
T GetModuleName()
{
  T str;
  GetModuleName( str );
  return str;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 独自フォーマットによるメッセージ出力
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//void CegWinOutputDebugMessage( char_cptr in_msg );
template<class... T_Params>
void WinOutputExA( char_cptr in_format, char_cptr in_func_name, T_Params&&... args )
{
  thread_local CPL::Core::StringA
    cpl_module_name_( CPL::WIN::GetModuleName<CPL::Core::StringA>() );
  Core::StringA format( "%s: [P:%d][T:%d][%u][%s]: " );
  format << in_format;
  CPL_OUTPUT_A
    ( format
    , (char_cptr)cpl_module_name_
    , (int)GetCurrentProcessId()
    , (int)CPL::Core::GetThreadID()
    , (int)timeGetTime()
    , in_func_name
    , std::forward<T_Params>(args)...
    );
}

template<class... T_Params>
void WinOutputExW( wchar_t_cptr in_format, wchar_t_cptr in_func_name, T_Params&&... args )
{
  thread_local CPL::Core::StringW
    cpl_module_name_( CPL::WIN::GetModuleName<CPL::Core::StringW>() );
  Core::StringW format( L"%S: [P:%d][T:%d][%u][%S]: " );
  format << in_format;
  CPL_OUTPUT_W
    ( format
    , (wchar_t_cptr)cpl_module_name_
    , (int)GetCurrentProcessId()
    , (int)CPL::Core::GetThreadID()
    , (int)timeGetTime()
    , in_func_name
    , std::forward<T_Params>(args)...
    );
}

template<class... T_Params>
void WinOutputEx( str_cptr in_format, str_cptr in_func_name, T_Params&&... args )
{
#ifdef CPL_UNICODE
  WinOutputExW( in_format, in_func_name, std::forward<T_Params>(args)... );
#else
  WinOutputExA( in_format, in_func_name, std::forward<T_Params>(args)... );
#endif
}

#define CPL_WIN_OUTPUT_EX( format, ... )   CPL::WIN::WinOutputEx( format, SwS(CPL_FUNCNAME) CPL_ARGS(__VA_ARGS__) )
#define CPL_WIN_OUTPUT_EX_A( format, ... ) CPL::WIN::WinOutputExA( format, CPL_FUNCNAME CPL_ARGS(__VA_ARGS__) )
#define CPL_WIN_OUTPUT_EX_W( format, ... ) CPL::WIN::WinOutputExW( format, ToUC(CPL_FUNCNAME) CPL_ARGS(__VA_ARGS__) )

#ifdef CPL_DEBUG

#define CPL_DBG_WIN_OUTPUT_EX( format, ... )   CPL_WIN_OUTPUT_EX( format CPL_ARGS(__VA_ARGS__) )
#define CPL_DBG_WIN_OUTPUT_EX_A( format, ... ) CPL_WIN_OUTPUT_EX_A( format CPL_ARGS(__VA_ARGS__) )
#define CPL_DBG_WIN_OUTPUT_EX_W( format, ... ) CPL_WIN_OUTPUT_EX_W( format CPL_ARGS(__VA_ARGS__) )

#else

#define CPL_DBG_WIN_OUTPUT_EX( format, ... )
#define CPL_DBG_WIN_OUTPUT_EX_A( format, ... )
#define CPL_DBG_WIN_OUTPUT_EX_W( format, ... )

#endif


// _____________________________________________________________________________
/// @brief DLL寿命管理マネージャ
///
// *****************************************************************************
class DllManager 
{
  HMODULE m_hdl_dll;
public:
  DllManager( str_cptr in_dll_name )
    : m_hdl_dll( LoadLibrary( in_dll_name ) )
  {}
  ~DllManager()
  {
    FreeLibrary( m_hdl_dll );
  }

  bool IsEmpty() const { return m_hdl_dll == nullptr; }

  void* GetAddress( char_cptr in_proc_name )
  {
    return reinterpret_cast<void*>(GetProcAddress( m_hdl_dll, in_proc_name ));
  }
};



#ifdef CPL_DEBUG

// コンストラクタ／デストラクタ呼び出し時にメッセージを出力するためのデバッグ用メッセージクラス
class DbgMsg {
  Core::StringA msg;
public:
  DbgMsg( char_cptr in_msg );
  DbgMsg( char_cptr in_msg0, char_cptr in_msg1 );
  DbgMsg( char_cptr in_msg0, char_cptr in_msg1, char_cptr in_msg2 );
  ~DbgMsg();
};

#else

class DbgMsg {
public:
  DbgMsg( char_cptr in_msg ){}
  DbgMsg( char_cptr in_msg0, char_cptr in_msg1 ){}
  DbgMsg( char_cptr in_msg0, char_cptr in_msg1, char_cptr in_msg2 ){}
  ~DbgMsg(){}
};

#endif




} // namespace WIN
} // namespace CPL
