// CFullscreenControlerクラス
// WIN32APIを利用した完全なフルスクリーンモードを提供します。

#include "Fullscreen.h"

/*
#include <d3d9.h>
#pragma comment(lib,"d3d9.lib")
static LPDIRECT3D9			g_lpD3D = NULL;
static LPDIRECT3DDEVICE9	g_lpD3DDEV = NULL;
*/

// グローバル変数
static windowstate	WindowState[MAX_WINDOWNUMBER];
static int			iWindowNumber;					// 画面上のウインドウ数

// ウインドウ位置保存のコールバック関数
BOOL CALLBACK EnumWindowsSaveFunc(HWND hWnd, LPARAM lParam)
{
	char szWindowName[MAX_NAMELENGTH];
	char szClassName[MAX_NAMELENGTH];

	// ウインドウ名とクラス名の取得
	GetWindowText(hWnd, szWindowName, MAX_NAMELENGTH);
	GetClassName(hWnd, szClassName, MAX_NAMELENGTH);

	// 見えているウインドウだけをセレクト
	if(IsWindowVisible(hWnd) &&
		GetWindow(hWnd, GW_OWNER) == NULL &&
		lstrlen(szWindowName) > 0 &&
		lstrcmp(szClassName, "Progman") != 0 ){

		// ウインドウ位置を保存
		if(iWindowNumber < MAX_WINDOWNUMBER){
			WindowState[iWindowNumber].WindowPlacement.length = sizeof(WINDOWPLACEMENT);
			GetWindowPlacement(hWnd, &WindowState[iWindowNumber].WindowPlacement);
			WindowState[iWindowNumber].hWnd = hWnd;

			++iWindowNumber;
		}
	}

	return TRUE;
}

// ウインドウ位置再現のコールバック関数
BOOL CALLBACK EnumWindowsLoadFunc(HWND hWnd, LPARAM lParam)
{
	char szWindowName[MAX_NAMELENGTH];
	int  i;

	// ウインドウ名の取得
	GetWindowText(hWnd, szWindowName, MAX_NAMELENGTH);

	// 保存されているウインドウを検索
	for(i = 0; i < iWindowNumber; i++){
		if(WindowState[i].hWnd == hWnd) break;
	}

	// 見つかったら元の場所に戻す
	if(i != iWindowNumber){
		SetWindowPlacement(hWnd, &WindowState[i].WindowPlacement);
	}

	return TRUE;
}

// プライベートコンストラクタ
CFullscreenControler::CFullscreenControler()
{
	pFlWnd = NULL;
	hFlWnd = NULL;
	pFkWnd = NULL;
	fscW = fscH = 0;

	return;
}
// プライベートデストラクタ
CFullscreenControler::~CFullscreenControler()
{
	return;
}

// ウインドウ位置保存
BOOL CFullscreenControler::SaveWindowPosition()
{
	iWindowNumber = 0;
	if(EnumWindows(EnumWindowsSaveFunc, NULL) == 0)	return FALSE;

	return TRUE;
}

// ウインドウ位置再現
BOOL CFullscreenControler::LoadWindowPosition()
{
	if(EnumWindows(EnumWindowsLoadFunc, NULL) == 0)	return FALSE;

	return TRUE;
}

