/***************************************************************/
//
//
//		torikuro	[main.cpp]
//
//									Author	kazuki tanaka
//									Date	2016 07/01
/*---------------------------------------------------------------
■Update : 2016/04/18
			メッセージボックスを表示
■Update : 2016/04/25

■Update : 2016/06/15
			FPSの表示

/*---------------------------------------------------------------
	インクルードファイル
---------------------------------------------------------------*/

#include "main.h"
#include "select_input_device.h"
#include "input.h"
#include "mouse.h"
#include "gamepad.h"
#include "sound.h"

#include "title.h"
#include "tutorial.h"
#include "game.h"
#include "result.h"
#include "ending.h"

#include "fade.h"									// シーンループに必要

#include "backbuffer.h"


/*---------------------------------------------------------------
	マクロ定義
---------------------------------------------------------------*/

// 関数ポインタの型名
typedef void (*MadeFunc)( void );

//#define _MODE_WIREFRAME_							// ワイヤーフレームモードで起動
/*---------------------------------------------------------------
	定数定義
---------------------------------------------------------------*/
#define CLASS_NAME "LEAD"							// ウィンドウクラスの名前
const char WINDOW_NAME[] = { "LEAD ver1.03" };		// ウィンドウの名前

#define TIMER_INTERVAL (1000/60)					// 発生間隔60分の1秒

//#define WIN_STYLE (WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MINIMIZEBOX ^ WS_MAXIMIZEBOX )
#define WIN_STYLE (WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MINIMIZEBOX )
													// Windowのマクロ


// 初期化アドレス
MadeFunc pInitFunc[] ={
	InitTitle,
	InitTutorial,
	InitGame,
	InitResult,
};

// 終了アドレス
MadeFunc pUninitFunc[] ={
	UninitTitle,
	UninitTutorial,
	UninitGame,
	UninitResult,
};

// 更新アドレス
MadeFunc pUpdateFunc[] ={
	UpdateTitle,
	UpdateTutorial,
	UpdateGame,
	UpdateResult,
};

// 描画アドレス
MadeFunc pDrawFunc[] ={
	DrawTitle,
	DrawTutorial,
	DrawGame,
	DrawResult,
};

/*---------------------------------------------------------------
	プロトタイプ宣言
---------------------------------------------------------------*/

LRESULT CALLBACK WndProc(HWND hWnd,					// ウィンドウプロシージャ関数
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam);

HRESULT Init(										// DirectX9の初期化設定
	HINSTANCE hInstance,
	HWND hWnd,
	BOOL bWindow);

void UnInit(void);									// 終了処理
void Update( void );								// 更新処理
void Draw ( void );									// 画面の描画

#ifdef _DEBUG
void DrawFPS( void );								// FPS表示用
#endif

/*----------------------------------------------------------------
	グローバル変数
----------------------------------------------------------------*/

LPDIRECT3D9 g_pD3D=NULL;							// デバイス作成用

LPDIRECT3DDEVICE9 g_pD3DDevice=nullptr;				// Direct3Dデバイスインターフェースの取得

#ifdef _DEBUG
LPD3DXFONT g_pFont = nullptr;						// FPSをウィンドウに書き込むためのインターフェース
#endif

SCENE sceneSelect = SCENE_TITLE;					// シーンの選択・制御

int g_nCountFPS = 0;								// FPSカウント用


