#include <stdlib.h>
#if defined(_MSC_VER)
#include "SDL.h"
#else
#include "SDL/SDL.h"
#endif

#ifdef WIN32
	#include <windows.h>
#endif
/*************************
//openGL definitions
#if defined(__APPLE__) && defined(__MACH__)
	#include <OpenGL/gl.h>
	#include <OpenGL/glu.h>
#else
	#include <GL/gl.h>
	#include <GL/glu.h>
#endif
*************************/

#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <iostream.h>
#include <math.h>
#include <time.h>

//#include <strings.h>
#include <string>


#define PI 3.1415926535897932384626433832795f
#define TICK_INTERVAL 30

using namespace std;

const int SCREEN_WIDTH=800;
const int SCREEN_HEIGHT=600;
const int SCREEN_BPP=32;
const double MAX_RANDOM_ID = 4.999;

const int MAX_COL = 10;
const int MAX_ROW = 13;
const int KEY_PRESS_RATE = 40;

const int CENTX=0;
const int CENTY=0;

const int THE_PILLS = 5; //we will use 4 colors, ending with gray
const int TOTAL_LEVELS = 3;
const int MAX_LEVEL_NUM = 300;

const int X_OFFSET_GRID = 40; //used to move the objects based on grid size
const int Y_OFFSET_GRID = 40;

double pillVel = 2; //Y Axis only
char *pillBMPLocations[4];

int playArea[MAX_COL][MAX_ROW]; 

//int thePillGrid[MAX_COL][MAX_ROW];

int level[3] = {125,150,MAX_LEVEL_NUM};


char totalPoints[6];

int totalPieces; //used to store the current level number of pieces to go
int pillCount; //total pills in play
int currentX;
int currentY;
int playerX; //store current grid location
int playerY; //store current grid location
//int colLocation; //store current location
//int rowLocation; //store current location

int pillIDLeft;
int pillIDRight;
int xOffsetL;
int yOffsetL;
int xOffsetR;
int yOffsetR;

int gRightPosition; //0-3 0 = L on Left, 1 L on Top, 2 L on Right, 3 L on Bottom

bool newGame;
bool gamePause;
bool newPill;

//SDL_RECT NEEDED SO WE CAN MOVE THE OBJECT AROUND
//SDL_Rect *thePillGrid[MAX_COL][MAX_ROW]; //0:Red 1:Green 2:Blue 3:Yellow 4: Gray 5: Blank


SDL_Rect *cPillPLeft;
SDL_Rect *cPillPRight;


SDL_Rect *originalLeft; //store original
SDL_Rect *originalRight; //store original

//SDL_Surface *cPill = NULL;
SDL_Surface *thePills[THE_PILLS];
SDL_Surface *currentPill;

SDL_Surface *gFont;



//stuct 

//SDL_Surface *ball

//background does not need rect, because we are not doing anything with it
SDL_Surface *background = NULL;
SDL_Surface *paused = NULL;
SDL_Surface *screen = NULL;

SDL_Event myEvent;


// Offset of character data per character
int gFontOfs[256];
// Width of character data per character
int gFontWidth[256];

#define PITCH (screen->pitch / 4)



struct colorkey 
{
	int r;
	int g;
	int b;
};

static Uint32 next_time;

Uint32 time_left(void)
{
    Uint32 now;
	
    now = SDL_GetTicks();
    if(next_time <= now)
        return 0;
    else
        return next_time - now;
}


SDL_Surface *load_image(char*filename);
unsigned int blend_avg(unsigned int source, unsigned int target);
unsigned int blend_mul(unsigned int source, unsigned int target);
unsigned int blend_add(unsigned int source, unsigned int target);