// フルスクリーン化
BOOL CFullscreenControler::ChangeScreen(HWND hWnd, int iFlag, int nWidth, int nHeight)
{
	DEVMODE DeviceMode;
	memset(&DeviceMode, 0, sizeof(DeviceMode));

	// フルスクリーンにするですよ
	if(iFlag == SCMODE_FULLSCREEN){

		// 現在の色数を取得
		HDC hDC = GetDC(hWnd);
		int ScreenColors = GetDeviceCaps(hDC, BITSPIXEL);
		int ScreenWidth = GetDeviceCaps(hDC, HORZRES);
		int ScreenHeight = GetDeviceCaps(hDC, VERTRES);
		int ScreenRefresh = GetDeviceCaps(hDC, VREFRESH);
		ReleaseDC(hWnd, hDC);

		// 今のスクリーンサイズが一緒ならいじらない
		if(ScreenWidth != nWidth || ScreenHeight != nHeight) {
			// フルスクリーンモードを探す
			int	i = 0, just = -1, overlap = 0;
			while(true){
				if(!EnumDisplaySettings(NULL, i, &DeviceMode)) break;
				if(DeviceMode.dmPelsWidth == (unsigned int)nWidth &&
					DeviceMode.dmPelsHeight	== (unsigned int)nHeight &&
					DeviceMode.dmBitsPerPel	== (DWORD)ScreenColors &&
					DeviceMode.dmDisplayFrequency == 60) {
						just = i;
						break;
				}
				if(DeviceMode.dmPelsWidth >= (unsigned int)nWidth &&
					DeviceMode.dmPelsHeight	>= (unsigned int)nHeight &&
					DeviceMode.dmBitsPerPel	== (DWORD)ScreenColors &&
					DeviceMode.dmDisplayFrequency == 60) {
						just = i;
						overlap = 1;
						break;
				}
				i++;
			}
			if(just == -1) return FALSE;
			EnumDisplaySettings(NULL, just, &DeviceMode);
			fscW = DeviceMode.dmPelsWidth;
			fscH = DeviceMode.dmPelsHeight;
			DeviceMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;

			// フルスクリーンに変更
			if(ChangeDisplaySettings(&DeviceMode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
				fscW = fscH = 0;
				return FALSE;
			}
		} else {
			fscW = nWidth;
			fscH = nHeight;
		}
		// 常に最前面設定用
		SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0,
			SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
	}

	// 元の画面モードに戻すですよ
	if(iFlag == SCMODE_WINDOW) {
		ChangeDisplaySettings(NULL, 0);
		// 常に最前面設定を解除
		SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
			SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
		fscW = fscH = 0;
	}

	return TRUE;
}

// バインド初期化
void CFullscreenControler::init(Fl_Window *p_main_win, fk_Window *p_draw_win)
{
	// ポインタ受け取り
	pFlWnd = p_main_win;		// Fl_Window操作用にポインタを取得
	hFlWnd = fl_xid(pFlWnd);	// 解像度変更用にウィンドウハンドルを取得
	pFkWnd = p_draw_win;
	mode = false;

	return;
}

/*
bool CFullscreenControler::changeByD3D(bool argMode)
{
	// D3D9 オブジェクトはグローバルでひとつだけ
	if(g_lpD3D == NULL) {
		if(NULL == (g_lpD3D = Direct3DCreate9(D3D_SDK_VERSION))) {
			MessageBox(hFlWnd, "Direct3D の作成に失敗しました。", "FK_D3D", MB_OK | MB_ICONSTOP);
			return false;
		}
	}

	// 現在の画面モードを取得
    D3DDISPLAYMODE			d3ddm;
    if(FAILED(g_lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm))) return false;

	// Direct3D 初期化パラメータの設定
	D3DPRESENT_PARAMETERS	d3dpp;
	ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));

	// フルスクリーンモードとしてパラメータをセット
	d3dpp.BackBufferCount = 0;
	d3dpp.Windowed = FALSE;
	d3dpp.BackBufferWidth = nW;
	d3dpp.BackBufferHeight = nH;
	// 現在の画面モードを使用
	d3dpp.BackBufferFormat = d3ddm.Format;
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hFlWnd;
	// Zバッファとかはいらない
	d3dpp.EnableAutoDepthStencil = FALSE;
	// 垂直同期は待たない
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	// デバイスの作成は一度だけ、2回目以降は追加スワップチェインを作成
	if(g_lpD3DDEV == NULL  && argMode) {
		// デバイス作成は2回試行する
		for(int i = 0; i < 1; i++) {
			// デバイスの作成 - T&L HAL
			if(FAILED(g_lpD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hFlWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &g_lpD3DDEV))) {
				// 失敗したので HAL で試行
				if(FAILED(g_lpD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hFlWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_lpD3DDEV))) {
					// 失敗したので REF で試行
					if(FAILED(g_lpD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hFlWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_lpD3DDEV))) {
						// 結局失敗した
						MessageBox(hFlWnd, "Direct3D の初期化に失敗しました。", "FK_D3D", MB_OK | MB_ICONSTOP);
						return false;
					}
				}
			}
		}
	} else if(g_lpD3DDEV != NULL && !argMode) {
		g_lpD3DDEV->Release();
		g_lpD3DDEV = NULL;
		g_lpD3D->Release();
		g_lpD3D = NULL;
	}

	return true;
}
*/

// モードチェック
bool CFullscreenControler::isFullscreen()
{
	return mode;
}

// フルスクリーン化
bool CFullscreenControler::changeToFullscreen()
{
	if(mode == false) {
		// ウィンドウサイズ保存
		SaveWindowPosition();
		nWndX = pFlWnd->x();	nWndY = pFlWnd->y();
		nWndW = pFkWnd->w();	nWndH = pFkWnd->h();
		// 解像度変更
		pFlWnd->fullscreen();	// Fl_Windowのフルスクリーン(ラベルバー消去)
		if(ChangeScreen(hFlWnd, SCMODE_FULLSCREEN, nWndW, nWndH) == TRUE) {
			pFlWnd->resize(0, 0, fscW, fscH);	// リサイズ
			pFlWnd->color(FL_BLACK);
			pFkWnd->resizeWindow((fscW-nWndW)/2, (fscH-nWndH)/2, nWndW, nWndH);
			mode = true;

			return true;
		} else {
			pFlWnd->fullscreen_off(nWndX, nWndY, nWndW, nWndH);
			LoadWindowPosition();
			MessageBox(hFlWnd, "この環境ではフルスクリーン化できませんでした。\n"
				"画面のプロパティを確認するか、ウィンドウモードでプレイしてください。", "フルスクリーン化失敗", 0);

			return false;
		}
	} else {
		return true;
	}

}

// ウィンドウ化
void CFullscreenControler::changeToWindow()
{
	if(mode == true) {
		pFlWnd->fullscreen_off(nWndX, nWndY, nWndW, nWndH);
		pFkWnd->resizeWindow(0, 0, nWndW, nWndH);
		ChangeScreen(hFlWnd, SCMODE_WINDOW, nWndW, nWndH);
		LoadWindowPosition();

		mode = false;
	}

	return;
}
