#include <windows.h>
#include <vector>

using namespace std;
HANDLE wh;
HANDLE rh;
CHAR_INFO bb[4000];
COORD cBS={80,50};
COORD cPos = {0,0};
SMALL_RECT wA = {0,0,79,49};
bool running=true;
int fc = 0;

struct u
{
	int x;
	int y;
	int dx;
	int dy;
	int state;
};

vector<u> c;
vector<u> mush;
vector<u> bullets;
u s = {20, 49, 20, 49, 1};
int score,lives,i,j;

void SetChar(int x, int y, char c, int a)
{
	bb[(y*80)+x].Char.AsciiChar = c;
	bb[(y*80)+x].Attributes = a;
}

void ResetC()
{
	c.clear();
	u head={19,0,1,1,1};
	c.push_back(head);
	for(i=18;i>=0;--i)
	{
		u b={i,0,0,0,0};
		c.push_back(b);
	}
}

void SetPos(int x,int y)
{
	COORD c1={x,y};
	SetConsoleCursorPosition(wh,c1);
}

void MoveCP()
{
	if(fc%2)
	{
		for(i=19;i>-1;--i)
		{
			int headx, heady;
			if(c[i].state == 1)
			{
				int tx = c[i].x + c[i].dx;
				bool hm = false;
				for( j = 0; j< mush.size(); ++j)
				{
					if( c[i].x+c[i].dx == mush[j].x && c[i].y == mush[j].y && mush[j].state )
						hm=true;
				}

				if( tx > 40 || tx < 0 || hm)
				{
					c[i].dx = -c[i].dx;
					int ty = c[i].y+c[i].dy;
					if(ty > 49 || ty < 0 || (ty < 39 && c[i].dy == -1) )
					{
						c[i].dy = -c[i].dy;
					}
					c[i].y += c[i].dy;
				}
				else
				{
					c[i].x += c[i].dx;
				}
			}
			else if(c[i].state != -1)
			{
				c[i].x = c[i-1].x;
				c[i].y = c[i-1].y;
			}
		}
	}
}


void MoveS()
{
	bool ok = true;
	for(i = 0; i < mush.size(); ++i)
	{
		if(s.dx == mush[i].x && s.dy == mush[i].y)
			ok=false;
	}
	
	if(ok)
	{
		s.x=s.dx;s.y = s.dy;
	}
}

void MoveB()
{
	for(j = 0; j < 2; ++j)
	{
		for(i = 0;i < 20; ++i)
		{

			//  Hit the centipede.
			if(bullets[j].x == c[i].x && bullets[j].y == c[i].y && bullets[j].state != 0 && c[i].state != -1)
			{

				if(c[i].state == 0)
					score+=10;
				else
					score+=50;
				u m={c[i].x,c[i].y,0,0,1};
				mush.push_back(m);
				
				c[i].state = -1;
				
				if(i !=  19)
				{
					if(c[i+1].state != -1)
					{
						c[i+1].state = 1;
						(c[i+1].x > c[i].x ? c[i+1].dx = -1 : c[i+1].dx = 1);
						c[i+1].dy = 1;
					}
				}
				
				int lc=0;
				for(int z = 0; z < 20; ++z)
				{
					if(c[z].state != -1)
						++lc;
				}
				
				if(lc == 0)
					ResetC();

				bullets[j].state = 0;
			}
		}
		
		//  Hit a mushroom
		for(i = 0; i < mush.size(); ++i)
		{
			if(bullets[j].x == mush[i].x && bullets[j].y == mush[i].y && bullets[j].state != 0 && mush[i].state == 1)
			{
				bullets[j].state = 0;
				mush[i].state = 0;
				score+=3;
			}
		}
		
		//  Went off the screen.
		if(bullets[j].state == 1)
		{
			--bullets[j].y;
			if(bullets[j].y < 0)
				bullets[j].state=0;
		}
	}
}
						
