///////////////////////////////////////
// ECCSkelton
// SDIウィンドウクラスCSDI定義ファイル
// Copyright (c) August, 2022
//        By Ysama
///////////////////////////////////////

class CSDI : public CWND
{
protected:
	HANDLE	m_hMutex;
	WCHAR	m_UniqName[MAX_PATH];

public:		//メンバー変数
	MSG			m_Msg;						//ウィンドウメッセージ
	HACCEL		m_Accel = 0;				//Acceleratorの有無
	HWND		m_hDlg = 0;					//IsDialogMessage()使用時に使う

public:		//メンバー関数
	CSDI(WCHAR*);
	~CSDI();
	bool Init(LPWSTR, HINSTANCE, LPWSTR,
			LPWSTR, LPWSTR, HBRUSH);				//ウィンドウ初期化関数
	bool Create(LPWSTR,
			DWORD, DWORD,
			HWND, HMENU,
			int, int,
			int, int);						//ウィンドウ作成関数
	static LRESULT CALLBACK DummyProc(HWND, UINT, WPARAM, LPARAM);	//ダミーのコールバック関数
	virtual LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);	//本来のコールバック関数
	virtual bool OnCreate(WPARAM, LPARAM);			//WM_CREATE
	virtual bool OnActivate(WPARAM, LPARAM);		//WM_ACTIVATE
	virtual bool OnEnable(WPARAM, LPARAM);			//WM_ENABLE
	virtual bool OnTimer(WPARAM, LPARAM);			//WM_TIMER
	virtual bool OnSetFocus(WPARAM, LPARAM);		//WM_SETFOCUS
	virtual	bool OnNotify(WPARAM, LPARAM);			//WM_NOTIFY
	virtual	bool OnCommand(WPARAM, LPARAM);			//WM_COMMAND
	virtual bool OnKeyDown(WPARAM, LPARAM);			//WM_KEYDOWN
	virtual bool OnKeyUp(WPARAM, LPARAM);			//WM_KEYUP
	virtual bool OnChar(WPARAM, LPARAM);			//WM_CHAR
	virtual bool OnLButtonDblclk(WPARAM, LPARAM);	//WM_LBUTTONDBLCLK
	virtual bool OnLButtonDown(WPARAM, LPARAM);		//WM_LBUTTONDOWN
	virtual bool OnLButtonUp(WPARAM, LPARAM);		//WM_LBUTTONUP
	virtual bool OnMButtonDblclk(WPARAM, LPARAM);	//WM_MBUTTONDBLCLK
	virtual bool OnMButtonDown(WPARAM, LPARAM);		//WM_MBUTTONDOWN
	virtual bool OnMButtonUp(WPARAM, LPARAM);		//WM_MBUTTONUP
	virtual bool OnMouseMove(WPARAM, LPARAM);		//WM_MOUSEMOVE
	virtual bool OnRButtonDblclk(WPARAM, LPARAM);	//WM_RBUTTONDBLCLK
	virtual bool OnRButtonDown(WPARAM, LPARAM);		//WM_RBUTTONDOWN
	virtual bool OnRButtonUp(WPARAM, LPARAM);		//WM_RBUTTONUP
	virtual bool OnPaint(WPARAM, LPARAM);			//WM_PAINT
	virtual bool OnEraseBkgnd(WPARAM, LPARAM);		//WM_ERASEBKGND
	virtual bool OnMove(WPARAM, LPARAM);			//WM_MOVE
	virtual bool OnSize(WPARAM, LPARAM);			//WM_SIZE
	virtual bool OnHScroll(WPARAM, LPARAM);			//WM_HSCROLL
	virtual bool OnVScroll(WPARAM, LPARAM);			//WM_VSCROLL
	virtual bool OnDropFiles(WPARAM, LPARAM);		//WM_DROPFILES
	virtual bool OnGetMaxInfo(WPARAM, LPARAM);		//WM_GETMAXINFO
	virtual	bool OnClose(WPARAM, LPARAM);			//WM_CLOSE
	virtual	bool OnDestroy(WPARAM, LPARAM);			//WM_DESTROY
	virtual bool OnOthers(UINT, WPARAM, LPARAM);	//その他のメッセージ
	bool InclAccel(LPWSTR);					//アクセラレーター使用時
	UINT Loop();							//メッセージループ関数
	bool IsOnlyOne();						//２重起動防止関数
};