// Win32アプリケーション
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPravInstance,LPSTR IpCmdLine, int nCmdShow)
{

	UNREFERENCED_PARAMETER(hPravInstance);
	UNREFERENCED_PARAMETER(IpCmdLine);

	RECT cr = {0,0,SCREEN_WIDTH,SCREEN_HEIGHT};


	WNDCLASSEX wcex={
		sizeof(WNDCLASSEX),							//WNDCALSSEXのメモリサイズを指定
		CS_CLASSDC,									//表示するウィンドウのスタイルを設定
		WndProc,									// ☆ ウィンドウプロシージャのアドレス(関数名)を指定
		0,											//通常は使用しないので"0"を指定
		0,											//通常は使用しないので"0"を指定x
		hInstance,									// ☆ WinMainのパラメータのインスタンスハンドルを設定
		LoadIcon(hInstance, TEXT("KITTY")),			//使用するアイコンを指定(Windowが持っているアイコンを使う)
		LoadCursor(NULL, IDC_CROSS),				//マウスカーソルを指定
		(HBRUSH)(COLOR_WINDOW + 1),					//ウィンドウのクライアント領域の背景色を設定
		NULL,										//Windowsにつけるメニューを設定
		CLASS_NAME,									// ☆ ウィンドウクラスの名前
		NULL										//小さいアイコンが設定された場合の情報を記述
	};


	// ウィンドウクラスの登録
	RegisterClassEx(&wcex);

	HWND hWnd;										//ウィンドウハンドル
	MSG msg;										//ウィンドウメッセージ

	AdjustWindowRect(&cr,WIN_STYLE,FALSE);

	int windowWidth = cr.right-cr.left;
	int windowHeight = cr.bottom-cr.top;

	RECT dr;

	GetWindowRect(GetDesktopWindow(),&dr);

	int dw = dr.right-dr.left;
	int dh = dr.bottom-dr.top;

	int windowX = windowWidth>dw ? 0:(dw-windowWidth)/2;
	int windowY = windowHeight>dh ? 0:(dh-windowHeight)/2;


	// ウィンドウを作成
	hWnd = CreateWindowEx(0,						//拡張ウィンドウスタイル
	CLASS_NAME,										//ウィンドウクラスの名前
	WINDOW_NAME,									//ウィンドウの名前
	WIN_STYLE,										//ウィンドウスタイル
	windowX,										//ウィンドウの左上X座標
	windowY,										//ウィンドウの左上Y座標
	windowWidth,									//ウィンドウの幅
	windowHeight,									//ウィンドウの高さ
	NULL,											//親ウィンドウのハンドル
	NULL,											//メニューハンドルまたは子ウィンドウID
	hInstance,										//インスタンスハンドル
	NULL);											//ウィンドウ作成データ


	// ウィンドウの表示

	// ウィンドウハンドル
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	// 分解能の設定
	timeBeginPeriod( 1 );

	// 各カウンターの初期化
	DWORD dwFrameCount = 0;
	DWORD dwCurrentTime = 0;
	DWORD dwExecLastTime = timeGetTime();			//現在の時刻をミリ秒単位で取得
	DWORD dwFPSLastTime = dwExecLastTime;			//時間を同期

	// 関数初期化
	Init(hInstance,hWnd,true);

	// メッセ―ジループ
	for(;;){
	
		if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)!=0){

			// Windowsの処理
			if(msg.message==WM_QUIT){
				break;
			}
			else{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else{

			dwCurrentTime = timeGetTime();		//現在の時刻をミリ秒単位で取得
			
			if( (dwCurrentTime-dwFPSLastTime) >= 500 ){

				g_nCountFPS=(dwFrameCount*1000)/(dwCurrentTime-dwFPSLastTime);
				dwFPSLastTime = dwCurrentTime;
				dwFrameCount=0;
			
			}
			if( (dwCurrentTime - dwExecLastTime) >= ( TIMER_INTERVAL ) ){

				// DirectXの処理
				dwExecLastTime=dwCurrentTime;	//処理した時間を保存
				Update();						//更新
				Draw();							//描画
				dwFrameCount++;

			}
		}
	}

	// 終了処理
	UnInit();

	// 以前にセットされた最小タイマ分解能をクリア
	timeEndPeriod( 1 );

	return (int)msg.wParam;						//関数に0を返す

}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{

	// Window Message
	switch( uMsg ){

		case WM_CLOSE:
			
			// Window終了処理
			if( MessageBox(NULL,"終了しますか？",WINDOW_NAME, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) == IDNO ){
				uMsg = NULL;
			}
			break;

		case WM_DESTROY:

			PostQuitMessage(0);
			break;

		case WM_KEYDOWN:

			// ESC終了処理
			if( wParam == VK_ESCAPE ) {					
				if( MessageBox(NULL,"終了しますか？",WINDOW_NAME, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) == IDYES ){
					DestroyWindow(hWnd);
				}
			}
			
			/*
			// 非常時用のデバイス再設定
			if( wParam == VK_CONTROL && VK_SHIFT )	{
				MessageBox(NULL," デバイスの再設定をします。 ", " DEVICE_RESET !! " , MB_OK | MB_ICONWARNING );
				SetInputDevice( DEVICE_STANDARD );
			}
			break;
			*/

		default:
			break;
	}

	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

// DirectXの初期化関数
HRESULT Init(HINSTANCE hInstance,HWND hWnd,BOOL bWindow)
{

	/* DirectX9の設定(初期化)					*/
	/* IDirect3DDevice9インターフェースの取得	*/


	D3DPRESENT_PARAMETERS d3dpp={0};			//デバイスの動作を設定する構造体
	D3DDISPLAYMODE d3ddm;						//ディスプレイの設定

	g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);	//D3D9インターフェースの取得
	if( g_pD3D==NULL ){
		return E_FAIL;
	}

	// 現在のデバイスモードの取得
	if(FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddm))){
		return E_FAIL;
	}

	// デバイスのプレゼンテーションパラメーターの設定
	ZeroMemory(&d3dpp,sizeof(d3dpp));

	d3dpp.BackBufferWidth=SCREEN_WIDTH;			//backbufferのサイズ(横)
	d3dpp.BackBufferHeight=SCREEN_HEIGHT;		//backbufferのサイズ(縦)
	d3dpp.BackBufferFormat=d3ddm.Format;		//FULLSCREENのときはこれの限りではない、WINDOWMODEは別
	d3dpp.BackBufferCount=1;					//backBufferの数
	d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;		//doubleBufferの切り替え
	d3dpp.EnableAutoDepthStencil=TRUE;			//※重要 ;DepthStencilの使用
	d3dpp.AutoDepthStencilFormat=D3DFMT_D16;	//D3Dフォーマットの設定
	d3dpp.Windowed=bWindow;						//WindowMode trueでウィンドウ falseでFullScreen(※WindoWIN_STYLEをpopup)
	d3dpp.FullScreen_RefreshRateInHz
	=D3DPRESENT_RATE_DEFAULT;					//書き換えスピード(fullscreenのときのみ設定可能)
	d3dpp.PresentationInterval
	=D3DPRESENT_INTERVAL_DEFAULT;				//fullscreenの時のみ 書き換えを見せるかどうか


	// Direct3Dデバイスの設定
	if(FAILED( g_pD3D->CreateDevice(
	D3DADAPTER_DEFAULT,							//デフォルトのアダプターを取得
	D3DDEVTYPE_HAL,								//描画処理typeの指定(ハードでやるかソフトでやるか)HAL=ハード
	hWnd,										//どのウィンドウの上に作成するか(フォーカスウィンドウ)
	D3DCREATE_HARDWARE_VERTEXPROCESSING,		//デバイス作成オプションフラグ(今回はハードウェアで頂点処理をする)
	&d3dpp,										//その他設定
	&g_pD3DDevice)))							//デバイスのポインタのポインタ
	//失敗したらNULLが返る
	{

		MessageBox(hWnd,"処理速度を下げます","注意",MB_OK);
		if(FAILED( g_pD3D->CreateDevice(
		D3DADAPTER_DEFAULT,							//デフォルトのアダプターを取得
		D3DDEVTYPE_HAL,								//描画処理typeの指定(ハードでやるかソフトでやるか)HAL=ハード
		hWnd,										//どのウィンドウの上に作成するか(フォーカスウィンドウ)
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,		//デバイス作成オプションフラグ(今回はソフトウェアで頂点処理をする)
		&d3dpp,										//その他設定
		&g_pD3DDevice)))							//デバイスのポインタのポインタ
		{	

			if(FAILED( g_pD3D->CreateDevice(
			D3DADAPTER_DEFAULT,							//デフォルトのアダプターを取得
			D3DDEVTYPE_REF,								//描画処理typeの指定(ハードでやるかソフトでやるか)
			hWnd,										//どのウィンドウの上に作成するか(フォーカスウィンドウ)
			D3DCREATE_SOFTWARE_VERTEXPROCESSING,		//デバイス作成オプションフラグ(今回はソフトウェアで頂点処理をする)
			&d3dpp,										//その他設定
			&g_pD3DDevice)))							//デバイスのポインタのポインタ
			{
				return E_FAIL;
			}
		}
	}

	// レンダ―ステートの設定
	//g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);				//時計回りを裏面とする
	g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);				//裏時計回りを裏面とする
	//g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);			//両面を描画する

	#ifndef _MODE_WIREFRAME_
	g_pD3DDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);				//ポリゴンを実線で描く
	#else
	g_pD3DDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);			//ポリゴンをワイヤーフレームで描く
	#endif //_MODE_WIREFRAME_

	// 現在画面に描かれている状態にして今から描画する内容
	g_pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );			//半透明処理
	g_pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );		//元のα
	g_pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );	//先のα(INVは逆の意味,ここではα値の逆)

	// サンプラーステートの設定(stagesteat texturestate)
	g_pD3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	g_pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

	g_pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
	g_pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
	//g_pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
	//g_pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
	
	// テクスチャステートの設定( α透過処理設定 )
	g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
	g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );

	g_pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
	g_pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
	g_pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
	

	// バックバッファサーフェスポインタの取得
	SetBackbuf( );

	// 入力デバイスの初期化
	InitInputDevice( hWnd, hInstance );

	// サウンドの設定
	InitWaveSound(hWnd);

	// 各関数の初期化
	InitFade( );
	pInitFunc[ sceneSelect ]( );

	#ifdef _DEBUG
	D3DXCreateFont( g_pD3DDevice,18,0,0,0,FALSE,
	SHIFTJIS_CHARSET,
	OUT_DEFAULT_PRECIS,
	DEFAULT_QUALITY,
	DEFAULT_PITCH,
	"terminal",&g_pFont );
	#endif	//_DEBUG

	//成功
	return S_OK;
	
}

