/*
 ╔══════════════════════════════════════╗
 ║   ///////         Θ             Γ    ╟───────────────┐
 ║       //     Γ              Ω        ║ Thomas Powers │▌
 ║  Γ   //              Γ               ║ April 22 2010 │▌
 ║	   //  ZOMBIES & Landmines          ╟───────────────┘▌
 ║    //                        Θ       ║▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
 ║   //       Γ               Γ     ▒   ║▌
 ║Γ /////////////////////               ║▌
 ╚══════════════════════════════════════╝▌
  ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
*/

#include <iostream>
#include <iomanip>
#include <conio.h>
#include <windows.h>
#include <time.h>
#include <string>
#include <cmath>
#include <assert.h>
using namespace std;

#include "common.h"
#include "weapon.h"
#include "zombie.h"
#include "player.h"

// function prototypes
void drawGrid();
void clearGrid();
void drawGrid();
void playGame(bool realTime);

square grid[GRID_ROWS][GRID_COLS];

int main()
{
	srand( static_cast<unsigned int>(time(NULL)) );

	char choice=NULL;
	do {
		system("cls");
		bool realTime=false;
		cout << "Select game mode: [T]urn based or [R]eal time";
		choice=toupper(_getch());
		if ( choice == 'R' ) realTime=true;

		playGame(realTime);
		cout << "Play again?: ";
		do {
			choice=_getch();
			choice=toupper(choice);
		} while ( !(choice=='N' || choice=='Y') );
	} while ( choice!='N' );

	return 0;
}

void updateGridSquare(position pos)
{
	setScreenPos(pos,false);

	setColor(grid[pos.row][pos.col].FGcolor,grid[pos.row][pos.col].BGcolor);
	cout << grid[pos.row][pos.col].sqIcon;
}