//CSDIのコンストラクター
CSDI::CSDI(WCHAR* UName) {

	wsprintfW(m_UniqName, L"%d", UName);
}

//CSDIのデストラクター
CSDI::~CSDI() {

	if(m_hMutex)
		CloseHandle(m_hMutex);
}

//初期化関数（ウィンドウ用）
bool CSDI::Init(LPWSTR ClassName, HINSTANCE hInstance,
				LPWSTR Menu = L"\0",
				LPWSTR Icon = L"IDI_ICON", LPWSTR cur = (LPWSTR)IDC_ARROW,
				HBRUSH brush = (HBRUSH)(COLOR_WINDOW + 1)) {

	WNDCLASSEXW WndClass;
	//ウィンドウクラスの登録
	WndClass.cbSize	= sizeof(WNDCLASSEXW);				//この構造体のサイズ
	WndClass.style	= CS_HREDRAW | CS_VREDRAW |
					  CS_DBLCLKS ;						//移動、サイズ変更時再描画、ダブルクリック使用可能
	WndClass.lpfnWndProc= DummyProc;					//コールバック関数ポインター
	WndClass.cbClsExtra	= 0;							//この構造体以降に置くバイト数
	WndClass.cbWndExtra	= 0;							//インスタンス以降に置くバイト数
	WndClass.hInstance	= m_hInstance = hInstance;		//このウィンドウのインスタンス
	WndClass.hIcon		= LoadIconW(hInstance, Icon);	//このウィンドウクラスのアイコン
	WndClass.hCursor	= LoadCursorW(NULL, cur);		//このウィンドウクラスのカーソル
	WndClass.hbrBackground	= brush;					//このウィンドウクラスの背景色
	WndClass.lpszMenuName	= Menu;					//このウィンドウクラスのメニュー
	WndClass.lpszClassName	= m_lpClassName
							= ClassName;				//このウィンドウクラスの名称
	WndClass.hIconSm	= LoadIconW(hInstance, Icon);	//最小化時の小さいアイコン
	if(!RegisterClassExW(&WndClass)) {
		MessageBoxW(0, L"ウィンドウを登録できませんでした。", L"エラー",
				MB_OK | MB_ICONEXCLAMATION);
		return FALSE;
	}
	return TRUE;
}

//ウィンドウ作成関数
bool CSDI::Create(LPWSTR WindowName = L"SDIWindow",
				DWORD Style = WS_OVERLAPPEDWINDOW,
				DWORD ExStyle = 0,
				HWND hParent = 0, HMENU hMenu = 0,
				int x = CW_USEDEFAULT, int y = CW_USEDEFAULT,
				int w = CW_USEDEFAULT, int h = CW_USEDEFAULT) {

	m_hWnd = CreateWindowExW(m_dwExStyle = ExStyle,
							m_lpClassName,
							m_lpWindowName = WindowName,
							m_dwStyle = Style,
							m_x = x,
							m_y = y,
							m_Width = w,
							m_Height = h,
							m_hParent = hParent,
							m_hMenu = hMenu,
							m_hInstance,
							m_lpParam = (LPVOID)this	//CSDIクラスインスタンスのアドレス
	);
	if(!m_hWnd) {
		MessageBoxW(0, L"ウィンドウを作れませんでした。", L"エラー",
				MB_OK | MB_ICONEXCLAMATION);
		return FALSE;
	}
	//ウィンドウの表示と再描画
	ShowWindow(m_hWnd, SW_SHOWNORMAL);
	UpdateWindow(m_hWnd);
	return TRUE;
}

