/// @file GameBase.h
#ifndef GAMEBASE_H
#define GAMEBASE_H

#include "LayerInfo.h"

/**
	@brief DirectX 장치설정 및 생성/제거를 위한 클래스

	게임장치들(D3D/DInupt)을 설정하고 관리합니다.\n
	BaseClass(WindowBase, GameBase)들은 설정->생성->제거의 순서를 가집니다.
*/
class GameBase {
	public:
		GameBase(LayerInfo* _pLayerInfo){
			pLayerInfo_ = _pLayerInfo;
			pD3D_ = NULL;
			pD3DDevice_ = NULL;
		}
		~GameBase(){}

		/// @brief 게임장치(D3D/DInput) 생성
		 void newGameDevice(){
			 //construct D3D and set for D3DDevice
			if( NULL == ( pD3D_ = Direct3DCreate9( D3D_SDK_VERSION ) ) ){return;}
			D3DDISPLAYMODE   aD3Ddm;
			pD3D_->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &aD3Ddm );
			D3DPRESENT_PARAMETERS aD3Dpp;
			ZeroMemory( &aD3Dpp, sizeof(aD3Dpp) );

			aD3Dpp.Windowed = pLayerInfo_->bWindowed;
			//monitor ratio
			if (pLayerInfo_->bWindowed){pLayerInfo_->monitorRatio = 1.333333f;}
			else{pLayerInfo_->monitorRatio = (float)aD3Ddm.Width/(float)aD3Ddm.Height;}
			
			aD3Dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
			aD3Dpp.BackBufferFormat = aD3Ddm.Format;
			aD3Dpp.BackBufferWidth = pLayerInfo_->width;
			aD3Dpp.BackBufferHeight = pLayerInfo_->height;
			aD3Dpp.EnableAutoDepthStencil = TRUE;
			aD3Dpp.AutoDepthStencilFormat = D3DFMT_D16;
			aD3Dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

			//construct D3DDevice
			if( FAILED( pD3D_->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, pLayerInfo_->hWnd,
                                             D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                             &aD3Dpp, &pD3DDevice_ ) ) ){return;}
			pLayerInfo_->pD3DDevice = pD3DDevice_;

			//construct DInput
			if(FAILED(DirectInput8Create(GetModuleHandle(NULL),DIRECTINPUT_VERSION,
				                         IID_IDirectInput8,(void**)&pDInPut_,NULL))){return;}

			if(FAILED(pDInPut_->CreateDevice(GUID_SysKeyboard,&pDIDeviceKB_,NULL))){return;}
			if(FAILED(pDIDeviceKB_->SetDataFormat(&c_dfDIKeyboard))){return;}
			if(FAILED(pDIDeviceKB_->SetCooperativeLevel(pLayerInfo_->hWnd,DISCL_BACKGROUND | DISCL_NONEXCLUSIVE))){return;}
			if(FAILED(pDIDeviceKB_->Acquire())){return;}
			pLayerInfo_->pDIDeviceKB = pDIDeviceKB_;

			if(FAILED(pDInPut_->CreateDevice(GUID_SysMouse,&pDIDeviceMouse_,NULL))){return;}
			if(FAILED(pDIDeviceMouse_->SetDataFormat(&c_dfDIMouse))){return;}
			if(FAILED(pDIDeviceMouse_->SetCooperativeLevel(pLayerInfo_->hWnd,DISCL_BACKGROUND | DISCL_NONEXCLUSIVE))){return;}
			if(FAILED(pDIDeviceMouse_->Acquire())){return;}
			pLayerInfo_->pDIDeviceMouse = pDIDeviceMouse_;
		}

		/// @brief 게임장치를 제거합니다.
		void deleteGameDevice(){

			if(pD3DDevice_ != NULL){ pD3DDevice_->Release(); pD3DDevice_ = NULL;}
			if(pD3D_ != NULL){ pD3D_->Release(); pD3D_ = NULL;}

			if(pDIDeviceMouse_ != NULL){ pDIDeviceMouse_->Release(); pDIDeviceMouse_ = NULL;}
			if(pDIDeviceKB_ != NULL){ pDIDeviceKB_->Release(); pDIDeviceKB_ = NULL;}
			if(pDInPut_ != NULL){ pDInPut_->Release(); pDInPut_ = NULL;}
		}

		/// @brief 기본 Runner입니다. 다른 Runner로 대체될 수 있습니다.
		void run(){
			MSG msg;
			ZeroMemory( &msg, sizeof(msg) );
			while( msg.message != WM_QUIT ){
				if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) ){
					TranslateMessage( &msg );
					DispatchMessage( &msg );
				}else{
				}
			}
		}

	private:
		GameBase(){}

		LPDIRECT3D9           pD3D_;
		LPDIRECT3DDEVICE9     pD3DDevice_;

		LPDIRECTINPUT8        pDInPut_;
		LPDIRECTINPUTDEVICE8  pDIDeviceKB_;
		LPDIRECTINPUTDEVICE8  pDIDeviceMouse_;

		LayerInfo* pLayerInfo_;
};

#endif