#include <windows.h>//put msimg32.lib in Project/Settings/Link for TransparentBlt
#include <math.h>//for modf
#define BACKGROUND 0xD0D0D0
#define BLACK 0
#define WHITE 0xFFFFFF
#define PADDLE 0x00FFFF
#define BALL_DIAMETER 35
#define DIRECTION 1
#define SPACE 1
#define d 0.70710678118655// square root of 0.5 (45 degree angle)

char Version[] = "Brokeout\nVersion 1.1\nNov 19, 2008\nDoug Cox\njdmcox.com/";
int Timers, cxScreen, cyScreen, PaddleLeft, PaddleTop, PaddleBottom, PaddleWidth, OldPaddle, NewPaddle;
int v, w, x, y, z, xBall, yBall, xBallCenter, yBallCenter, xDirection, yDirection, BlockWidth, BlockHeight, unit, brush;
double d2, d3;
struct
{
	int left;
	int right;
	int top;
	int bottom;
} Block[75];//5*15
BYTE red, green, blue;
BOOL notfirst = FALSE, first = TRUE, goingleft, goingright, inred = TRUE, ingreen = FALSE, inblue = FALSE;
HWND hwnd;
HPEN hPen, hBackgroundPen, hColor;
HBRUSH hPaddleBrush, hBackgroundBrush, hWhiteBrush, hBlockBrush[5];
RECT rect;
HBITMAP hBitmap, hBitmap2;
HDC hdc, hdcMem, hdcMem2;
PAINTSTRUCT ps;
SYSTEMTIME st;
FILETIME ft;
ULARGE_INTEGER ul;

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	case WM_CREATE:
		ShowCursor(FALSE);
		hBackgroundPen = CreatePen(PS_SOLID, 0, BACKGROUND);
		hPen = CreatePen(PS_SOLID, 0, BLACK);
		hPaddleBrush = CreateSolidBrush(PADDLE);
		hWhiteBrush = CreateSolidBrush(WHITE);
		hBackgroundBrush = CreateSolidBrush(BACKGROUND);
		hBlockBrush[0] = CreateSolidBrush(0x000F0);
		hBlockBrush[1] = CreateSolidBrush(0x00F0F0);
		hBlockBrush[2] = CreateSolidBrush(0x00F000);
		hBlockBrush[3] = CreateSolidBrush(0xF0F000);
		hBlockBrush[4] = CreateSolidBrush(0xF00000);
		return 0;

	case WM_SIZE:
		cxScreen = LOWORD(lParam);
		cyScreen = HIWORD(lParam);
		rect.left = rect.top = 0;
		rect.right = cxScreen;
		rect.bottom = cyScreen;
		return 0;
		
	case WM_TIMER:
		InvalidateRect(hwnd, &rect, FALSE);
		UpdateWindow(hwnd);
		return 0;

	case WM_KEYDOWN:
		if (wParam == VK_LEFT)
			goingleft = TRUE;

		else if (wParam == VK_RIGHT)
			goingright = TRUE;

		else if (wParam == VK_UP)
		{
			SetTimer(hwnd, Timers, 1, NULL);
			Timers++;
		}

		else if ((wParam == VK_DOWN) && (Timers > 0))
		{
			KillTimer(hwnd, Timers);
			Timers--;
		}

		else if (wParam == VK_ESCAPE)
		{
			for (x = 0; x < Timers; x++)
				KillTimer(hwnd, x);
			if (IDNO == MessageBox(hwnd, "", "QUIT?", MB_YESNO))
				for (x = 0; x < Timers; x++)
					SetTimer(hwnd, x, 1, NULL);//x is an ID and 1 means send a WM_TIMER message every millisecond
			else
				DestroyWindow(hwnd);
		}
		return 0;

	case WM_KEYUP:
		goingleft = goingright = FALSE;
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		if (notfirst)
		{
			if ((goingleft) && (OldPaddle > 1))
			{
				NewPaddle = OldPaddle-1;
				BitBlt(hdc, NewPaddle-1, PaddleTop, 122, 20, hdc, OldPaddle-1, PaddleTop, SRCCOPY);
				OldPaddle = NewPaddle;
			}
			else if ((goingright) && (OldPaddle < (cxScreen-121)))
			{
				NewPaddle = OldPaddle+1;
				BitBlt(hdc, NewPaddle-1, PaddleTop, 122, 20, hdc, OldPaddle-1, PaddleTop, SRCCOPY);
				OldPaddle = NewPaddle;
			}
			d2 += d;
			modf(d2, &d3);
			if (unit <= (int)d3) 
			{
				if (xBall+BALL_DIAMETER >= cxScreen)
				{//RIGHT SIDE
					MessageBeep(MB_OK);
					xDirection = -xDirection;
				}
				else if (xBall == 0)
				{//LEFT SIDE
					MessageBeep(MB_OK);
					xDirection = -xDirection;
				}
				if (yBall == 0)
				{//TOP
					MessageBeep(MB_OK);
					yDirection = -yDirection;
				}
				else if (yBall+BALL_DIAMETER >= PaddleBottom)
				{
					for (x = 0; x < Timers; x++)
						KillTimer(hwnd, x);
					if (IDYES == MessageBox(hwnd, "", "Play Again?", MB_YESNO))
						goto jump;
					else
					{
						DestroyWindow(hwnd);
						EndPaint(hwnd, &ps);
						return 0;
					}
				}
				else if ((yDirection == 1) && (yBall+BALL_DIAMETER >= PaddleTop) && (yBallCenter <= PaddleTop) && (xBall+BALL_DIAMETER >= NewPaddle) && (xBall <= NewPaddle+PaddleWidth))
				{//PADDLE
					MessageBeep(MB_OK);
					if ((xDirection == 1) && (xBallCenter <= NewPaddle+(PaddleWidth/2)))
						xDirection = -xDirection;
					else if ((xDirection == -1) && (xBallCenter > NewPaddle+(PaddleWidth/2)))
						xDirection = -xDirection;
					yDirection = -yDirection;
				}
				else for (z = 0; z < 75; z++)
				{//BLOCKS
					if (((xBall+BALL_DIAMETER == Block[z].left) || (xBall == Block[z].right))
					  && ((yBallCenter >= Block[z].top) && (yBallCenter <= Block[z].bottom)))
					{//LEFT/RIGHT SIDE
						xDirection = -xDirection;
						MessageBeep(MB_OK);
						BitBlt(hdc, Block[z].left, Block[z].top, BlockWidth, BlockHeight, hdc, 0, 0, SRCCOPY);
						Block[z].left = Block[z].top = Block[z].right = Block[z].bottom = -1;
					}
					else if ((yDirection == -1) && (xDirection == -1) && ((yBall < Block[z].bottom) && (yBall > Block[z].top) && (xBall > Block[z].left) && (xBall < Block[z].right))
					 || ((yDirection == -1) && (xDirection == 1)  && (yBall < Block[z].bottom) && (yBall > Block[z].top) && (xBall+BALL_DIAMETER > Block[z].left) && (xBall+BALL_DIAMETER < Block[z].right))
					 || ((yDirection == 1) && (xDirection == 1)  && (yBall+BALL_DIAMETER > Block[z].top) && (yBall+BALL_DIAMETER < Block[z].bottom) && (xBall+BALL_DIAMETER > Block[z].left) && (xBall+BALL_DIAMETER < Block[z].right))
					 || ((yDirection == 1) && (xDirection == -1)  && (yBall+BALL_DIAMETER > Block[z].top) && (yBall+BALL_DIAMETER < Block[z].bottom) && (xBall > Block[z].left) &&  (xBall < Block[z].right)))
					{//CORNER
						yDirection = -yDirection;
						xDirection = -xDirection;
						MessageBeep(MB_OK);
						BitBlt(hdc, Block[z].left, Block[z].top, BlockWidth, BlockHeight, hdc, 0, 0, SRCCOPY);
						Block[z].left = Block[z].top = Block[z].right = Block[z].bottom = -1;
					}
					else if (((yBall+BALL_DIAMETER == Block[z].top) || (yBall == Block[z].bottom))
						 && ((xBallCenter >= Block[z].left) && (xBallCenter <= Block[z].right)))
					{//TOP/BOTTOM
						yDirection = -yDirection;
						MessageBeep(MB_OK);
						BitBlt(hdc, Block[z].left, Block[z].top, BlockWidth, BlockHeight, hdc, 0, 0, SRCCOPY);
						Block[z].left = Block[z].top = Block[z].right = Block[z].bottom = -1;
					}
				}
				TransparentBlt(hdc, xBall, yBall, BALL_DIAMETER, BALL_DIAMETER, hdcMem2, 0, 0, BALL_DIAMETER, BALL_DIAMETER, WHITE);//erase
				TransparentBlt(hdc, xBall+xDirection, yBall+yDirection, BALL_DIAMETER, BALL_DIAMETER, hdcMem, 0, 0, BALL_DIAMETER, BALL_DIAMETER, WHITE);
				xBall += xDirection;
				yBall += yDirection;
				xBallCenter += xDirection;
				yBallCenter += yDirection;
				unit++;
			}
		}
		else//if (notfirst == FALSE)
		{
			notfirst = TRUE;
			if (first)
			{
				first = FALSE;
				hdcMem = CreateCompatibleDC(hdc);
				hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);
				SelectObject(hdcMem, hBitmap);
				hdcMem2 = CreateCompatibleDC(hdc);
				hBitmap2 = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);
				SelectObject(hdcMem2, hBitmap2);
				FillRect(hdcMem2, &rect, hWhiteBrush);
				SelectObject(hdcMem2, hBackgroundPen);
				SelectObject(hdcMem2, hBackgroundBrush);
				Ellipse(hdcMem2, 0, 0, BALL_DIAMETER, BALL_DIAMETER);//to erase
				FillRect(hdcMem, &rect, hWhiteBrush);
				SelectObject(hdcMem, hPen);
				SelectObject(hdcMem, hPaddleBrush);
				Ellipse(hdcMem, 0, 0, BALL_DIAMETER, BALL_DIAMETER);
			}
			GetSystemTime(&st);
			SystemTimeToFileTime(&st, &ft);
			ul.LowPart = ft.dwLowDateTime;
			ul.HighPart = ft.dwHighDateTime;
			ul.QuadPart /= 10000;//because low 4 digits are 0's
			srand(ul.LowPart);
			srand(rand());

