/*
RM Color Clear
Developed by Robbie Meyer in May of 2003

Todo:
	Add support to save users settings
	Add highscore list (save/load/display)
	Create functions for future programs (new window, generic string char[] functions)
	Add speed game mode (user picks level and plays for x minutes or is timed to win x games)

*/

#include "main.h"

////////////////////////////////////////////////////////////
////////////				Main				////////////
////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow){
	srand( GetTickCount() ); // Seed the random number generator

    MSG msg;
    WNDCLASS wndclass = {0};
	char class_name[] = "Clear Color - By Robbie Meyer";
	HWND hwnd;
	
	if (!InitilizeStuff (msg, wndclass, hinstance, class_name, hwnd, ishow))
		return false;//very bad stuff has happened
	
	// Load Bitmaps from file
	LoadImageSet(0);

	if(mySong.Init(SONG_NAME)){
		mySong.PlaySong();
	}
	else{
		MessageBox(hwnd, "The song couldn't be loaded", "Error!", MB_OK);
		sound = false;
	}
	levelstr[0] = '1';
	rInputMatrix(levelstr[0] - '0', 12, 18);

	OutputBoardAndInfo ();
	while(1){
		// Get message(s) if there is one
		if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
			if(msg.message == WM_QUIT)
				break;
				
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	//Free up bitmaps
	UnloadBitmaps();

	// Free up global HDC
	ReleaseDC(hwnd,hdc);

	return msg.wParam;
}//end of win main


////////////////////////////////////////////////////////
////////////			Win Proc			////////////
////////////////////////////////////////////////////////
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam){
    	
	PAINTSTRUCT ps;

    switch(message){

		//handles all of the menu clicks
		case WM_COMMAND:
			if(LOWORD(wparam) == ID_SpeedGame){		
//Add Code here
			}
			else if (LOWORD(wparam) == ID_NormalGame){
				//set the score string to zero
				levelstr[0] = '1';
				score[0] = '0'; score[1] = '0'; score[2] = '0';
				score[3] = '0'; score[4] = '0'; score[5] = '0'; score[6] = '0';
				rInputMatrix (levelstr[0] - '0', 12, 18);	
				OutputBoardAndInfo ();
			}
			else if (LOWORD(wparam) == ID_Highscores){
//Add code here
			}
			else if (LOWORD(wparam) == ID_About){
//Add Code here
			}
			//turns music on
			else if (LOWORD(wparam) == ID_SoundOn){
				if (sound == false && mySong.Init(SONG_NAME)){
					mySong.PlaySong();
					sound = true;
				}
				else{
					MessageBox(hwnd, "The song couldn't be loaded", "Error!", MB_OK);
					sound = false;
				}
			}
			//turns music off
			else if (LOWORD(wparam) == ID_SoundOff && sound == true){
				mySong.FreeSoundSystem();
				sound = false;
			}
			//switch to standard graphics
			else if (LOWORD(wparam) == ID_StdGfx){
				UnloadBitmaps();
				LoadImageSet(0);
			}
			//switch to Apple logo graphics
			else if (LOWORD(wparam) == ID_AppleGfx){
				UnloadBitmaps();
				LoadImageSet(1);
			}
			else if (LOWORD(wparam) == ID_ClixGfx){
				UnloadBitmaps();
				LoadImageSet(2);
			}
			//exit program
			else if (LOWORD(wparam) == ID_Quit){
				PostQuitMessage(0);
				return 0;
			}
		break;

		//left mouse button down so make a corrisponding move
		case WM_LBUTTONDOWN:{ 
			int xPos = LOWORD(lparam); // This gives us the horizontal (the X position) of the cursor
			int yPos = HIWORD(lparam); // This gives us the vertical (the Y position) of the cursor
				
			//	board[0][0] coorisponds with x and y that are
			//	0 <= x < bmpSZ    and    11 * bmpSZ < y < 12 * bmpSZ
			int rowindex = 11 - ((yPos - BRD_VERTSHIFT) / bmpHEIGHT);
			int colindex = xPos / bmpWIDTH;
				
			if (rowindex > -1 && rowindex < 12 && colindex > -1 && colindex < 18){
				int numremoved = mover (rowindex, colindex);
				addtoscore(numremoved * numremoved);
			}
			else if (xPos > 400 && xPos < (400 + bmpWIDTH) && yPos > (bmpHEIGHT * 12 + 10) && yPos < (bmpHEIGHT * 12 + 10 + bmpHEIGHT)){
				if (sound == true){
					mySong.FreeSoundSystem();
					sound = false;
				}
				else{ //sound == false
					if (sound == false && mySong.Init(SONG_NAME)){
						mySong.PlaySong();
						sound = true;
					}
					else{
						MessageBox(hwnd, "The song couldn't be loaded", "Error!", MB_OK);
						sound = false;
					}
				}
			}
			//handles the new game button
			else if (xPos > 30 && xPos < 30 + buttonWIDTH && yPos > bmpHEIGHT * 12 + 10 && yPos < bmpHEIGHT * 12 + 10 + buttonHEIGHT){
				levelstr[0] = '1';
				score[0] = '0'; score[1] = '0'; score[2] = '0';
				score[3] = '0'; score[4] = '0'; score[5] = '0'; score[6] = '0';
				rInputMatrix (levelstr[0] - '0', 12, 18);	
				OutputBoardAndInfo ();
			}
			//handles the new speed game button
			else if (xPos > 120 && xPos < 120 + buttonWIDTH && yPos > bmpHEIGHT * 12 + 10 && yPos < bmpHEIGHT * 12 + 10 + buttonHEIGHT){
//Add Code Here
			}
							
			//check for level compleation
			if (nummoves() == 0 && board[0][0] == '0'){
				levelstr[0] = levelstr[0] + 1;
				addtoscore (5000);
				rInputMatrix(levelstr[0] - '0', 12, 18);
			}

			//redraw board
			OutputBoardAndInfo ();
			return 0;
		}

		case WM_PAINT:
            BeginPaint(hwnd,&ps);
			
			//redraw board
			OutputBoardAndInfo ();

			EndPaint(hwnd,&ps);
			return 0;

		break;

		case WM_SIZE:
			ResizeBuffers(&gBuffer);//resize buffer
		break;

		case WM_DESTROY:
		case WM_CLOSE:
            
			PostQuitMessage(0);
			return 0;
		break;

    } // end of switch(message)

    return DefWindowProc(hwnd, message, wparam, lparam);
}