void initFonts(void)
{
	
	SDL_Surface *temp = load_image("Dr_Thomas.app/Contents/Resources/data/font.bmp");
	gFont = SDL_ConvertSurface(temp, screen->format, SDL_SWSURFACE);
	SDL_FreeSurface(temp);
	
	if (SDL_MUSTLOCK(gFont))
		if (SDL_LockSurface(gFont) < 0) 
			return;
	
	int i, j, k;
	for (k = 0; k < gFont->h / gFont->w; k++)
	{
		gFontOfs[k] = gFont->w;
		gFontWidth[k] = 0;
		for (i = 0; i < gFont->w; i++)
		{
			for (j = 0; j < gFont->w; j++)
			{
				if (((unsigned int*)gFont->pixels)[(i + k * gFont->w) * 
					(gFont->pitch / 4) + j] != 0) 
				{
					if (j < gFontOfs[k])
						gFontOfs[k] = j;
					
					if (j > gFontWidth[k])
						gFontWidth[k] = j;
				}
			}
		}
		gFontWidth[k] -= gFontOfs[k];
	}
	
	// Unlock if needed
	if (SDL_MUSTLOCK(gFont)) 
		SDL_UnlockSurface(gFont);
}



void drawcharacter(int x, int y, int character, int blend)
{
	if (character == 32)
		return;
	
	character -= 33; // our font does not have the first 33 characters.
	
	// Lock surface if needed
	if (SDL_MUSTLOCK(gFont))
		if (SDL_LockSurface(gFont) < 0) 
			return;
	
	int i, j;
	for (i = 0; i < gFont->w; i++)
	{
		int screenofs = x + (y + i) * PITCH;
		int charofs = (i + character * gFont->w) * 
			(gFont->pitch / 4) + gFontOfs[character];
		for (j = 0; j < gFontWidth[character]; j++)
		{
			int pixel = ((unsigned int*)gFont->pixels)[charofs];
			if (pixel != 0)
			{
				int oldpixel = ((unsigned int*)screen->pixels)[screenofs];
				switch (blend)
				{
					case -1:
						pixel = blend_mul(pixel ^ 0xffffff, oldpixel);
						break;
					case 0:
						pixel = blend_avg(pixel, oldpixel);
						break;
					case 1:
						pixel = blend_add(pixel, oldpixel);
						break;
				}
				((unsigned int*)screen->pixels)[screenofs] = pixel;        
			}
			screenofs++;
			charofs++;
		}
	}
	
	// Unlock if needed
	if (SDL_MUSTLOCK(gFont)) 
		SDL_UnlockSurface(gFont);
}

void drawstring(int x, int y, char *s)
{
	/*
	char string[1024];                  // Temporary string
	
	va_list ap;                         // Pointer To List Of Arguments
	va_start(ap, str);                  // Parses The String For Variables
	vsprintf(string, str, ap);          // And Converts Symbols To Actual Numbers
	va_end(ap);                         // Results Are Stored In Text
	*/
	
	while (*s != 0)
	{
		drawcharacter(x + 1, y + 1, *s, -1);
		drawcharacter(x, y, *s, 0);
		if (*s == 32)
			x += gFont->w / 2;
		else
			x += gFontWidth[*s - 33];
		s++;
	}
}


unsigned int blend_avg(unsigned int source, unsigned int target)
{
    unsigned int sourcer = (source >>  0) & 0xff;
    unsigned int sourceg = (source >>  8) & 0xff;
    unsigned int sourceb = (source >> 16) & 0xff;
    unsigned int targetr = (target >>  0) & 0xff;
    unsigned int targetg = (target >>  8) & 0xff;
    unsigned int targetb = (target >> 16) & 0xff;
	
    targetr = (sourcer + targetr) / 2;
    targetg = (sourceg + targetg) / 2;
    targetb = (sourceb + targetb) / 2;
	
    return (targetr <<  0) |
		(targetg <<  8) |
		(targetb << 16);
}

unsigned int blend_mul(unsigned int source, unsigned int target)
{
    unsigned int sourcer = (source >>  0) & 0xff;
    unsigned int sourceg = (source >>  8) & 0xff;
    unsigned int sourceb = (source >> 16) & 0xff;
    unsigned int targetr = (target >>  0) & 0xff;
    unsigned int targetg = (target >>  8) & 0xff;
    unsigned int targetb = (target >> 16) & 0xff;
	
    targetr = (sourcer * targetr) >> 8;
    targetg = (sourceg * targetg) >> 8;
    targetb = (sourceb * targetb) >> 8;
	
    return (targetr <<  0) |
		(targetg <<  8) |
		(targetb << 16);
}