void Init()
{
	wh = GetStdHandle(STD_OUTPUT_HANDLE);
	rh = GetStdHandle(STD_INPUT_HANDLE);
	SMALL_RECT w = {0,0,79,49};
	SetConsoleWindowInfo(wh, TRUE, &w);
	COORD b={80,50};
	SetConsoleScreenBufferSize(wh, b);
}

void Input()
{
	DWORD nE=0;
	DWORD nER=0;
	GetNumberOfConsoleInputEvents(rh,&nE);
	if(nE != 0)
	{
		INPUT_RECORD *eB=new INPUT_RECORD[nE];
		ReadConsoleInput(rh,eB,nE,&nER);
		for(i = 0; i < nER; ++i)
		{
			if(eB[i].EventType == KEY_EVENT)
			{
				if(eB[i].Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
				{
					running=false;
				}
			}
			else if(eB[i].EventType == MOUSE_EVENT)
			{
				s.dx = eB[i].Event.MouseEvent.dwMousePosition.X;
				if(s.dx > 40)
					s.dx=40;
				s.dy=eB[i].Event.MouseEvent.dwMousePosition.Y;
				if(s.dy < 40)
					s.dy = 40;
				if(eB[i].Event.MouseEvent.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED)
				{
					for(int z = 0; z < 2; ++z)
					{
						if(bullets[z].state == 0)
						{
							bullets[z].x = s.x;
							bullets[z].y = s.y-1;
							bullets[z].state = 1;
							break;
						}
					}
				}
			}
		}
		delete eB;
	}
}

void ResetM()
{
	mush.clear();
	for(i = 0;i < 100; ++i)
	{
		u m={rand()%40, (rand()%46)+1, 0, 0, 1};
		mush.push_back(m);
	}
}
								
int main(int argc, char* argv[])
{
	Init();
	ResetC();
	ResetM();
	u b1={ -1, -1, 0, 0, 0};
	bullets.push_back(b1);
	bullets.push_back(b1);
	score=0;
	lives=3;

	while(running)
	{
		ZeroMemory(bb,4000*sizeof(CHAR_INFO));
		Input();
		MoveCP();
		MoveS();
		MoveB();
		for(i = 0; i < 50; ++i)
			SetChar(41, i, 219, 15);
		SetChar(s.x, s.y, 127, 15);
		for(i = 0;i < 20; ++i)
		{
			if(c[i].state == 1)
			{
				SetChar(c[i].x,	c[i].y, 1, 2);
			}
			else if(c[i].state == 0)
			{
				SetChar(c[i].x, c[i].y, 15, 2);
			}
		}

		for(i = 0; i < mush.size(); ++i)
		{
			if(mush[i].state)
			{
				SetChar(mush[i].x, mush[i].y, 6, 14);
			}
		}

		for(i = 0; i < 2; ++i)
		{
			if(bullets[i].state)
				SetChar(bullets[i].x, bullets[i].y, 24, 15);
		}

		WriteConsoleOutput(wh, bb, cBS, cPos, &wA);
		bool ll = false;
		for(i = 0; i < c.size(); ++i)
		{
			if(c[i].state != -1 && c[i].x == s.x && c[i].y == s.y)
			{
				--lives;
				ll=true;
				ResetC();
			}
		}

		int mc = 0;
		for(i = 0; i < mush.size(); ++i)
			if(mush[i].state)
				++mc;
		if(mc<50)
		{
			int nm = 0;
			do
			{
				nm = rand() % mush.size();
			} while(mush[nm].state);
			mush[nm].state = 1;
		}

		SetPos(45,0);
		printf("SANDWORM!");
		SetPos(45, 1);
		printf("Score: %d", score);
		SetPos(45, 2);
		printf("Lives: %d", lives);
		if(!lives)
		{
			SetPos(16, 20);
			printf("GAME OVER");
			Sleep(5000);
			score=0;
			lives=3;
			ResetM();
		}
		else if(ll)
			Sleep(1000);
		else
			Sleep(17);
		++fc;
	}
	return 0;
}