////////////////////////////////////////////////////////////
////////////			Clix Functions			////////////
////////////////////////////////////////////////////////////
void addtoscore (int n){
//Post:  Adds n to the global char array called score[7] 
	int ones = n % 10;
	int tens = (n % 100 - ones) / 10;
	int hund = (n % 1000 - tens - ones) / 100;
	int thos = (n % 10000 - hund - tens - ones) / 1000;
	int tent = (n % 100000 - thos - hund - tens - ones) / 10000;
	int hunt = (n % 1000000 - tent - thos - hund - tens - ones) / 100000;
	int mill = (n % 10000000 - hunt - tent - thos - hund - tens - ones) / 1000000;

	score[0] += mill;
	score[1] += hunt;
	score[2] += tent;
	score[3] += thos;
	score[4] += hund;
	score[5] += tens;
	score[6] += ones;

	for (int i = 7; i > 0; i--){//basicly 9 + 1 = 10 (not 0[weird character])
		if (score[i] > '9'){
			score[i] = '0';
			score[i-1] += 1;
		}
	}
}
void rInputMatrix (int level, int numrow, int numcol){
//creates a random matrix of level + 2 types of pieces
	board.resize (numrow, numcol);
	for (int i = 0; i < numrow; i++)
		for (int k = 0; k < numcol; k++)
			board[i][k] = rand() % (level + 2) + 'a';
}

void fInputMatrix (const string &filename){
//loads a matrix from the given file
	ifstream ins (filename.c_str());
	if (!ins){
		cerr << "the file " << filename << " couldn't be opened for input";
		exit(1);
	}
	int rows, cols;
	ins >> rows >> cols;
	board.resize(rows,cols);
	for (int i = 0; i < rows; i++)
		for (int k = 0; k < cols; k++)
			ins >> board[i][k];
	
}

int nummoves (){
//Pre: none
//Post: returns number of moves
	int numrow = board.numrows();
	int numcol = board.numcols();
	int cnt = 0;
	for (int i = 0; i < numrow; i++){
		for (int j = 0; j < numcol; j++){
			if (isvalidmove(i, j))
				cnt++;
		}
	}
	return cnt;
}

int mover (int row, int col){
//Pre: none
//Post: modifies the global board so that move has been made with zeros removed
	int numremoved = 0;
	//check if a move is possible and if so then start the recursion
	if (isvalidmove(row, col)){
		apmatrix <char> newboard = board;
		
		recursivemove (newboard, row, col, board[row][col], ' ', numremoved);
		
		board = newboard;

		removezeros();
	}
	return numremoved;
}