unsigned int blend_add(unsigned int source, unsigned int target)
{
    unsigned int sourcer = (source >>  0) & 0xff;
    unsigned int sourceg = (source >>  8) & 0xff;
    unsigned int sourceb = (source >> 16) & 0xff;
    unsigned int targetr = (target >>  0) & 0xff;
    unsigned int targetg = (target >>  8) & 0xff;
    unsigned int targetb = (target >> 16) & 0xff;
	
    targetr += sourcer;
    targetg += sourceg;
    targetb += sourceb;
	
    if (targetr > 0xff) targetr = 0xff;
    if (targetg > 0xff) targetg = 0xff;
    if (targetb > 0xff) targetb = 0xff;
	
    return (targetr <<  0) |
		(targetg <<  8) |
		(targetb << 16);
}


SDL_Surface *load_image_key(char*filename, colorkey userKey)
{
	//Uint32 colorkey;
	SDL_Surface* loadedImage = NULL;
	SDL_Surface* optimizedImage = NULL;
	
	loadedImage = SDL_LoadBMP(filename);
	
	if(loadedImage != NULL)
	{
		optimizedImage = SDL_DisplayFormat( loadedImage );
		SDL_FreeSurface(loadedImage);
		
		if(optimizedImage != NULL)
		{
			SDL_SetColorKey(optimizedImage, SDL_SRCCOLORKEY,
							SDL_MapRGB(optimizedImage->format,userKey.r, userKey.g, userKey.b));
		}
		
	}
	return optimizedImage;

}

SDL_Surface *load_image(char*filename)
{
	SDL_Surface* loadedImage = NULL;
	SDL_Surface* optimizedImage = NULL;
	
	loadedImage = SDL_LoadBMP(filename);
	
	if(loadedImage != NULL)
	{
		optimizedImage = SDL_DisplayFormat( loadedImage );
		SDL_FreeSurface(loadedImage);
		
	}
	else
	{
		
	}
	return optimizedImage;
	
	
}


void apply_surface(int x, int y, SDL_Surface*source, SDL_Surface*destination)
{
	SDL_Rect offset;
	offset.x = x;
	offset.y = y;
	SDL_BlitSurface(source,NULL,destination,&offset);
}


int init(void) {
//LOADING SDL and LOADING IMAGES
	
	
	if( SDL_Init(SDL_INIT_EVERYTHING) == -1)
	{
		return 0;
	}
	screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE);
	if( screen == NULL)
	{
		return false;
	}
	
	SDL_WM_SetCaption("Dr. Thomas", NULL);
	
	
	//initFonts moved to font class
	initFonts();
	
	
	background = load_image("Dr_Thomas.app/Contents/Resources/data/background.bmp");
	paused = load_image("Dr_Thomas.app/Contents/Resources/data/paused.bmp");
	pillBMPLocations[0] = "Dr_Thomas.app/Contents/Resources/data/gray.bmp";
	pillBMPLocations[1] = "Dr_Thomas.app/Contents/Resources/data/red.bmp"; 
	pillBMPLocations[2] = "Dr_Thomas.app/Contents/Resources/data/green.bmp";
	pillBMPLocations[3] = "Dr_Thomas.app/Contents/Resources/data/blue.bmp";
	pillBMPLocations[4] = "Dr_Thomas.app/Contents/Resources/data/yellow.bmp";
	pillBMPLocations[5] = "Dr_Thomas.app/Contents/Resources/data/xgrid.bmp";
	
	colorkey myColorkey;
	myColorkey.r = 255;
	myColorkey.g = 0;
	myColorkey.b = 255;
	
	colorkey altColorkey;
	altColorkey.r = 0;
	altColorkey.g = 255;
	altColorkey.b = 255;
	
	thePills[0] = load_image_key(pillBMPLocations[0],  myColorkey);
	thePills[1] = load_image_key(pillBMPLocations[1],  altColorkey);
	thePills[2] = load_image_key(pillBMPLocations[2],  myColorkey);
	thePills[3] = load_image_key(pillBMPLocations[3],  myColorkey);
	thePills[4] = load_image_key(pillBMPLocations[4],  myColorkey);
	thePills[5] = load_image_key(pillBMPLocations[5],  myColorkey);
	
	return 0;

}