void setScreenPos(position pos, bool absolute)
{
	COORD coord;
	coord.X=short(pos.col);
	coord.Y=short(pos.row);
	if ( !absolute ) 
	{
		// compensate for the border occupying row 0 and column 0
		coord.X++;
		coord.Y++;
	}

	SetConsoleCursorPosition ( GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

square* gridSquare(position pos)
{
	assert ( pos.row >=0 && pos.row < GRID_ROWS && pos.col >=0 && pos.col < GRID_COLS );

	square *sq;
	sq=&grid[pos.row][pos.col];
	return sq;
}

void playGame(bool realTime)
{
	int level=1;	// starting level

	player *thePlayer = new player();
	bool gameOn=true;

	// the outer loop repeats while there are more lives to play (it's the whole game)
	do {
		clearGrid();
		thePlayer->setIcon();

		position exitPosition;
		position playerPos;
		playerPos = thePlayer->getPosition();

		// decide where to stick the exit
		do {
			exitPosition.row=rand() % GRID_ROWS; exitPosition.col=rand() % GRID_COLS;
		} while ( !(grid[exitPosition.row][exitPosition.col].sqIcon == EMPTY_ICON ) );
		grid[exitPosition.row][exitPosition.col].sqIcon=EXIT_ICON;
		grid[exitPosition.row][exitPosition.col].FGcolor=BRyellow;
		grid[exitPosition.row][exitPosition.col].BGcolor=BKG_COLOR;

		// make some zombies
		int numZombies=5+level;
		zombie *theZombies = new zombie[numZombies];
		for ( int z=0; z < numZombies; z++ )
			theZombies[z].init(playerPos);

		// the inner loop runs while this level (or life) is in play
		drawGrid();
		bool zombiesActive=false;	// true after player has made the initial step

		while ( thePlayer->isAlive() && !(playerPos.row == exitPosition.row && playerPos.col == exitPosition.col)  ) 
		{

			// zombies move slower than players by *not* for (delay) moves
			int delay;
			if ( realTime )
			{
				delay=1000-(50*level);
				if ( delay < MAX_ZOMBIE_SPEED ) delay=MAX_ZOMBIE_SPEED;			
			} else {
				if ( level < 5 )
					delay=4;
				else if ( level < 10 )
					delay=3;
				else
					delay=2;
			}

			setColor(black,white);
			position statsPos;
			statsPos.col=7;	statsPos.row=GRID_ROWS+2;
			setScreenPos(statsPos);
			cout << left << setw(3) << thePlayer->getNumLives();
			statsPos.col=30;	setScreenPos(statsPos);
			thePlayer->displayWeapon();
			statsPos.col=63;	setScreenPos(statsPos);
			cout << left << setw(3) << level;
			statsPos.row++;
			statsPos.col=8;	setScreenPos(statsPos);
			cout << left <<setw(5) << thePlayer->getMonies();

			// get input from the keyboard
			char key;
			if ( (realTime && _kbhit()) || (!realTime) )
			{
				key=_getch();
				if ( key != NULL ) zombiesActive=true;

				// if it's an arrow key....
				if ( key == -32 ) {
					key=_getch();
					// which arrow key?
					switch ( key )
					{
						case KEY_UP:
							thePlayer->move(DIR_UP);
							break;
						case KEY_DOWN:
							thePlayer->move(DIR_DOWN);
							break;
						case KEY_LEFT:
							thePlayer->move(DIR_LEFT);
							break;
						case KEY_RIGHT:
							thePlayer->move(DIR_RIGHT);
							break;
					}
				} 
				else if ( key == 9 )
					thePlayer->toggleWeapon();
				else if ( key == 'a')
					thePlayer->useWeapon(DIR_LEFT);
				else if ( key == 'd')
					thePlayer->useWeapon(DIR_RIGHT);
				else if ( key == 'w')
					thePlayer->useWeapon(DIR_UP);
				else if ( key == 's')
					thePlayer->useWeapon(DIR_DOWN);
				else if ( key == '7' && thePlayer->hasRunningShoes() ) {
					thePlayer->move(DIR_UP);
					thePlayer->move(DIR_LEFT);
				} else if ( key == '8' && thePlayer->hasRunningShoes() ) {
					thePlayer->move(DIR_UP);
					thePlayer->move(DIR_UP);
				} else if ( key == '9' && thePlayer->hasRunningShoes() ) {
					thePlayer->move(DIR_UP);
					thePlayer->move(DIR_RIGHT);
				} else if ( key == '4' && thePlayer->hasRunningShoes() ) {
					thePlayer->move(DIR_LEFT);
					thePlayer->move(DIR_LEFT);
				} else if ( key == '6' && thePlayer->hasRunningShoes() ) {
					thePlayer->move(DIR_RIGHT);
					thePlayer->move(DIR_RIGHT);
				} else if ( key == '1' && thePlayer->hasRunningShoes() ) {
					thePlayer->move(DIR_DOWN);
					thePlayer->move(DIR_LEFT);
				} else if ( key == '2' && thePlayer->hasRunningShoes() ) {
					thePlayer->move(DIR_DOWN);
					thePlayer->move(DIR_DOWN);
				} else if ( key == '3' && thePlayer->hasRunningShoes() ) {
					thePlayer->move(DIR_DOWN);
					thePlayer->move(DIR_RIGHT);
				}

				// the player may have moved, make sure I know what the new position is
				playerPos=thePlayer->getPosition();
			}

			// do stuff with zombies
			// tell each zombie to move toward the player
			if ( zombiesActive )
			{
				for ( int z=0; z < numZombies; z++ )
				{
					if ( theZombies[z].isAlive() ) theZombies[z].checkStatus();
					if ( theZombies[z].isAlive() ) {
						if ( theZombies[z].move(playerPos,delay,realTime) )
							thePlayer->die();
					}
				}
			}

			// did the player get killed by a blast?
			thePlayer->checkStatus();

		} // end inner loop
		
		// this level is complete, either through success or fail

		// apply bonus monies for killing all zombies
		int zombiesKilledThisLevel=0;
		for ( int z=0; z < numZombies; z++ )
			if ( !theZombies[z].isAlive() ) zombiesKilledThisLevel++;
		if ( zombiesKilledThisLevel == numZombies )
			thePlayer->addMonies(ALL_ZOMBIE_BONUS);

		// i'm not into recycling zombies, kill these guys off and make more later.
		delete [] theZombies;

		// was this level a success or fail?
		gameOn=false;
		if ( thePlayer->isAlive() ) 
		{
			// player passed the level
			thePlayer->addMonies(MONIES_PER_LEVEL*level);
			thePlayer->shop(level);
			// next level
			level++;
			gameOn=true;
		} else if ( thePlayer->respawn() ) 	{ 
			// player failed level, but has more lives so respawn
			gameOn=true;
		}

	} while ( gameOn );

	setColor(black,white);
	position p;
	p.row=int(GRID_ROWS/2);
	p.col=5;
	setScreenPos(p);
	cout << "You have run out of lives!  ";
}

void drawGrid()
{
	setColor(black,black);
	system("cls");	// clear the screen

	// draw top border
	setColor(red,BRyellow);
	cout << char(201); // ╔
	for ( int i=0; i < GRID_COLS; i++ ) cout << char(205); // ═
	cout << char(187) << endl; // ╗

	// draw each item in the grid
	for ( int r=0; r < GRID_ROWS; r++ )
	{	
		setColor(red,BRyellow);
		cout << char(186); // ║

		for ( int c=0; c < GRID_COLS; c++ )
		{
			// draw this square's icon (at it's color)
			setColor(grid[r][c].FGcolor, grid[r][c].BGcolor);
			cout << grid[r][c].sqIcon;
		}
		setColor(red,BRyellow);
		cout << char(186) << endl; // ║
		setColor(black,black);
	}

	// draw bottom border
	setColor(red,BRyellow);
	cout << char(200); // ╚
	for ( int i=0; i < GRID_COLS; i++ ) cout << char(205); // ═
	cout << char(188) << endl; // ╝
	setColor(black,black);

	// draw text
	setColor(black,white);
	cout << "Lives: 000	[TAB] Weapon: Gun	Ammo: 0000	Level: 000\n"
		 << "Monies: 00000	[Arrow keys] Move	[A,D,W,S] Fire weapon";
}

void clearGrid()
{
	position p;
	for ( int r=0; r < GRID_ROWS; r++ )
	{
		for ( int c=0; c < GRID_COLS; c++ )
		{
			p.row=r;	p.col=c;
			grid[r][c].sqIcon=EMPTY_ICON;
			grid[r][c].BGcolor=black;
			grid[r][c].FGcolor=BKG_COLOR;
			updateGridSquare(p);
		}
	}
}

void setColor(int foreground, int background)
{
	int color=background;
	color+=(16*foreground);

	HANDLE hConsole;
	hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(hConsole,color);
}

void explosion(position pos)
{
	
	int left	=pos.col-(BLAST_RANGE/2);
	int right	=pos.col+(BLAST_RANGE/2);
	int top		=pos.row-(BLAST_RANGE/2);
	int bottom	=pos.row+(BLAST_RANGE/2);

	position p;
	for ( int row=top; row <= bottom; row++ )
	{
		for ( int col=left; col <= right; col++ )
		{
			p.row=row;	p.col=col;
			if ( (grid[row][col].sqIcon == ZOMBIE_ICON || grid[row][col].sqIcon == PLAYER_ICON) && calcDistance(p,pos) <= BLAST_RANGE )
			{
				grid[row][col].sqIcon=CORPSE_ICON;
				grid[row][col].BGcolor=CORPSE_COLOR;
				grid[row][col].FGcolor=BKG_COLOR;
				updateGridSquare(p);
			}
		}
	}

}

int calcDistance(position A, position B)
{
	double X=B.col-A.col;
	double Y=B.row-A.row;
	X=pow(X,2);
	Y=pow(Y,2);

	return int( sqrt(X+Y) );
}