//ダミーコールバック関数
LRESULT CALLBACK CSDI::DummyProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) {

	if(Message == WM_CREATE) {	//最初のWM_CREATEで、
		//m_hWndを取得し（これをしないとOnCreateで失敗する）、
		((CSDI*)((CREATESTRUCT*)lParam)->lpCreateParams)->m_hWnd = hWnd;
		//クラスインスタンスポインターを受け取りウィンドウプロパティに書込む（重要）
		SetPropW(hWnd, L"CPP_CLASS", (HANDLE)((CREATESTRUCT*)lParam)->lpCreateParams);
	}
	//Initメンバー関数が書き込んだこのクラスのインスタンスへのポインターを読む
	CSDI* pWnd = (CSDI*)GetPropW(hWnd, L"CPP_CLASS");
	//もしそれが読み込めたら
	if(pWnd) {
		//本来のコールバック関数（WndProc）を呼ぶ
		LRESULT lResult = pWnd->WndProc(hWnd, Message, wParam, lParam);
		//ウィンドウ破壊時にウィンドウプロパティを廃棄する
		if(Message == WM_DESTROY)
			RemovePropW(hWnd, L"CPP_CLASS");
		return lResult;
	}
	else
		//ウィンドウズの処理に任せる
		return DefWindowProcW(hWnd, Message, wParam, lParam);
}

//本来のコールバック関数（仮想関数）
LRESULT CALLBACK CSDI::WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) {

	bool Done = FALSE;
	switch(Message) {
		case WM_CREATE:
			Done = OnCreate(wParam, lParam);
			break;
		case WM_ACTIVATE:
			Done = OnActivate(wParam, lParam);
			break;
		case WM_ENABLE:
			Done = OnEnable(wParam, lParam);
			break;
		case WM_TIMER:
			Done = OnTimer(wParam, lParam);
			break;
		case WM_SETFOCUS:
			Done = OnSetFocus(wParam, lParam);
			break;
		case WM_NOTIFY:
			Done = OnNotify(wParam, lParam);
			break;
		case WM_COMMAND:
			Done = OnCommand(wParam, lParam);
			break;
		case WM_KEYDOWN:
			Done = OnKeyDown(wParam, lParam);
			break;
		case WM_KEYUP:
			Done = OnKeyUp(wParam, lParam);
			break;
		case WM_CHAR:
			Done = OnChar(wParam, lParam);
			break;
		case WM_LBUTTONDBLCLK:
			Done = OnLButtonDblclk(wParam, lParam);
			break;
		case WM_LBUTTONDOWN:
			Done = OnLButtonDown(wParam, lParam);
			break;
		case WM_LBUTTONUP:
			Done = OnLButtonUp(wParam, lParam);
			break;
		case WM_MBUTTONDBLCLK:
			Done = OnMButtonDblclk(wParam, lParam);
			break;
		case WM_MBUTTONDOWN:
			Done = OnMButtonDown(wParam, lParam);
			break;
		case WM_MBUTTONUP:
			Done = OnMButtonUp(wParam, lParam);
			break;
		case WM_MOUSEMOVE:
			Done = OnMouseMove(wParam, lParam);
			break;
		case WM_RBUTTONDBLCLK:
			Done = OnRButtonDblclk(wParam, lParam);
			break;
		case WM_RBUTTONDOWN:
			Done = OnRButtonDown(wParam, lParam);
			break;
		case WM_RBUTTONUP:
			Done = OnRButtonUp(wParam, lParam);
			break;
		case WM_PAINT:
			Done = OnPaint(wParam, lParam);
			break;
		case WM_ERASEBKGND:
			Done = OnEraseBkgnd(wParam, lParam);
			break;
		case WM_MOVE:
			Done = OnMove(wParam, lParam);
			break;
		case WM_SIZE:
			Done = OnSize(wParam, lParam);
			break;
		case WM_HSCROLL:
			Done = OnHScroll(wParam, lParam);
			break;
		case WM_VSCROLL:
			Done = OnVScroll(wParam, lParam);
			break;
		case WM_DROPFILES:
			Done = OnDropFiles(wParam, lParam);
			break;
		case WM_GETMINMAXINFO:
			Done = OnGetMaxInfo(wParam, lParam);
			break;
		case WM_CLOSE:
			Done = OnClose(wParam, lParam);
			if(Done) {
				DestroyWindow(m_hWnd);
				return TRUE;
			}
			else
				return FALSE;
		case WM_DESTROY:
			Done = OnDestroy(wParam, lParam);
			break;
	default:
			Done = OnOthers(Message, wParam, lParam);
			break;
	}
	if(Done)
		return 0;
	else
		return DefWindowProcW(hWnd, Message, wParam, lParam);
}