void recursivemove (apmatrix <char> &newboard, const int &row, const int &col, const char &target, const char &dontmove, int &numremoved){
//Pre: Function must be called with dontmove = '0' for the first call and row col must be a valid move
//Post: will return the board by reference after execution (zeros not yet removed)
	
	int numrow = newboard.numrows();
	int numcol = newboard.numcols();
	
	newboard[row][col] = '0';
	numremoved++;
	
	if (dontmove != 'u' && row < numrow-1 && newboard[row+1][col] == target)//up	
		recursivemove (newboard, row+1, col, target, 'd', numremoved);
	
	if (dontmove != 'd' && row > 0        && newboard[row-1][col] == target)//down
		recursivemove (newboard, row-1, col, target, 'u', numremoved);
	
	if (dontmove != 'r' && col < numcol-1 && newboard[row][col+1] == target)//right
		recursivemove (newboard, row, col+1, target, 'l', numremoved);
	
	if (dontmove != 'l' && col > 0        && newboard[row][col-1] == target)//left
		recursivemove (newboard, row, col-1, target, 'r', numremoved);
	
}

bool isvalidmove (const int &row, const int &col){
	int numrow = board.numrows();
	int numcol = board.numcols();
	return (board[row][col] != '0' && ((row < numrow-1 && board[row][col] == board[row+1][col]) || 
									   (row > 0        && board[row][col] == board[row-1][col]) ||
									   (col < numcol-1 && board[row][col] == board[row][col+1]) || 
									   (col > 0        && board[row][col] == board[row][col-1])));
}

void removezeros (){
	
	int numrow = board.numrows();
	int numcol = board.numcols();
	
	//remove col search
	bool remove;
	for (int i = 0; i < numrow; i++){
		for (int col = 0; col < numcol; col++, remove = true){
		
			for (int row = 0; row < numrow; row++){
				if (board[row][col] != '0'){
					remove = false;
				}
			}
			if (remove)
				removecol(col);
		}
	}
	//downshift/fall search
	for (i = 0; i < numrow - 1; i++){
		for (int j = 0; j < numcol; j++){
		
			//it found a zero so it needs to make sure that there is something to drop
			bool remove = false;
			for (int k = i; k < numrow; k++)
				if (board[k][j] != '0') 
					remove = true;
			
			while (remove && board[i][j] == '0'){//repeat until object drops all the way
				//begin downshift function (board, i, j)
				for (int row = i; row < numrow - 1; row++)
					board[row][j] = board[row+1][j];
				board[numrow-1][j] = '0';

				//end of downshift function
			}
		}
	}

}

void removecol (int col){
//removes the col sent over to it
	int numcol = board.numcols();
	int numrow = board.numrows();

	//shift everything to the right of col left by one
	for (int currentcol = col; currentcol < numcol - 1; currentcol++){
		for (int currentrow = 0; currentrow < numrow; currentrow++){
			board[currentrow][currentcol] = board[currentrow][currentcol+1];
		}
	}
	//fill last collumn of the board with '0' characters
	for (int currentrow = 0; currentrow < numrow; currentrow++){
		board[currentrow][numcol-1] = '0';
	}
}

void sOutputMatrix (){

	int numrow = board.numrows();
	int numcol = board.numcols();
	//when row index is 0 the output y should be at the bottom
	//when col index is 0 the output x should be at 0
	for (int i = numrow - 1, y = BRD_VERTSHIFT; i > -1; i--, y += bmpHEIGHT){
		for (int j = 0, x = 0; j < numcol; j++, x += bmpWIDTH){
			
			switch (board[i][j]){
			
			case 'a':
				DisplayBitmap(&gBuffer, red_bmp, x, y);
				break;
			case 'b':
				DisplayBitmap(&gBuffer, blue_bmp, x, y);
				break;
			case 'c':
				DisplayBitmap(&gBuffer, green_bmp, x, y);
				break;
			case 'd':
				DisplayBitmap(&gBuffer, yellow_bmp, x, y);
				break;
			case 'e':
				DisplayBitmap(&gBuffer, silver_bmp, x, y);
				break;
			case 'f':
				DisplayBitmap(&gBuffer, purple_bmp, x, y);
				break;
			}
		}	
	}
	if (sound)
		DisplayBitmap(&gBuffer, soundon_bmp, 400, bmpHEIGHT * 12 + 10);
	else
		DisplayBitmap(&gBuffer, soundoff_bmp, 400, bmpHEIGHT * 12 + 10);

	DisplayBitmap(&gBuffer, newgameon_bmp, 30,bmpHEIGHT * 12 + 10);
	DisplayBitmap(&gBuffer, newspdgameon_bmp, 120,bmpHEIGHT * 12 + 10);

	SwapBackBuffer(&gBuffer, TRUE);//puts all this stuff in the screen
}