void cleanup(void)
{
	//clean up SDL_Surface
	SDL_FreeSurface(screen);
	SDL_FreeSurface(background);
	
	SDL_Quit();
}

void showCurrentPill(void)
{
	//apply_surface x, y, source, destination
	
	//just the current pill in motion, no need for the array yet
	//Left
	apply_surface(currentX-xOffsetL,currentY-yOffsetL,thePills[pillIDLeft],screen);
	
	//Right
	apply_surface(currentX-xOffsetR,currentY-yOffsetR,thePills[pillIDRight],screen);

}


void showStoredPills(void)
{
	int tmpX, tmpY;
	for(int row = 0; row < MAX_ROW;row++)
	{
		for(int col = 0; col < MAX_COL ;col++)
		{
			
				//found a pill, display it
				
				if(playArea[col][row] > 0)
				{
					tmpX = (col*40)+200;
					tmpY = (row*40)+40;
					 //must apply some off set for XPos and YPos
					apply_surface(tmpX,tmpY,thePills[playArea[col][row]],screen);
				}
		}
	}
	
}

void initPills(void)
{
	for(int i = 0; i < MAX_ROW;i++)
	{
		for(int j = 0; j < MAX_COL;j++)
		{
			playArea[i][j] = -1;
		}
	}	
}

void storePill(void)
{
	
	int storeY,storeX;
	//int tmpR = pillIDRight;
	//int tmpL = pillIDLeft;
	
	
	storeX = ((currentX-xOffsetL)-200)/40;
	storeY = ((currentY-yOffsetL)-40)/40;
	
	playArea[storeX][storeY] = pillIDLeft;

	storeX = ((currentX-xOffsetR)-200)/40;
	storeY = ((currentY-yOffsetR)-40)/40;

	playArea[storeX][storeY] = pillIDRight;
	

		
}

void swap(int &r1, int &r2)
{
	
	//how do you pass a reference to an Array?
	int temp=r1;
	r1=r2;
	r2=temp;
}


int getLocation(int x, int y)
{
	
		
		int gridX = (x-200)/40;
		int gridY = (y-40)/40;
		
		
		if(playArea[gridX][gridY] > 0) 
		{
			if((playArea[gridX][gridY] >=0) && (playArea[gridX][gridY]<5))
			{
				//cout << "\ngrid x : " << gridX << "\ngrid y : " << gridY << "\n\n";
				return playArea[gridX][gridY];
			}
			else
			{
				return 0;
			}
		}
		else
		{
			return 0;
		}
	
}

void collapse()
{
	int center, left, top, right, bottom;
	
	int currentCheck;
	//int totalRow;
	//int rowToCollapse[MAX_ROW][MAX_COL];
	
	int tempClear[MAX_ROW][MAX_COL];
	
	
	
	
	for(int i = 0; i < MAX_ROW;i++)
	{
		for(int j = 0; j < MAX_COL;j++)
		{

		}
	}
	
}



void drop()
{
	int to=-1;
	for(int j=0;j<MAX_COL;j++)
	{
		to=-1;
		for(int i=MAX_ROW-1;i>=0;i--)
		{
			if(to==-1 && playArea[i][j]==-1) {to=i;}
			else if(to!=-1 && playArea[i][j]!=-1) 
			{
				swap(playArea[i][j],playArea[to][j]); 
				to--;
			}
		}
	}
	
	
}
int bunch(int i,int j)
{
	int cols = MAX_COL;
	int rows = MAX_ROW;
	int a = playArea[i][j];
	if(i!=0 && playArea[i-1][j] == a) return 1;
	if(j!=0 && playArea[i][j-1] == a) return 1;
	if(i<rows-1 && playArea[i+1][j] == a) return 1;
	if(j<cols-1 && playArea[i][j+1] == a) return 1;
	return 0;
}


