/////////////////////////////////////////////////////////////////////
// ex10.cpp : Defines the entry point for the application.
/////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
// Generated by the CDX Application Wizard
// By: Michael Rich [istan@alltel.net]
//
// With valuable help from:
//   Ioannis Karagiorgos, for the debugging help
//   Bil Simser, for the good ideas and constructive criticism
/////////////////////////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN
#define CDXINCLUDEALL

#include <windows.h>
#include <cdx.h>

#include "resource.h"

/////////////////////////////////////////////////////////////////////
// Global Variables
/////////////////////////////////////////////////////////////////////
char szAppName[] = "ex10";
char szClassName[] = "Ex10WndClass";

HINSTANCE g_hInst;			// instance handle
HWND g_hWnd;				// window handle

BOOL g_bFullScreen = TRUE;	// running fullscreen?

/////////////////////////////////////////////////////////////////////
// CDX Objects
/////////////////////////////////////////////////////////////////////
CDXScreen *Screen = 0;

/////////////////////////////////////////////////////////////////////
// Forward declarations
/////////////////////////////////////////////////////////////////////
void AdjustWinStyle();

POINT increment[4] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
POINT left[4] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
POINT right[4] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
POINT pos = {1, 3};
int direction = 1;
int visibility = 4;

#define WALL_COLOR	RGB(255, 255, 255)
#define TEXT_COLOR	RGB(255, 255, 0)

#define SCREEN_WIDTH	320
#define SCREEN_HEIGHT	200

#define MAZE_WIDTH	16
#define MAZE_HEIGHT	16

