// starFall.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

// #include "starFall.h"
#include "resource.h"
#include "windows.h"
#include "windowsx.h"
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
//#include <tchar.h>
#include <time.h>
#include <stdio.h>
#define MAX_LOADSTRING 100
#define SCREEN_WIDTH 40
#define SCREEN_HEIGHT 25
#define ID_MYTIMER 1
#define GAME_RUNNING 0
#define GAME_OVER 1

// グローバル変数 :
HINSTANCE hInst;								// 現在のインターフェイス
TCHAR szTitle[MAX_LOADSTRING];					// タイトル バーのテキスト
TCHAR szWindowClass[MAX_LOADSTRING];			// メイン ウィンドウ クラス名
SIZE fontExtent;								// 固定長フォントサイズ
SIZE screenExtent = {SCREEN_WIDTH, SCREEN_HEIGHT};	// スクリーン論理サイズ
char vram[SCREEN_HEIGHT][SCREEN_WIDTH];			// キャラクタVRAM
int score = 0;									// 得点
POINTS userPosition = {SCREEN_WIDTH / 2, SCREEN_HEIGHT - 1};	// 自機座標
HDC hBackDC;
HBITMAP hBackBmp;
int status = GAME_RUNNING;					// 今の状態

// このコード モジュールに含まれる関数の宣言を転送します :
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
void handleCreate(HWND hWnd);
void handleTimer(HWND hWnd);
void handlePaint(HWND hWnd);
void handleMousemove(HWND hWnd, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
 	// TODO: ここにコードを挿入してください。
	MSG msg;
	HACCEL hAccelTable;

	// グローバル文字列を初期化しています。
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_STARFALL, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// アプリケーションの初期化を実行します。:
	if (!InitInstance (hInstance, nCmdShow)) 
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_STARFALL);

	// メイン メッセージ ループ :
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  関数 : MyRegisterClass()
//
//  目的 : ウィンドウ クラスを登録します。
//
//  コメント :
//
//    この関数および使い方は、'RegisterClassEx' 関数が追加された
//     Windows 95 より前の Win32 システムと互換させる場合にのみ必要です。
//    アプリケーションが、関連付けられた 
//    正しい形式の小さいアイコンを取得できるようにするには、
//    この関数を呼び出してください。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(NULL, (LPCTSTR)IDI_APPLICATION);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(GetStockObject(BLACK_BRUSH));
	wcex.lpszMenuName	= (LPCTSTR)IDC_STARFALL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(NULL, (LPCTSTR)IDI_APPLICATION);

	return RegisterClassEx(&wcex);
}

//
//   関数 : InitInstance(HANDLE, int)
//
//   目的 : インスタンス ハンドルを保存して、メイン ウィンドウを作成します。
//
//   コメント :
//
//        この関数で、グローバル変数でインスタンス ハンドルを保存し、
//        メイン プログラム ウィンドウを作成および表示します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // グローバル変数にインスタンス処理を格納します。

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  関数 : WndProc(HWND, unsigned, WORD, LONG)
//
//  目的 :  メイン ウィンドウのメッセージを処理します。
//
//  WM_COMMAND	- アプリケーション メニューの処理
//  WM_PAINT	- メイン ウィンドウの描画
//  WM_DESTROY	- 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	RECT rc = {0, 0, 100, 100};

	switch (message) 
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam); 
		wmEvent = HIWORD(wParam); 
		// 選択されたメニューの解析 :
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_CREATE:
		handleCreate(hWnd);
		break;
	case WM_TIMER:
		if (wParam != ID_MYTIMER)
			return (DefWindowProc(hWnd, message, wParam, lParam));
		handleTimer(hWnd);
		break;

	case WM_PAINT:
		handlePaint(hWnd);
		break;
	case WM_MOUSEMOVE:
		handleMousemove(hWnd, lParam);
		break;
	case WM_DESTROY:
		DeleteDC(hBackDC);
		DeleteObject(hBackBmp);
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