bool collide()
{
	int tmpPos;
	tmpPos = 0;
	
	//vertical detection first

	if((gRightPosition == 1) || (gRightPosition == 3)) {
		
		
		tmpPos = getLocation(currentX, currentY+20+pillVel);
		if(tmpPos > 0)
		{
			//good for gRightPosition == 1, 
			return true;
		}
		
	}
	else
	{

	
	//then horizontal
		tmpPos = getLocation(currentX+xOffsetR+40, currentY+yOffsetR+20+pillVel);
		
		if(tmpPos > 0)
		{
			//good

			return true;
		}
		tmpPos = getLocation(currentX+xOffsetL+40, currentY+yOffsetL+20+pillVel);
		if(tmpPos > 0)
		{

			return true;
		}
		
	//end of else statement	
	}

	if((currentY > 520) || (currentY < 0)) 
	{
		return true;
	}		

	return false;
	
	
}

	
void loadNewPill(void)
{
	double r,r2;
	double randID, randID2;

	//start position
	currentX = (SCREEN_WIDTH/2)-40;
	currentY = 20;
	
	for( int i = 0; i < 103; i++)
	{
		rand();
	}
	
	//generates true random between 0 - 4
	r = ((double)rand())/ ((double)RAND_MAX +(double)(1));
	randID = (int)(double(r * MAX_RANDOM_ID)-double(1));
	
	for( int i = 0; i < 57; i++)
	{
		rand();
	}
	r2 = ((double)rand())/ ((double)RAND_MAX +(double)(1));
	randID2 = (int)(double(r2 * MAX_RANDOM_ID)-double(1));
	
	randID++;
	randID2++;
	
	//default offset 
	xOffsetR = 0;
	yOffsetR = 0;
	xOffsetL = -40;
	yOffsetL = 0;
	gRightPosition = 0;
	
	//store current pill ID 
	pillIDLeft = ((int)randID);
	pillIDRight = ((int)randID2);
}

void gamePlay(void)
{
	if (pillCount < 1)
	{
		newPill = true;
	}
	
	showCurrentPill();
	showStoredPills();
	
	//move pill down
	currentY = currentY + pillVel;
	
	//player location
	if(collide())
	{
		storePill();
		newPill = true;
	}
	
	//add bouding box information, probally a function for active pill locations
	
	if (newPill == true) 
	{
		collapse();
		loadNewPill();
		pillCount++;
		newPill = false;
		//move current pill, to fillUp state
	}
	
	//Collapse and Add points
	//collapse();
	
}

int drawScreen(void)
{

	SDL_BlitSurface(background, NULL, screen, NULL);
	int points = 230;
	std::string outputString = "%i" + points;
	
	outputString.str_c();
	
	//char test[6] = (char)outputString;
	
	
	
	//char outChar = "%i", points;
		drawstring(100, 100, "%i", outputString.str_c());
		
		//CHECK FIRST PILL?
		if(!gamePause) 
		{
			gamePlay();
		}
		if(gamePause == true)
		{
			apply_surface(200,40,paused,screen);
		}
		
				
	
		if(SDL_Flip(screen) == -1)
		{
			return 0;
		}
		return true;
}