jump:		BlockWidth = (cxScreen-(16*SPACE)) / 15;
			BlockHeight = 32;
			PaddleWidth = 120;
			NewPaddle = OldPaddle = (rand() % (cxScreen-(PaddleWidth+2))+1);
			PaddleTop = 700;//cyScreen-100;
			PaddleBottom = 720;//cyScreen-80;

			FillRect(hdc, &rect, hBackgroundBrush);
			SelectObject(hdc, hPen);
			SelectObject(hdc, hPaddleBrush);
			RoundRect(hdc, OldPaddle, PaddleTop, OldPaddle+PaddleWidth, PaddleBottom, 10, 10);
			MoveToEx(hdc, OldPaddle+(PaddleWidth/2), PaddleTop, NULL);
			LineTo(hdc, OldPaddle+(PaddleWidth/2), PaddleTop+10);

			Timers = 5;
			goingleft = goingright = FALSE;
			d2 = 0.0;
			z = brush = unit = 0;
			for (v = 0, y = 120; v < 5; y += BlockHeight+SPACE, v++)
			{
				switch (v)
				{
					case 0:
						red = 0xFF;
						green = 0;
						blue = 0;
						break;
					case 1:
						red = 0xFF;
						green = 0xFF;
						blue = 0;
						break;
					case 2:
						red = 0;
						green = 0xFF;
						blue = 0;
						break;
					case 3:
						red = 0;
						green = 0xFF;
						blue = 0xFF;
						break;
					case 4:
						red = 0;
						green = 0;
						blue = 0xFF;
						break;								
				}
				SelectObject(hdc, hBlockBrush[brush++]);
				for (x = SPACE; x < (15*(BlockWidth+SPACE)); x += (BlockWidth+SPACE))
				{
					Block[z].left = x;
					Block[z].top = y;
					Block[z].right = x+BlockWidth;
					Block[z].bottom = y+BlockHeight;
					for (w = 0; w < BlockHeight; w++)
					{
						hColor = CreatePen(PS_SOLID, 0, RGB(red, green, blue));
						SelectObject(hdc, hColor);
						MoveToEx(hdc, x, y+w, NULL);
						LineTo(hdc, x+BlockWidth, y+w);
						switch (v)
						{
							case 0:
								green += 8;
								break;
							case 1:
								red -= 8;
								break;
							case 2:
								blue += 8;
								break;
							case 3:
								green -= 8;
								break;
							case 4:
								red += 8;
								break;								
						}
					}
					z++;
				}
			}
			xBall = (OldPaddle+(PaddleWidth/2) - (BALL_DIAMETER/2));
			yBall = PaddleTop-50;
			xBallCenter = xBall+(BALL_DIAMETER/2);
			yBallCenter = yBall+(BALL_DIAMETER/2);
			if (xBall < (cxScreen/2))
				xDirection = DIRECTION;
			else
				xDirection = -DIRECTION;
			yDirection = -DIRECTION;
			for (x = 0; x < Timers; x++)
				SetTimer(hwnd, x, 1, NULL);//x is an ID and 1 means send a WM_TIMER message every millisecond
			SetBkMode(hdc, TRANSPARENT);
			TextOut(hdc, (cxScreen/2)-140, cyScreen-60, "Use Arrow Keys - Press ESC to Pause/Exit", 40);
			SetBkMode(hdc, OPAQUE);
		}
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		ReleaseDC(hwnd, hdcMem);
		ShowCursor(TRUE);
		for (x = 0; x < Timers; x++)
			KillTimer(hwnd, x);
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	char szAppName[] = "STATIC";
	MSG          msg;
	WNDCLASS     wndclass;

	wndclass.style         = CS_HREDRAW|CS_VREDRAW;
	wndclass.lpfnWndProc   = WndProc;
	wndclass.cbClsExtra    = 0;
	wndclass.cbWndExtra    = 0;
	wndclass.hInstance     = hInstance;
	wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)CreateSolidBrush(BACKGROUND);
	wndclass.lpszMenuName  = NULL;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
		return 0;

	hwnd = CreateWindow(szAppName, NULL,
		WS_POPUP,
		0, 0, 0, 0,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, SW_SHOWMAXIMIZED);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
