#include "cGameApp.h"

const int TIMER_ID = 0;

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

}

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_OVERLAPPEDWINDOW | WS_VISIBLE, // 윈도우 스타일
		CW_USEDEFAULT, // x 좌표
		CW_USEDEFAULT, // y 좌표
		CW_USEDEFAULT, // 창의 폭
		CW_USEDEFAULT, // 창의 높이
		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){
	DDSURFACEDESC ddsd;

	// 여기서 다이렉트 드로우를 초기화한다.
	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 차 표면을 만든다.
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	if(pDD->CreateSurface(&ddsd, &pDDSFront, NULL) != DD_OK){
		MessageBox(NULL, "CreateSurface() failed.", "DirectX Error!", MB_OK);
		return false;
	}

	if(pDDSFront->GetSurfaceDesc(&ddsd) != DD_OK) 
		return false;
	
	// 2차 표면을 만든다.

	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;  
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; 
	if(pDD->CreateSurface(&ddsd, &pDDSBack, NULL) != DD_OK) 
		return false;

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

	xPos = 0;
	SetTimer(hWnd, TIMER_ID, 10, NULL);
	return true;
}

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

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

	PostQuitMessage(0) ;

}

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

	// 마치 화면을 그린것처럼 위장한다.
	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;
	pDDSBack->Blt(&rcClient, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx); 

	// 화면 DC를 얻어낸다.
	if(pDDSBack->GetDC(&hdc) != DD_OK)
		return;

	// 그림자 있는 문자열을 출력한다.
	SetBkMode(hdc, TRANSPARENT);
	for(i = 0; i < 16; i ++){
		SetTextColor(hdc, RGB(i * 16, i * 16, i * 16));
		TextOut(hdc, 
			rcClient.left + xPos - i,
			(rcClient.bottom + rcClient.top) / 2 - i ,
			"반갑습니다.", 11);
	}
	pDDSBack->ReleaseDC(hdc);

	// 배경 버퍼의 내용을 전경으로 복제한다.
	pDDSFront->Blt(NULL, pDDSBack, NULL, DDBLT_WAIT, NULL);

}

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

	xPos -= 4;
	GetClientRect(hWnd, &rcClient);
	if(xPos < 0) xPos = rcClient.right;
	InvalidateRect(hWnd, NULL, FALSE);
}