int main(int argv, char*argc[])
{
	gamePause = false;
	bool gameOver = false;
	bool done = false;
	
	//call radom numbers, so that Rand() is really random
	//initialize one time only
	srand((unsigned)time(0));
	
	newGame = true;
	
	
	init(); //load things
	
	while(!done)
	{
		
		
		if(gameOver)
		{
			gameOver = false;
			newGame = true;
		}
		
		
		if(newGame)
		{
			pillCount = 0;
			totalPieces = level[0];
			initPills(); //set pills to 0
			newGame = false;
		}
		
		if( pillCount > totalPieces)
		{
			done = true;
		}
		

		
		drawScreen();
		next_time = SDL_GetTicks() + TICK_INTERVAL;
		SDL_Delay(time_left());
		next_time += TICK_INTERVAL;
		
		
		//getPoints
		
		//add timer for levels
		//add checkLevel up?
		
		//add bonus for combo's
		
		while(gamePause)
		{
			//display paused image
			
		
			while(SDL_PollEvent(&myEvent)) //address of myEvent
			{
				switch (myEvent.type) 
				{
					case SDL_KEYDOWN:
						switch(myEvent.key.keysym.sym)
						{
							case SDLK_SPACE:
								gamePause = false;
								break;
							case SDL_QUIT:
								cleanup(); //unload things
								return 0;
								break;
							case SDLK_n:
								newGame=true;
								break;
							default:
								break;
						}
				}
			}
		}

		
		while(SDL_PollEvent(&myEvent)) //address of myEvent
		{
			switch (myEvent.type) 
			{
				case SDL_KEYDOWN:
					switch(myEvent.key.keysym.sym)
					{
						case SDL_QUIT:
							done = true;
							break;
						case SDLK_ESCAPE:
							done = true;
							break;
						case SDLK_LEFT:
						
								if(currentX > 220)
								{
									
									if((gRightPosition == 0) || (gRightPosition == 2))
										cout << "\ncurrentx 220 : ";
										cout << getLocation(currentX-40, currentY+20+pillVel);
										if(getLocation(currentX-40, currentY+20+pillVel) < 1)
										{
											currentX -= (double)KEY_PRESS_RATE;
										}
									else
									{
										if(getLocation(currentX-40, currentY-40+20+pillVel) < 1)
										{
											currentX -= (double)KEY_PRESS_RATE;
										}
									}
								}

							
							break;
						case SDLK_RIGHT:

							if((gRightPosition == 0) || (gRightPosition == 2))
							{

								if(currentX < (520) )
								{

									if( getLocation(currentX+80, currentY+20+pillVel) < 1)
									{
										currentX += (double)KEY_PRESS_RATE;
									}
									
								}
									
							}
							else
							{
								if(currentX < (560) )
								{
									
									cout << "\ncurrentx 560A : ";
									cout << getLocation(currentX, currentY-40+20+pillVel);
									cout << "\ncurrentx 560B : ";
									cout << getLocation(currentX-40, currentY+20+pillVel);
									
									//works for top piece
									
									if(getLocation(currentX+40, currentY-40+20+pillVel) < 1)
									{
										currentX += (double)KEY_PRESS_RATE;
									}
									


									
								}	
							}
							
							break;
						case SDLK_UP:
							if(currentX == 560)
							{
								//move piece back, away from wall
								currentX -= (double)KEY_PRESS_RATE;	
							}
							
							switch(gRightPosition)
							{
								case 0: 
									xOffsetL = 0;
									yOffsetL = 40;
									xOffsetR = 0;
									yOffsetR = 0;
									gRightPosition = 1;
									break;
								
								case 1: 
									xOffsetL = 0;
									yOffsetL = 0;
									xOffsetR = -40;
									yOffsetR = 0;
									gRightPosition = 2;
									break;
									
								case 2: 
									xOffsetL = 0;
									yOffsetL = 0;
									xOffsetR = 0;
									yOffsetR = 40;
									gRightPosition = 3;
									break;
									
								case 3: 

									xOffsetL = -40;
									yOffsetL = 0;
									xOffsetR = 0;
									yOffsetR = 0;
									gRightPosition = 0;
									break;
							}
							cout << "\n Current R Position:  " << gRightPosition << "\n";
						
							break;
						case SDLK_n:
							newPill = true;
							break;
							
						case SDLK_x:
							newGame = true;
							break;
						
						case SDLK_SPACE:
							gamePause =true;
							break;
						case SDLK_DOWN:
							pillVel *= (5.75);
							break;
						default:
							break;
					}
					break;
				case SDL_KEYUP:
					switch(myEvent.key.keysym.sym)
					{
						case SDL_QUIT:
							done = true;
							break;
						case SDLK_ESCAPE:
							done = true;
							break;
						case SDLK_LEFT:
							break;
						case SDLK_RIGHT:
							break;
						case SDLK_DOWN:
							pillVel = pillVel / (5.75);
							break;
						default:
							break;
					}
					break;
				case SDL_QUIT:
					done = true;
					break;
			}

		} 
	} 

	cleanup(); //unload things
	
	return 0;
}