void handleCreate(HWND hWnd)
{
	HDC hdc;
	RECT rect = {0, 0, 0, 0};
	SIZE screenExtent;

	srand(1);
	SetTimer(hWnd, ID_MYTIMER, 20, NULL);
	memset(vram, ' ', SCREEN_WIDTH * SCREEN_HEIGHT);
	hdc = GetDC(hWnd);

	// 文字一つの大きさを求める。
	GetTextExtentPoint32(hdc, "*", 1, &fontExtent);

	rect.right = fontExtent.cx * SCREEN_WIDTH;
	rect.bottom = fontExtent.cy * SCREEN_HEIGHT;

	// 画面の大きさをぴったりあわせる。
	AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, TRUE);
	screenExtent.cx = rect.right - rect.left;
	screenExtent.cy = rect.bottom - rect.top;
	MoveWindow(hWnd, 0, 0, screenExtent.cx, screenExtent.cy, FALSE);

	// 描画面作成
	hBackDC = CreateCompatibleDC(hdc);
	hBackBmp = CreateCompatibleBitmap(hdc, screenExtent.cx, screenExtent.cy);
	SelectObject(hBackDC, hBackBmp);
  	ReleaseDC(hWnd, hdc);


}

bool speedFactor(int _score)
{
	int lowScore;
	int scoreFactor;
	lowScore = _score % 800;
	if      (lowScore < 150)
		scoreFactor = 5;
	else if (lowScore < 200)
		scoreFactor = 4;
	else if (lowScore < 350)
		scoreFactor = 3;
	else if (lowScore < 700)
		scoreFactor = 2;
	else
		scoreFactor = 1;
	return (_score % scoreFactor) == 0;
}
int objectFactor(int _score)
{
	if ((_score % 800) < 50)
		return 0;
	return _score / 800 + 1;
}

void scrollStar(void);

void handleTimer(HWND hWnd)
{
	char scoreString[SCREEN_WIDTH];
	int nScoreString = 0;
	switch (status)
	{
	case GAME_RUNNING:
		score++;

		// スコア表示
		nScoreString = sprintf(scoreString, "SCORE: %05d", score);
		memcpy(vram[1] - nScoreString, scoreString, nScoreString);

		if (speedFactor(score))
			scrollStar();

		if (vram[userPosition.y][userPosition.x] == '*') {
			status = GAME_OVER;
		{
			char overString[] = "[ GAME OVER ]";
			memcpy(vram[SCREEN_HEIGHT / 2] + ((SCREEN_WIDTH - sizeof(overString) - 1) / 2),
				overString,
				sizeof(overString) - 1);
		}
			break;
		}
		vram[userPosition.y][userPosition.x] = 'A';


		break;
	case GAME_OVER:
		return;
		break;
	default:
		return;
	}
	InvalidateRect(hWnd, NULL, TRUE);
}


void scrollStar(void)
{
	int objectSize;
	// スクロール
	memmove(vram[2], vram[1], SCREEN_WIDTH * (SCREEN_HEIGHT - 2));
	memset(vram[1], ' ', SCREEN_WIDTH);

	// 新しい星
	objectSize = objectFactor(score);
	for (int i = 0; i < objectSize; i++) {
		vram[1][rand() % SCREEN_WIDTH] = '*';
	}
}

void handlePaint(HWND hWnd)
{
	PAINTSTRUCT ps;
	HDC hdc;

	SetTextColor(hBackDC, RGB(0, 255, 0));
	SelectObject(hBackDC, GetStockObject(ANSI_FIXED_FONT));
	SetBkColor(hBackDC, RGB(0, 0, 0));

	for (int y = 0; y < screenExtent.cy; y++) {
		for (int x = 0; x < screenExtent.cx; x++) {
			TextOut(hBackDC, x * fontExtent.cx, y * fontExtent.cy, &vram[y][x], 1);
		}
	}

	hdc = BeginPaint(hWnd, &ps);
	BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, hBackDC, 0,0, SRCCOPY);
	EndPaint(hWnd, &ps);
}

void handleMousemove(HWND hWnd, LPARAM lParam)
{
	userPosition.x = GET_X_LPARAM(lParam) / fontExtent.cx; 
	userPosition.y = GET_Y_LPARAM(lParam) / fontExtent.cy; 
}

// バージョン情報ボックスのメッセージ ハンドラです。
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_INITDIALOG:
		return TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
		{
			EndDialog(hDlg, LOWORD(wParam));
			return TRUE;
		}
		break;
	}
	return FALSE;
}