// 終了処理
void UnInit(void)
{


	// 入力デバイスの解放処理
	UninitInputDevice( );
	
	// 終了解放処理
	UninitFade( );
	pUninitFunc[ sceneSelect ]( );

	// サウンドデバイス解放処理
	UninitSound();


	// バックバッファサーフェスポインタの解放
	ReleaseBackbuf( );

	// Direct3DDeviceとアドレスの解放
	SAFE_RELEASE(g_pD3DDevice)

	// Direect3Dインターフェースの解放
	SAFE_RELEASE(g_pD3D)

	// フォント表示用のインターフェースの開放
	#ifdef _DEBUG
	SAFE_RELEASE(g_pFont)
	#endif //_DEBUG

}

// 更新処理
void Update( void )
{
	
	static unsigned int frame;		//ゲームを数えるフレーム
	

	// 入力デバイスの更新処理
	UpdateInputDevice( );

	// モードの選択
	pUpdateFunc[ sceneSelect ]( );

	/*
	if( GetKeyboardTrigger( DIK_RETURN ) ){

		switch( sceneSelect )
		{
			case SCENE_TITLE:
				//ChangeMode( MODE_GAME );
				ChangeScene( SCENE_TUTORIAL );
				break;

			case SCENE_TUTORIAL:

				SetFade( FADE_OUT, SCENE_GAME );
				break;

			case SCENE_GAME:
				//ChangeMode( MODE_RESULT );
				SetFade( FADE_OUT, SCENE_RESULT );
				break;

			case SCENE_RESULT:
				//ChangeMode( MODE_TITLE );
				SetFade( FADE_OUT, SCENE_TITLE );
				break;

		}
	}*/

	UpdateFade( );

	frame++;

	// フレームのリセット
	FRAME_RESET(frame,60)	

}

