#include "cGameApp.h"

const int TIMER_ID = 0;

cGameApp::cGameApp(){
	hInstance = NULL;
	hWnd = NULL;

	pDD = NULL;
	pDDClipper = NULL;
	pPlayer = 0;

	AppState = AS_INIT;

}

cGameApp::~cGameApp(){

}

int cGameApp::Main(HINSTANCE _hInstance, LPSTR lpCmdLine, int nShowCmd){
	int rv;

	hInstance = _hInstance;

	// 애플리케이션의 초기화
	if(!OpenApp()) return -1;

	// 중심 메시지 루프
	rv = MessageLoop();

	// 애플리케이션 닫기
	CloseApp();

	return rv;

}

bool cGameApp::OpenApp(){
	WNDCLASSEX  wndclass ;

	wndclass.cbSize        = sizeof (wndclass) ;  //WNDCLASSEX의 크기 지정  
	wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
	wndclass.lpfnWndProc   = StaticWndProc ;   //윈도의 프로시저 
	wndclass.cbClsExtra    = 0 ;
	wndclass.cbWndExtra    = 0 ;
	wndclass.hInstance     = hInstance ; //현재의 인스턴스 핸들
	wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION) ; //큰 아이콘
	wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW) ; //마우스 커서의 모양
	wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH) ; //배경 
	wndclass.lpszMenuName  = NULL ;  //메뉴 없음
	wndclass.lpszClassName = "GameApp" ;  //클래스 이름  
	wndclass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION) ; //작은 아이콘 

	 //윈도 클래스를 윈도우95에 등록한다
	RegisterClassEx (&wndclass) ;

	hWnd = CreateWindow ("GameApp", // 윈도우 클래스 이름
		"Game Programming", // 윈도우 캡션이름
		WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_VISIBLE, // 윈도우 스타일
		0, // x 좌표
		0, // y 좌표
		640, // 창의 폭
		480, // 창의 높이
		NULL, // 부모 윈도우
		NULL, // 메뉴
		hInstance, // 인스턴스 핸들
		this) ; // 추가 정보 없음
	if(!hWnd) return false;
	UpdateWindow (hWnd) ;  //윈도 프로시듀어에 WM_PAINT를 보낸다

	return true;
}

void cGameApp::CloseApp(){
	if(hWnd){
		DestroyWindow(hWnd);
		hWnd = NULL;
	}

}

int cGameApp::MessageLoop(){
	MSG msg;

	// 메시지루프를 만듦
	while(GetMessage(&msg, NULL, 0, 0)){
		TranslateMessage(&msg); //가상키 메시지를 문자메시지로 번역한다
		DispatchMessage (&msg); //메시지를 윈도 프로시듀어에 보낸다
	}

	return msg.wParam ;
}

//윈도 프로시듀어
//윈도우95에 의해 호출되고, 메시지 큐에서 메시지를 전달 받는다  
LRESULT CALLBACK cGameApp::StaticWndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam){
	cGameApp *pObject;

	if(iMsg == WM_CREATE){
		SetWindowLong(hWnd, GWL_USERDATA, LONG(((LPCREATESTRUCT)lParam)->lpCreateParams)); 
		((cGameApp *)((LPCREATESTRUCT)lParam)->lpCreateParams)->hWnd = hWnd;
	}
	pObject = (cGameApp *)GetWindowLong(hWnd, GWL_USERDATA);
	return pObject->WndProc(hWnd, iMsg, wParam, lParam);
}

LRESULT cGameApp::WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam){
	switch(iMsg){
	case WM_CREATE:
		if(OnCreate(wParam, lParam))
			return 0;
		return -1;

	case WM_DESTROY:
		OnDestroy(wParam, lParam);
		return 0;

	case WM_PAINT:
		OnPaint(wParam, lParam);
		return 0;

	case WM_TIMER:
		OnTimer(wParam, lParam);
		return 0;
	}
	//애플리케이션에서 처리되지 않은 윈도 메시지들에 대해
	//기본적인 처리를 제공하기 위하여 기본 윈도 프로시듀어를 호출한다    
	return DefWindowProc (hWnd, iMsg, wParam, lParam) ;
}