//
// NOTE: this could be in a CDXMap file and loaded at startup. The code
// to access it later is basically the same as using an array like this
//
char maze[MAZE_WIDTH][MAZE_HEIGHT] = {
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
	{1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1},
	{1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1},
	{1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1},
	{1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1},
	{1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1},
	{1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1},
	{1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1},
	{1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1},
	{1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
	{1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1},
	{1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1},
	{1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
	{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};

/////////////////////////////////////////////////////////////////////
// DrawText - draw some text to show where we are and which direction we're facing
/////////////////////////////////////////////////////////////////////
void DrawText()
{
	char sz[80];
	char *dirs[] = { "E", "N", "W", "S" };

	sprintf(sz, "X: %d, Y: %d, %s", pos.x, pos.y, dirs[direction]);
	
	Screen->GetBack()->GetDC();
	Screen->GetBack()->TextXY(10, 10, TEXT_COLOR, sz);
	Screen->GetBack()->ReleaseDC();
}

/////////////////////////////////////////////////////////////////////
// DrawMaze - draw the psudeo 3D maze
/////////////////////////////////////////////////////////////////////
void DrawMaze()
{
	POINT block, lblock, rblock;
	int dist;

	Screen->GetBack()->Lock();
	for(dist=0; dist<visibility; dist++)
	{
		// find the current square of the maze
		block.x = pos.x + dist * increment[direction].x;
		block.y = pos.y + dist * increment[direction].y;

		// find the square to the left
		lblock.x = block.x + left[direction].x;
		lblock.y = block.y + left[direction].y;

		// find the square to the right
		rblock.x = block.x + right[direction].x;
		rblock.y = block.y + right[direction].y;

		// draw image of squares according to distance
		switch(dist)
		{
		case 0:
			// current square
			// wall open to the left?
			if(maze[block.x][block.y])
			{
				Screen->GetBack()->Line(82, 19, 135, 44, WALL_COLOR);
				Screen->GetBack()->Line(135, 44, 135, 93, WALL_COLOR);
				Screen->GetBack()->Line(135, 93, 82, 118, WALL_COLOR);
			}
			else
			{
				// draw opening
				Screen->GetBack()->Line(82, 44, 135, 44, WALL_COLOR);
				Screen->GetBack()->Line(135, 44, 135, 93, WALL_COLOR);
				Screen->GetBack()->Line(135, 93, 82, 93, WALL_COLOR);
			}

			// wall open to right?
			if(maze[rblock.x][rblock.y])
			{
				Screen->GetBack()->Line(294, 19, 242, 44, WALL_COLOR);
				Screen->GetBack()->Line(242, 44, 242, 93, WALL_COLOR);
				Screen->GetBack()->Line(294, 118, 242, 93, WALL_COLOR);
			}
			else
			{
				// draw opening
				Screen->GetBack()->Line(294, 44, 242, 44, WALL_COLOR);
				Screen->GetBack()->Line(242, 44, 242, 93, WALL_COLOR);
				Screen->GetBack()->Line(242, 93, 294, 93, WALL_COLOR);
			}
			break;

		case 1:
			// can we see the next square?
			// if not, draw the wall
			if(maze[block.x][block.y])
			{
				Screen->GetBack()->Line(135, 44, 135, 93, WALL_COLOR);
				Screen->GetBack()->Line(242, 44, 242, 93, WALL_COLOR);
				Screen->GetBack()->Line(135, 44, 242, 44, WALL_COLOR);
				Screen->GetBack()->Line(135, 93, 242, 93, WALL_COLOR);
			}
			else
			{
				// draw sides of next square
				if(maze[lblock.x][lblock.y])
				{
					Screen->GetBack()->Line(135, 44, 162, 57, WALL_COLOR);
					Screen->GetBack()->Line(162, 57, 162, 80, WALL_COLOR);
					Screen->GetBack()->Line(162, 80, 135, 93, WALL_COLOR);
				}
				else
				{
					Screen->GetBack()->Line(135, 57, 162, 57, WALL_COLOR);
					Screen->GetBack()->Line(162, 57, 162, 80, WALL_COLOR);
					Screen->GetBack()->Line(162, 80, 135, 80, WALL_COLOR);
				}
				
				if(maze[rblock.x][rblock.y])
				{
					Screen->GetBack()->Line(242, 44, 215, 57, WALL_COLOR);
					Screen->GetBack()->Line(215, 57, 215, 80, WALL_COLOR);
					Screen->GetBack()->Line(215, 80, 242, 93, WALL_COLOR);
				}
				else
				{
					Screen->GetBack()->Line(242, 57, 215, 57, WALL_COLOR);
					Screen->GetBack()->Line(215, 57, 215, 80, WALL_COLOR);
					Screen->GetBack()->Line(215, 80, 242, 80, WALL_COLOR);
				}
			}
			break;

		case 2:
			if(maze[block.x][block.y])
			{
				Screen->GetBack()->Line(162, 57, 162, 80, WALL_COLOR);
				Screen->GetBack()->Line(215, 57, 215, 80, WALL_COLOR);
				Screen->GetBack()->Line(162, 57, 215, 57, WALL_COLOR);
				Screen->GetBack()->Line(162, 80, 215, 80, WALL_COLOR);
			}
			else
			{
				if(maze[lblock.x][lblock.y])
				{
					Screen->GetBack()->Line(162, 57, 175, 63, WALL_COLOR);
					Screen->GetBack()->Line(175, 63, 175, 74, WALL_COLOR);
					Screen->GetBack()->Line(175, 74, 162, 80, WALL_COLOR);
				}
				else
				{
					Screen->GetBack()->Line(162, 63, 175, 63, WALL_COLOR);
					Screen->GetBack()->Line(175, 63, 175, 74, WALL_COLOR);
					Screen->GetBack()->Line(175, 74, 162, 74, WALL_COLOR);
				}

				if(maze[rblock.x][rblock.y])
				{
					Screen->GetBack()->Line(215, 57, 202, 63, WALL_COLOR);
					Screen->GetBack()->Line(202, 63, 202, 74, WALL_COLOR);
					Screen->GetBack()->Line(202, 74, 215, 80, WALL_COLOR);
				}
				else
				{
					Screen->GetBack()->Line(215, 63, 202, 63, WALL_COLOR);
					Screen->GetBack()->Line(202, 63, 202, 74, WALL_COLOR);
					Screen->GetBack()->Line(202, 74, 215, 74, WALL_COLOR);
				}
			}
			break;

		case 3:
			if(maze[block.x][block.y])
			{
				Screen->GetBack()->Line(175, 63, 175, 74, WALL_COLOR);
				Screen->GetBack()->Line(202, 63, 202, 74, WALL_COLOR);
				Screen->GetBack()->Line(175, 63, 202, 63, WALL_COLOR);
				Screen->GetBack()->Line(175, 74, 202, 74, WALL_COLOR);
			}
			else
			{
				if(maze[lblock.x][lblock.y])
				{
					Screen->GetBack()->Line(175, 63, 182, 66, WALL_COLOR);
					Screen->GetBack()->Line(182, 66, 182, 70, WALL_COLOR);
					Screen->GetBack()->Line(182, 70, 175, 74, WALL_COLOR);
				}
				else
				{
					Screen->GetBack()->Line(175, 66, 182, 66, WALL_COLOR);
					Screen->GetBack()->Line(182, 66, 182, 70, WALL_COLOR);
					Screen->GetBack()->Line(182, 70, 175, 70, WALL_COLOR);
				}

				if(maze[rblock.x][rblock.y])
				{
					Screen->GetBack()->Line(202, 63, 195, 66, WALL_COLOR);
					Screen->GetBack()->Line(195, 66, 195, 70, WALL_COLOR);
					Screen->GetBack()->Line(195, 70, 202, 74, WALL_COLOR);
				}
				else
				{
					Screen->GetBack()->Line(202, 66, 195, 66, WALL_COLOR);
					Screen->GetBack()->Line(195, 66, 195, 70, WALL_COLOR);
					Screen->GetBack()->Line(195, 70, 202, 70, WALL_COLOR);
				}
			}
			break;
		}

		// if view is obscured by wall, stop drawing
		if(maze[block.x][block.y])
			break;
	}
	Screen->GetBack()->UnLock();
}

/////////////////////////////////////////////////////////////////////
// cdx_Init - handles initialization of the CDX objects
/////////////////////////////////////////////////////////////////////
BOOL cdx_Init()
{
	// Create the CDXSreen object
	Screen = new CDXScreen();
	if (Screen==NULL) return FALSE;

	// start app fullscreen
	if (FAILED(Screen->CreateFullScreen(g_hWnd, SCREEN_WIDTH, SCREEN_HEIGHT, 8))) return FALSE;

	// TODO: Initialize your own CDX objects here
	
	return TRUE;
}

/////////////////////////////////////////////////////////////////////
// cdx_DeInit - handles cleanup of CDX objects
/////////////////////////////////////////////////////////////////////
void cdx_DeInit()
{
	// TODO: Destroy your CDX objects here

	SAFEDELETE(Screen);
}

/////////////////////////////////////////////////////////////////////
// cdx_DoFrame - performs drawing of the current frame
/////////////////////////////////////////////////////////////////////
void cdx_DoFrame()
{

	// clear the current frame
	Screen->GetBack()->Fill(0);

	// TODO: Add code to draw your objects during each frame
	DrawMaze();
	DrawText();

	Screen->Flip();	 // Flip the back buffer to the front
}

/////////////////////////////////////////////////////////////////////
// AdjustWinStyle - adjusts the window style according to the mode
/////////////////////////////////////////////////////////////////////
static void AdjustWinStyle()
{
	if (g_bFullScreen)
	{
		DWORD       dwStyle;

		// Change window attributes

		dwStyle = WS_POPUP | WS_VISIBLE;

		SetWindowLong(g_hWnd, GWL_STYLE, dwStyle);

		SetWindowPos(g_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE |
			SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
	}
	else
	{
		RECT        rect = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
		DWORD       dwStyle;

		// Change window attributes

		dwStyle = GetWindowStyle(g_hWnd);
		dwStyle &= ~WS_POPUP;
		dwStyle |= WS_OVERLAPPED | WS_CAPTION;

		SetWindowLong(g_hWnd, GWL_STYLE, dwStyle);

		// Resize the window so that the client area is 640x480

		AdjustWindowRectEx(&rect, GetWindowStyle(g_hWnd), GetMenu(g_hWnd) != NULL,
			GetWindowExStyle(g_hWnd));

		// Just in case the window was moved off the visible area of the
		// screen.

		SetWindowPos(g_hWnd, NULL, 0, 0, rect.right-rect.left,
			rect.bottom-rect.top, SWP_NOMOVE | SWP_NOZORDER |
			SWP_NOACTIVATE);

		SetWindowPos(g_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
			SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
	}
}

/////////////////////////////////////////////////////////////////////
// WinProc - handles application messages
/////////////////////////////////////////////////////////////////////
static long PASCAL WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{

		case WM_KEYDOWN:
			switch(wParam)
			{
			case VK_UP:
				// move 1 square forward, beep if unavailable
				switch(direction)
				{
				case 0:
					pos.x++;
					if(pos.x > MAZE_WIDTH)
					{
						pos.x = MAZE_WIDTH;
						MessageBeep(MB_OK);
					}
					break;

				case 1:
					pos.y++;
					if(pos.y > MAZE_HEIGHT)
					{
						pos.y = MAZE_HEIGHT;
						MessageBeep(MB_OK);
					}
					break;

				case 2:
					pos.x--;
					if(pos.x < 0)
					{
						pos.x = 0;
						MessageBeep(MB_OK);
					}
					break;

				case 3:
					pos.y--;
					if(pos.y < 0)
					{
						pos.y = 0;
						MessageBeep(MB_OK);
					}
					break;
				}
				break;

			case VK_DOWN:
				// move 1 square backward, beep if unavailable
				switch(direction)
				{
				case 0:
					pos.x--;
					if(pos.x < 0)
					{
						pos.x = 0;
						MessageBeep(MB_OK);
					}
					break;

				case 1:
					pos.y--;
					if(pos.y < 0)
					{
						pos.y = 0;
						MessageBeep(MB_OK);
					}
					break;

				case 2:
					pos.x++;
					if(pos.x > MAZE_WIDTH)
					{
						pos.x = MAZE_WIDTH;
						MessageBeep(MB_OK);
					}
					break;

				case 3:
					pos.y++;
					if(pos.y > MAZE_HEIGHT)
					{
						pos.y = MAZE_HEIGHT;
						MessageBeep(MB_OK);
					}
					break;
				}
				break;

			case VK_LEFT:
				// turn to the left
				if(direction == 0)
					direction = 3;
				else
					direction--;
				break;

			case VK_RIGHT:
				// turn to the right
				if(direction == 3)
					direction = 0;
				else
					direction++;
				break;

			case VK_ESCAPE:
				// if Escape is pressed, close the application
				SendMessage(hWnd, WM_CLOSE, 0, 0);
				break;
			}
			return  0;
		case WM_SYSKEYUP:
			// Alt+Enter switches modes
			if (wParam == VK_RETURN)
			{
				g_bFullScreen = !g_bFullScreen;
				if(g_bFullScreen)
				{
					AdjustWinStyle();
					// we need to destroy and recreate the supporting CDX objects
					// that can't survive intact when we switch back to fullscreen mode
					{
						// TODO: add your own CDX objects that would have to be destroyed
						// on a mode switch (they will be recreated below)
					}
					Screen->CreateFullScreen(g_hWnd, SCREEN_WIDTH, SCREEN_HEIGHT, 8);
					{
						// TODO: recreate your CDX objects that were destroyed above
					}
				}
				else
				{
					Screen->GetDD()->RestoreDisplayMode();
					AdjustWinStyle();
					// we need to destroy and recreate the supporting CDX objects
					// that can't survive intact when we switch back to windowed mode
					{
						// TODO: add your own CDX objects that would have to be destroyed
						// on a mode switch (they will be recreated below)
					}
					Screen->CreateWindowed(g_hWnd, SCREEN_WIDTH, SCREEN_HEIGHT);
					{
						// TODO: recreate your CDX objects that were destroyed above
					}

				}
				return 0;
			}
			return 1;
		case WM_SETCURSOR:
			SetCursor(NULL);
        
			return 1;
		case WM_CLOSE:
			cdx_DeInit();
		case WM_DESTROY:
			PostQuitMessage(0);
		return 0;

		default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
}

/////////////////////////////////////////////////////////////////////
// ChangeToEXEDir - sets CWD to the DIR the EXE is in
/////////////////////////////////////////////////////////////////////
static void ChangeToEXEDir()
{
	char buf[MAX_PATH];
	char *cptr;

	//now change the directory
	//to make sure were accessing the proper file
	GetModuleFileName(NULL, buf, MAX_PATH);

	//move pointer to end of string
	cptr = buf + lstrlen(buf);

	//find the end of the path
	do
	{
		cptr--;
	} while (*cptr != '\\');
	cptr++;
	*cptr='\0';

	//change directory
	SetCurrentDirectory(buf);
}

/////////////////////////////////////////////////////////////////////
// InitApp - Create the window and the CDX objects
/////////////////////////////////////////////////////////////////////
static BOOL InitApp(int nCmdShow)
{
	WNDCLASS WndClass;

	WndClass.style = CS_HREDRAW | CS_VREDRAW;
	WndClass.lpfnWndProc = WinProc;
	WndClass.cbClsExtra = 0;
	WndClass.cbWndExtra = 0;
	WndClass.hInstance = g_hInst;
	WndClass.hIcon = LoadIcon(g_hInst, "APPICON");
	WndClass.hCursor = LoadCursor(0, IDC_ARROW);
	WndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	WndClass.lpszMenuName = 0;
	WndClass.lpszClassName = szClassName;
	RegisterClass(&WndClass);

	g_hWnd = CreateWindowEx(
		WS_EX_TOPMOST,
		szClassName,
		szAppName,
 		WS_POPUP,
		0,
		0,
		SCREEN_WIDTH,
		SCREEN_HEIGHT,
		NULL,
		NULL,
		g_hInst,
		NULL);

	if(!g_hWnd) return FALSE;


	ShowWindow(g_hWnd, nCmdShow);
	UpdateWindow(g_hWnd);

	ChangeToEXEDir();
	
	return TRUE;
}

/////////////////////////////////////////////////////////////////////
// WinMain - inital function called by windows
/////////////////////////////////////////////////////////////////////
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;

	// save the app instance
	g_hInst = hInstance;


	if(!InitApp(nCmdShow)) return FALSE;

	cdx_Init();
	while(1)
	{
		if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if(!GetMessage(&msg, NULL, 0, 0 )) return msg.wParam;
			TranslateMessage(&msg); 
			DispatchMessage(&msg);
		}
		else
		{
			cdx_DoFrame();
		}
	}
}