// 描画処理
void Draw ( void )
{

	// バックバッファとZバッファのクリア
	g_pD3DDevice->Clear(0,NULL,
	D3DCLEAR_TARGET								//色
	| D3DCLEAR_ZBUFFER,							//深度情報
	D3DCOLOR_RGBA(0,0,0,0),						//クリア色
	1.0f,										//クリア深度値
	0);											//ステンシル値

	// 描画の開始
	if(SUCCEEDED( g_pD3DDevice->BeginScene() ))
	{

		// モードの選択
		pDrawFunc[ sceneSelect ]( );

		// フェード描画
		if( GetFade() ){
			DrawFade( );
		}

		#ifdef _DEBUG
		DrawFPS();
		#endif	//_DEBUG
		
		// 描画終了
		g_pD3DDevice->EndScene();

	}
	g_pD3DDevice->Present(NULL,NULL,NULL,NULL);

}

// FPSの表示
#ifdef _DEBUG
void DrawFPS( void )
{

	// FPSの描画前処理
	RECT rect={ 0,0,SCREEN_WIDTH,SCREEN_HEIGHT };

	char aStr[ 256 ];
	char str1[ 256 ];
	char str2[ 256 ];
	long x,y,z;

	sprintf(&aStr[0]," FPS : %d ",g_nCountFPS);

	// マウスの座標取得
	x = GetMouseX( );
	y = GetMouseY( );
	z = GetMouseZ( );


	//sprintf( &str1[0], "\n MouseX : %d \n MouseY : %d \n MouseZ : %d ", x, y, z );

	//sprintf( &str1[0], "\n MouseX : %d \n MouseY : %d \n MouseZ : %d ", point.x, point.y, z );
	sprintf( &str1[0], "\n MouseX : %d \n MouseY : %d \n MouseZ : %d ", GetMouseX(), GetMouseY(), z );


	// 敵の座標取得


	// FPSの描画処理
	g_pFont->DrawText(NULL,&aStr[0],-1,&rect,DT_LEFT,D3DCOLOR_RGBA(0,255,0,255));
	g_pFont->DrawText(NULL,&str1[0],-1,&rect,DT_LEFT,D3DCOLOR_RGBA(0,255,0,255));
	//g_pFont->DrawText(NULL,&str2[0],-1,&rect,DT_LEFT,D3DCOLOR_RGBA(0,255,0,255));


}
#endif	//_DEBUG

// デバイスの取得
LPDIRECT3DDEVICE9 GetDevice(void)
{

	return g_pD3DDevice;

}

// シーンの取得
void ChangeScene( SCENE scene )
{

	// 現在の関数を終了
	pUninitFunc[ sceneSelect ]( );

	// シーンチェンジ
	sceneSelect = scene;
	//SetFade( FADE_OUT, mode );
	

	// 起動するモードの初期化
	pInitFunc[ sceneSelect ]( );


}

// -> END