//WM_CREATE時処理（仮想関数）
bool CSDI::OnCreate(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_ACTIVATE時処理（仮想関数）
bool CSDI::OnActivate(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_ENABLE時処理（仮想関数）
bool CSDI::OnEnable(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_TIMER時処理（仮想関数）
bool CSDI::OnTimer(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_SETFOCUS時処理（仮想関数）
bool CSDI::OnSetFocus(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_NOTIFY時処理（仮想関数）
bool CSDI::OnNotify(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_COMMAND時処理（仮想関数）
bool CSDI::OnCommand(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_KEYDOWN時処理（仮想関数）
bool CSDI::OnKeyDown(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_KEYUP時処理（仮想関数）
bool CSDI::OnKeyUp(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_CHAR時処理（仮想関数）
bool CSDI::OnChar(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_LBUTTONDBLCLK時処理（仮想関数）
bool CSDI::OnLButtonDblclk(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_LBUTTONDOWN時処理（仮想関数）
bool CSDI::OnLButtonDown(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_LButtonUp時処理（仮想関数）
bool CSDI::OnLButtonUp(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_MBUTTONDBLCLK時処理（仮想関数）
bool CSDI::OnMButtonDblclk(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_MBUTTONDOWN時処理（仮想関数）
bool CSDI::OnMButtonDown(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_MButtonUp時処理（仮想関数）
bool CSDI::OnMButtonUp(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_MOUSEMOVE時処理（仮想関数）
bool CSDI::OnMouseMove(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_RBUTTONDBLCLK時処理（仮想関数）
bool CSDI::OnRButtonDblclk(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_RBUTTONDOWN時処理（仮想関数）
bool CSDI::OnRButtonDown(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_RButtonUp時処理（仮想関数）
bool CSDI::OnRButtonUp(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_PAINT時処理（仮想関数）
bool CSDI::OnPaint(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_ERASEBKGND時処理（仮想関数）
bool CSDI::OnEraseBkgnd(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_MOVE時処理（仮想関数）
bool CSDI::OnMove(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_SIZE時処理（仮想関数）
bool CSDI::OnSize(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_HSCROLL時処理（仮想関数）
bool CSDI::OnHScroll(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_VSCROLL時処理（仮想関数）
bool CSDI::OnVScroll(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_DROPFILES時処理（仮想関数）
bool CSDI::OnDropFiles(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_GETMINMAXINFO時処理（仮想関数）
bool CSDI::OnGetMaxInfo(WPARAM wParam, LPARAM lParam) {
	return FALSE;
}

//WM_CLOSE時処理(仮想関数）
bool CSDI::OnClose(WPARAM wParam, LPARAM lParam) {

	return TRUE;
}

//WM_DESTROY時処理(仮想関数）
bool CSDI::OnDestroy(WPARAM wParam, LPARAM lParam) {

	PostQuitMessage(0);
	return TRUE;
}

//その他のメッセージ処理(仮想関数）
bool CSDI::OnOthers(UINT Message, WPARAM wParam, LPARAM lParam) {

	return FALSE;
}

//アクセラレーター使用時に呼ぶ
bool CSDI::InclAccel(LPWSTR Name) {

	if(!(m_Accel = LoadAcceleratorsW(m_hInstance, Name))) {
		MessageBoxW(NULL, L"アクセラレーターの読込に失敗しました",
					L"エラー", MB_OK);
		return FALSE;
	}
	else
		return TRUE;
}

//メインウィンドウの場合のメッセージループ
UINT CSDI::Loop() {

	while(GetMessageW(&m_Msg, NULL, 0, 0)) {
		//モードレスダイアログがある場合
		if(!IsDialogMessageW(m_hDlg, &m_Msg) &&
		//アクセラレーター処理
			!TranslateAcceleratorW(m_hWnd, m_Accel, &m_Msg)) {
			TranslateMessage(&m_Msg);
			DispatchMessageW(&m_Msg);
		}
	}
	return m_Msg.wParam;
}

bool CSDI::IsOnlyOne() {

	m_hMutex = CreateMutexW(NULL, TRUE, m_UniqName);
	if (!m_hMutex)
		return FALSE;
	// もしGetLastError()が、ERROR_ALREADY_EXISTSなら、
	// すでにアプリケーションが起動している
	if (GetLastError() == ERROR_ALREADY_EXISTS) {
    //二重起動時の処理
    	return FALSE;
	}
	return TRUE;
}