void OutputBoardAndInfo (){
//outputs board and text in one call
	sOutputMatrix();
	TextOut(hdc, 230, bmpHEIGHT * 12 + 10, "Level: ", strlen ("Level: "));
	TextOut(hdc, 280, bmpHEIGHT * 12 + 10, levelstr, strlen(levelstr));		
	TextOut(hdc, 230, bmpHEIGHT * 12 + 30, "Score: ", strlen ("Score: "));
	TextOut(hdc, 280, bmpHEIGHT * 12 + 30, score, strlen(score));
}
/////////////////////////////////////////////////////////////
////////////         Initialize Graphics         ////////////
/////////////////////////////////////////////////////////////
bool InitilizeStuff (MSG &msg, WNDCLASS &wndclass, HINSTANCE &hinstance, char class_name[], HWND &hwnd, int &ishow){
//Pre: give the stuff in the prototype
//Post: Sets up a window as defined here and loads some bitmaps
//		returns true if it works, false otherwise

	// Fill the fields we care about
    wndclass.style			= CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc	= WinProc;
    wndclass.hInstance		= hinstance;
    wndclass.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszClassName	= class_name;
	wndclass.hCursor		= LoadCursor (NULL, IDC_ARROW);
	wndclass.lpszMenuName	= "Menu";

    RegisterClass(&wndclass);	// Register the window

    hwnd = CreateWindow(class_name,
						"RM Color Clear",
						WS_SYSMENU, // Window can not be resized
						0,			// Window start at (0,0) of the screen
						0,			// CW_USERDEFAULT lets windows pick the pos
						WIN_WIDTH,
						WIN_HEIGHT,
						NULL,
						NULL,
						hinstance,
						NULL);

	// Error check
	if(!hwnd)
		return false; // Something really bad happened!

	hdc = GetDC(hwnd); // Get the window's device context
		
	//Error check
	if(!hdc)
		return false; // Couldn't get window's hdc

	// Sets Text Color
	SetTextColor(hdc, RGB(0, 0, 255));

	CreateDoubleBuffering(&gBuffer, hwnd);

    ShowWindow(hwnd, ishow);
    UpdateWindow(hwnd);

	return true;
}

void LoadImageSet(int imageset){
	
	switch (imageset){
		case 0://sphere gfx
			bmpWIDTH = 48;
			bmpHEIGHT = 48;
			InitBitmap (red_bmp,	"sphere-red.bmp");
			InitBitmap (yellow_bmp,	"sphere-yellow.bmp");
			InitBitmap (green_bmp,	"sphere-green.bmp");
			InitBitmap (silver_bmp,	"sphere-silver.bmp");
			InitBitmap (blue_bmp,	"sphere-blue.bmp");
			InitBitmap (purple_bmp, "sphere-purple.bmp");
		break;
		case 1://apple gfx
			bmpWIDTH = 48;
			bmpHEIGHT = 48;
			InitBitmap (red_bmp,	"apple-red.bmp");
			InitBitmap (yellow_bmp,	"apple-yellow.bmp");
			InitBitmap (green_bmp,	"apple-green.bmp");
			InitBitmap (silver_bmp,	"apple-grey.bmp");
			InitBitmap (blue_bmp,	"apple-blue.bmp");
			InitBitmap (purple_bmp,	"apple-pink.bmp");
		break;
		case 2://original clix gfx
			bmpWIDTH = 33;
			bmpHEIGHT = 33;
			InitBitmap (red_bmp,	"orig-red.bmp");
			InitBitmap (yellow_bmp,	"orig-yellow.bmp");
			InitBitmap (green_bmp,	"orig-green.bmp");
			InitBitmap (silver_bmp,	"orig-grey.bmp");
			InitBitmap (blue_bmp,	"orig-blue.bmp");
			InitBitmap (purple_bmp,	"orig-black.bmp");
		break;
	}
	InitBitmap (newgameon_bmp,		"newgame_buttonon.bmp");
	InitBitmap (newspdgameon_bmp,	"newspdgame_buttonon.bmp");
	InitBitmap (newgameoff_bmp,		"newgame_buttonoff.bmp");
	InitBitmap (newspdgameoff_bmp,	"newspdgame_buttonoff.bmp");
	InitBitmap (soundon_bmp,		"soundon.bmp");
	InitBitmap (soundoff_bmp,		"soundoff.bmp");
}


void UnloadBitmaps() {
	// Free the bitmaps
	DeleteObject(red_bmp);
	DeleteObject(yellow_bmp);
	DeleteObject(blue_bmp);
	DeleteObject(silver_bmp);
	DeleteObject(green_bmp);
	DeleteObject(purple_bmp);
	DeleteObject(soundon_bmp);
	DeleteObject(soundoff_bmp);
	DeleteObject(newgameon_bmp);
	DeleteObject(newspdgameon_bmp);
	DeleteObject(newgameoff_bmp);
	DeleteObject(newspdgameoff_bmp);
}

void InitBitmap (HBITMAP &temp, LPSTR filename){
	temp = NULL;//just incase its not already set to null
	temp = LoadABitmap(filename);
	if (!temp){
		MessageBox(NULL, "Required Graphics file(s) could not be found: The program will now exit", "Error", MB_OK);
		exit(1);
	}
}