// 메시지 핸들러들
bool cGameApp::OnCreate(WPARAM wParam, LPARAM lParam){
	// 여기서 다이렉트 드로우를 초기화한다.
	if(DirectDrawCreate(NULL, &pDD, NULL) != DD_OK){
		MessageBox(NULL, "DirectDrawCreate() failed.", "DirectX Error!", MB_OK);
		return false;
	}
	if(pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL) != DD_OK){
		MessageBox(NULL, "SetCooperativeLevel() failed.", "DirectX Error!", MB_OK);
		return false;
	}
	
	// 1 차 표면을 만든다.
	if(!FrontPage.BindScreen(pDD))
		return false;

	// 2차 표면을 만든다.
	if(!BackPage.CreateClone(pDD, &FrontPage))
		return false;

	// 클립퍼를 붙인다.
	if(pDD->CreateClipper(0, &pDDClipper, NULL) != DD_OK)
		return false;
	if(pDDClipper->SetHWnd(0, hWnd) != DD_OK)
		return false;
	if(FrontPage.GetDDS()->SetClipper(pDDClipper) != DD_OK)
		return false;
	if(BackPage.GetDDS()->SetClipper(pDDClipper) != DD_OK)
		return false;

	if(!LoadSprites()) return false;

	CreateGame();
	SetTimer(hWnd, TIMER_ID, 1000 / 64, NULL);
	return true;
}

#define ReleaseCOM(p) if(p){ (p)->Release(); p = NULL; }

void cGameApp::OnDestroy(WPARAM wParam, LPARAM lParam){
	// 여기서 다이렉트 드로우를 소멸하고
	// 메시지루프를 중단시킨다.
	// 코드 추가 예정
	KillTimer(hWnd, TIMER_ID);
	DestroyGame();
	ReleaseSprites();
	ReleaseCOM(pDDClipper);
	BackPage.Release();
	FrontPage.Release();
	ReleaseCOM(pDD);

	PostQuitMessage(0) ;
}

void cGameApp::OnPaint(WPARAM wParam, LPARAM lParam){
	PAINTSTRUCT ps;
	HDC hdc;
	RECT rcClient;
	DDBLTFX ddbltfx;

	// 마치 화면을 그린것처럼 위장한다.
	hdc = BeginPaint(hWnd, &ps);
	EndPaint(hWnd, &ps);

	// 다이렉트 드로우 서피스에 그리기
	GetClientRect(hWnd, &rcClient);
	ClientToScreen(hWnd, (LPPOINT)&rcClient);
	ClientToScreen(hWnd, ((LPPOINT)&rcClient) + 1);

	// 검정색으로 화면을 지운다.
	ddbltfx.dwSize = sizeof(ddbltfx);
	ddbltfx.dwFillColor = 0;
	BackPage.GetDDS()->Blt(&rcClient, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx); 

	// 배경을 그린다.
	BackPage.BltAdjust(rcClient.left, rcClient.top, &BackSprites[0], DDBLT_WAIT);
	// 스프라이트를 배경 표면에 그린다.
	// 투명색은 사용하지 않았다.

	BackPage.BltAdjust(
		pPlayer->GetX() + rcClient.left,
		pPlayer->GetY() + rcClient.top,
		&pSprites[pPlayer->GetSpriteSet()][pPlayer->GetSpriteNo()], 
		DDBLT_WAIT | DDBLT_KEYSRC);

	// 배경 버퍼의 내용을 전경으로 복제한다.
	FrontPage.BltAdjust(0, 0,  &BackPage,DDBLT_WAIT);

}

void cGameApp::OnTimer(WPARAM wParam, LPARAM lParam){

	pPlayer->Act();

	InvalidateRect(hWnd, NULL, FALSE);
}

bool cGameApp::LoadSprites(){
	DDCOLORKEY ddck;

	// 컬러키를 지정한다.
	ddck.dwColorSpaceHighValue = 0;
	ddck.dwColorSpaceLowValue = 0;

	BackSprites[0].LoadBitmap(pDD, "back.bmp");
	BackSprites[0].GetDDS()->SetColorKey(DDCKEY_SRCBLT, &ddck);
	BackSprites[0].SetAdjust(0, 0);

	PlayerSprites[0].LoadBitmap(pDD, "player0.bmp");
	PlayerSprites[0].GetDDS()->SetColorKey(DDCKEY_SRCBLT, &ddck);
	PlayerSprites[0].SetAdjust(0, -174);

	PlayerSprites[1].LoadBitmap(pDD, "player1.bmp");
	PlayerSprites[1].GetDDS()->SetColorKey(DDCKEY_SRCBLT, &ddck);
	PlayerSprites[1].SetAdjust(-12, -167);

	pSprites[0] = BackSprites;
	pSprites[1] = PlayerSprites;


	return true;
}

void cGameApp::ReleaseSprites(){
	BackSprites[0].Release();
	PlayerSprites[0].Release();
	PlayerSprites[1].Release();
}


bool cGameApp::CreateGame()
{
	pPlayer = new cPlayer;
	pPlayer->Setxy(50, 400);
	pPlayer->SetSpriteSet(1);
	AppState = AS_BODY;
	return true;
}

void cGameApp::DestroyGame()
{
	AppState = AS_EXIT;
	if(pPlayer){
		delete pPlayer;
		pPlayer = 0;
	}

}
