#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "game.h"

struct Game game;
// Test player
struct Tile playerTileDir[4*4]={
	{"up 1",    -1,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(0),TY(0),20,20,10,20,3,250,0},
	{"down 1",  -2,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(3),TY(0),20,20,10,20,3,250,0},
	{"left 1",  -3,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(6),TY(0),20,20,10,20,3,250,0},
	{"right 1", -4,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(9),TY(0),20,20,10,20,3,250,0},

	{"up 2",    -5,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(0),TY(1),20,20,10,20,3,250,0},
	{"down 2",  -6,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(3),TY(1),20,20,10,20,3,250,0},
	{"left 2",  -7,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(6),TY(1),20,20,10,20,3,250,0},
	{"right 2", -8,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(9),TY(1),20,20,10,20,3,250,0},

	{"up 3",    -9,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(0),TY(2),20,20,10,20,3,250,0},
	{"down 3", -10,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(3),TY(2),20,20,10,20,3,250,0},
	{"left 3", -11,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(6),TY(2),20,20,10,20,3,250,0},
	{"right 3",-12,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(9),TY(2),20,20,10,20,3,250,0},

	{"up 4",   -13,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(0),TY(3),20,20,10,20,3,250,0},
	{"down 4", -14,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(3),TY(3),20,20,10,20,3,250,0},
	{"left 4", -15,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(6),TY(3),20,20,10,20,3,250,0},
	{"right 4",-16,TILEF_ANIMATED|TILEF_OSCILLATE,MAP_CHARACTER,	TX(9),TY(3),20,20,10,20,3,250,0},
};

// list of all of the tiles and where they are on the tile sheet.	
struct Tile tileset[32]={
	// 0= player 
	{"player",  0,TILEF_ANIMATED|TILEF_FOURVIEW_BELOW|TILEF_OSCILLATE,MAP_CHARACTER,	TX(0),TY(0),20,20,10,20,3,250,0},
	// 1 = carpet
	{"carpet",  1, 0,MAP_FLOOR, TX(12),TY(0),20,20,0,0,1,1,1},
	// 2 = desk
	{"desk",   2, 0,MAP_DESK, TX(7),TY(4),20,20,0,0,1,1,1},
	// 3 = chair
	{"chair",    3, 0,MAP_ITEM, TX(8),TY(4),20,20,0,0,1,1,1},
	// 4 = office wall
	{"office",  4, 0,MAP_WALL, TX(12),TY(1),20,20,0,0,1,1,1},
	// 5 = drink machine
	{"drink machine", 5, 0,MAP_ITEM, TX(14),TY(3),20,20,0,0,1,1,1},
	// 6 = candy dispenser
	{"candy dispenser",6, 0,MAP_ITEM, TX(13),TY(2),20,20,0,0,1,1,1},
	// 7 = water dispenser
	{"water cooler",7, 0,MAP_ITEM, TX(14),TY(2),20,20,0,0,1,1,1},
	// 8 = plant
	{"plant",8, 0,MAP_ITEM, TX(13),TY(3),20,20,0,0,1,1,1},
	// 9 = spawn point
	{"spawn point",9, TILEF_NPCSPAWNER,MAP_FLOOR, TX(12),TY(0),20,20,0,0,1,1,5000},
	// 10 = top wall
	{"top wall",10, 0,MAP_WALL, TX(0),TY(4),20,20,0,0,1,1,1},
	// 11 = right wall
	{"right wall",11, 0,MAP_WALL, TX(1),TY(4),20,20,0,0,1,1,1},
	// 12 = left wall
	{"left wall",12, 0,MAP_WALL, TX(2),TY(4),20,20,0,0,1,1,1},
	// 13 = bottom wall
	{"bottom wall",13, 0,MAP_WALL, TX(3),TY(4),20,20,0,0,1,1,1},
	// 14 = top-left wall
	{"TL wall",14, 0,MAP_WALL, TX(0),TY(5),20,20,0,0,1,1,1},
	// 15 = right wall
	{"TR wall",15, 0,MAP_WALL, TX(1),TY(5),20,20,0,0,1,1,1},
	// 16 = left wall
	{"BL wall",16, 0,MAP_WALL, TX(2),TY(5),20,20,0,0,1,1,1},
	// 17 = bottom wall
	{"BR wall",17, 0,MAP_WALL, TX(3),TY(5),20,20,0,0,1,1,1},
	// 18 = exit
	{"exit",18, 0,MAP_EXIT, TX(6),TY(4),20,20,0,0,1,1,1},
	// 19 = door
	{"door",19, 0,MAP_DOOR, TX(4),TY(4),20,20,0,0,1,1,1},
	// 20 = door
	{"open door",20, 0,MAP_FLOOR, TX(5),TY(4),20,20,0,0,1,1,1},
	// 21 = cabinet
	{"cabinet", 21, 0,MAP_ITEM, TX(12),TY(2),20,20,0,0,1,1,1},
};
int tilesetCount=22;

struct Tile avatar[16]={
	{"Les",     128, 0,MAP_CHARACTER, 0,384,128,128,0,0,1,1,1},
	{"Jason",   129, 0,MAP_CHARACTER, 128,384,128,128,0,0,1,1,1},
	{"Bill",    130, 0,MAP_CHARACTER, 256,384,128,128,0,0,1,1,1},
	{"Chris",   131, 0,MAP_CHARACTER, 384,384,128,128,0,0,1,1,1},
	{"Passcard",132, 0,MAP_CHARACTER, TX(16),TY(2),42,42,0,0,1,1,1},
	{"Wine",    133, 0,MAP_CHARACTER, TX(11),TY(4),42,42,0,0,1,1,1},
	{"Candy Bar",134, 0,MAP_CHARACTER, TX(9),TY(4),42,42,0,0,1,1,1},
	{"Stapler", 135, 0,MAP_CHARACTER, TX(0),TY(6),42,42,0,0,1,1,1},
	{"Paperclip",136, 0,MAP_CHARACTER, TX(2),TY(6),42,42,0,0,1,1,1},
	{"Binder clip",137, 0,MAP_CHARACTER, TX(4),TY(6),42,42,0,0,1,1,1},
	{"Notebook", 138, 0,MAP_CHARACTER, TX(6),TY(6),42,42,0,0,1,1,1},
};

struct Tile buttontile[4]={
	{"Cross",   256, 0,MAP_CHARACTER, TX(4),TY(5),20,20,0,0,1,1,1},	
	{"Square",  257, 0,MAP_CHARACTER, TX(5),TY(5),20,20,0,0,1,1,1},	
	{"Triangle",258, 0,MAP_CHARACTER, TX(6),TY(5),20,20,0,0,1,1,1},	
	{"Circle",  259, 0,MAP_CHARACTER, TX(7),TY(5),20,20,0,0,1,1,1},	
};

struct Tile pointtile[5]={
	{"175",   512, 0,MAP_CHARACTER, TX(14),TY(0),20,20,0,0,1,1,1},	
	{"100",   513, 0,MAP_CHARACTER, TX(15),TY(0),20,20,0,0,1,1,1},	
	{"50",    514, 0,MAP_CHARACTER, TX(16),TY(0),20,20,0,0,1,1,1},	
	{"25",    515, 0,MAP_CHARACTER, TX(17),TY(0),20,20,0,0,1,1,1},	
	{"1000",  516, 0,MAP_CHARACTER, TX(13),TY(0),20,20,0,0,1,1,1},	
};

const char *names[]={
	"Mike","Jim","Dave","John","Bob","Chris","Bill","Jason","Brian","Scott",
	"Rick","Jeff","Mark","Kevin","Joe","Steve","Tom","Eric","Matt","Dan",
	"Tim","Chuck","Anthony","Paul","Ken","Greg","Shawn","Chad","Todd","Ron",
	"Ed","Andy","Don","Pat","Keith","Gary","Doug","Aaron","George","Craig",
	"Larry","Jeremy","Ryan","Ray","Frank","Jose","Terry","Adam","Brad",
	"Rod","Shane","Travis","Tony","Derek","Russ","Marcus","Sam","Randy",
	"Ben","Carl","Phil","Corey","Joshua","Juan","Jerry","Lawrence","Joel",
	"Curtis","Vince","Carlos","Walt","Brett","Wayne","Brandon","Jamie","Lance",
	"Alan","Darren"
};
const int nameCount=sizeof(names)/sizeof(char *);

struct Comment newgame_comment[]={
	{100,"Les, congrats on that promotion"},
	{100,"I can't beleive they got you drunk"},
	{100,"I guess it is a rite of passage"},
	{100,"I think you should go home"},
	{100,"Well, you need to find the exit"},
	{50,"I think the subway is still running"},
	{50,"You will need a passckey to open a door"},
	{25,"Borrow keys from people's desks."},
	{100,"Keep drunk or you'll never make it home"},
},
comment[]={
	{50,"You are drunk! Get away!"},
	{0,"I think the way out is to the East"},
	{100,"Here have an other. Check the drink machine."},
	{0,"I think the way out is to the West"},
	{50,"Go home. sleep it off."},
	{0,"I think the way out is to the South"},
	{100,"I'm calling security"},
	{0,"I think the way out is to the North"},
	{100,"Keep away from my desk"},
	{50,"Do you belong here?"},
	{100,"Buddy, you need to get home"},
	{100,"I'm reporting you to your manager"},
	{50,"That was some party.  Congrats."},
	{25,"Have you seen my keys?"},
	{25,"I should finish this work up"},
	{0,"Do you have that report ready for me yet?"},
	{0,"Want to go to a pub with me?"},
	{0,"Hey did I miss the action?"},
	{25,"I hope you're not driving"},
	{25,"Coffee would be nice...Irish coffee?"},
	{0,"Jenny was sure hot at that party"},
	{0,"I've never seen you so drunk"},
	{100,"Don't toss your cookies on my new shoes."},
	{0,"You look wasted man, try and get out fast."},
	{100,"Hey! Don't go near my desk."},
	{0,"Maybe a drink of water would do you good."},
	{100,"What do you think you're doing?"},
	{25,"I thought you were headed home."},
	{25,"I know a great hangover cure."},
	{50,"You're a wreck"},
	{50,"Why did they even give you that raise?"},
	{50,"You'll regret it in the morning."},
	{50,"My raise party is next!"},
	{100,"It's a good thing the boss was blitzed too."},
	{25,"Did you enjoy my Scotch?"},
	{25,"You buy for me next time."},
	{100,"Someone should escort you out."},
	{100,"I hope you're not driving like that."},
};
const int newgame_commentCount=sizeof(newgame_comment)/sizeof(struct Comment);
const int commentCount=sizeof(comment)/sizeof(struct Comment);

struct HighScore defaultHighScore[16]={
	{"HardHat",60000,8},
	{"A_Nub",50000,7},
	{"Birdman12078",45000,6},
	{"Zion",38000,5},
	{"WernerVonBraun",32000,4},
	{"Wally",26000,3},
};
	
struct Character *newCharacter(enum CharacterType type,struct Tile *tile,int x,int y)
{
	struct Character *c=calloc(1,sizeof(struct Character));
	static int id=0;
	static int commentId=0;
	
	c->type=type;
	c->tile=tile;
	c->x=x;
	c->y=y;
	c->name=names[id];
	id++;
	if(id>=nameCount) id=0;
	c->comment=comment[commentId].comment;
	c->commentPoints=comment[commentId].points;
	commentId++;
	if(commentId>=commentCount) commentId=0;
		
	return c;
}

void initGame()
{
	// One time initializations go here.
	srand(time(0));
	newGame();
	setMode(GAME_INTRO);
	memcpy(game.highscore,defaultHighScore,sizeof(defaultHighScore));
	FILE *file=fopen("data/highscore.bin","rb");
	if(file) {
		fread(game.highscore,sizeof(game.highscore),1,file);
		fclose(file);
	}
}

void newGame()
{
	// Resets the game for a new game round.
	game.score=0;
	game.level=1;
	game.lives=5;
	game.health=50;
	game.healthTimer=0;
	game.keys=0;
	while(game.firstCharacter) {
		struct Character *c=game.firstCharacter;
		game.firstCharacter=c->next;
	}
	game.characterCount=0;
	game.player=0;
	loadLevel(1);
}

struct Tile *getTile(int x,int y);
int walkableOrDoor(int tx,int ty)
{
	struct Tile *tile=getTile(tx,ty);
	if(tile && (tile->type==MAP_FLOOR || tile->type==MAP_ITEM)) return 1;	// walkable
	if(tile && tile->type==MAP_DESK) return 2;	// hiding spot
	if(tile && tile->type==MAP_DOOR) return 3;	// door
	if(tile && tile->type==MAP_EXIT) return 4; // exit
	return 0;	// wall
}

int door_x[4096],door_y[4096];
int nextDoor;
int currentDoor;

int fillPath(char *path,int x,int y)
{
	int count=0;
	int id;
	
	if(x<0 || y<0 || x>=game.mapWidth || y>=game.mapHeight) return count;
	id=path[x+y*game.mapWidth];
	if(id==3) {
		count++;
		path[x+y*game.mapWidth]=-id;	// No double counts.
		door_x[nextDoor]=x;
		door_y[nextDoor++]=y;
	} else if(id==1 || id==2) {
		path[x+y*game.mapWidth]=-id;	// No double counts.		
		// Now recurse
		count+=fillPath(path,x+1,y-1);
		count+=fillPath(path,x+1,y);
		count+=fillPath(path,x-1,y+1);

		count+=fillPath(path,x+1,y-1);
		count+=fillPath(path,x+1,y);
		count+=fillPath(path,x+1,y+1);

		count+=fillPath(path,x,y+1);
		count+=fillPath(path,x,y-1);
	}
	return count;
}

int populateKeyDesk(char *path,int x,int y)
{
	//flood fill from player start.
	path[x+y*game.mapWidth]=1;	// if it is a door, open it.
	int doors=fillPath(path,x,y);
	
	// Now we need to pick doors desks to fill.
	int dcount=0;
	for(y=0;y<game.mapHeight;y++) {
		for(x=0;x<game.mapWidth;x++) {
			if(path[x+y*game.mapWidth]==-2) dcount++;
		}
	}
	if(dcount==0) return 0;
	// Now two different possible desks
	int d1=rand()/(RAND_MAX/dcount);
	int d2=rand()/(RAND_MAX/dcount);
	if(d1==d2) d2++;
	d1=d1%dcount;
	d2=d2%dcount;
	int i=0;
	for(y=0;y<game.mapHeight;y++) {
		for(x=0;x<game.mapWidth;x++) {
			if(path[x+y*game.mapWidth]==-2) {
				i++;
				if(i==d1 || i==d2) game.map[x+y*game.mapWidth].flags|=MAPF_KEY;
			}
		}
	}
	
	return doors;
}

void populateKeys()
{
	// Grow a solution through doors to the exit, and count how many keys we need to place.	
	int x,y;
	char *path=calloc(game.mapWidth,game.mapHeight);
	
	// Now we need to place that many keys in the appropriate areas to allow a valid path.
	for(y=0;y<game.mapHeight;y++) {
		for(x=0;x<game.mapWidth;x++) {
			int walk=walkableOrDoor(x,y);
			path[x+y*game.mapWidth]=walk;
		}
	}
	
	door_x[0]=1;
	door_y[0]=1;
	nextDoor=1;
	currentDoor=0;
	while(currentDoor<nextDoor) {
		populateKeyDesk(path,door_x[currentDoor],door_y[currentDoor]);
		currentDoor++;
	}
	free(path);
}

int populateItems(int flag)
{
	int places=0;
	int x,y;
	for(y=0;y<game.mapHeight;y++) {
		for(x=0;x<game.mapWidth;x++) {
			struct Tile *tile=getTile(x,y);
			switch(tile->type) {
			case MAP_ITEM:
				if(flag==MAPF_CANDY) {
					switch(tile->id) {
					case 8:
						places++;
						if(places%2!=0) break;
						game.map[x+y*game.mapWidth].flags|=flag;
						break;
					case 6:
						places++;
						game.map[x+y*game.mapWidth].flags|=flag;
					}
				} else if(flag==MAPF_BOTTLE) {
					switch(tile->id) {
					case 7:
						//if(places%2==0) break;
					case 5:
						places++;
						game.map[x+y*game.mapWidth].flags|=flag;
					}
				} else if(flag==MAPF_SPECIAL) {
					if(tile->id==21) {
						game.map[x+y*game.mapWidth].flags|=flag;
					}
				}
				break;
			case MAP_DESK:
				places++;
				if(places%(flag==MAPF_BOTTLE?15:6)!=flag) break;
				game.map[x+y*game.mapWidth].flags|=flag;
				break;
			default:
				break;
			}
		}
	}
	return places;
}

int loadLevel(int level)
{
	char fname[256];
	char strLine[512];//for extra data /just to get to the next line
	int ch = 0;//current char
	int temp=0,temp1=0,temp2 =0;//to calculate the tile type
				
	// heres the important data im not sure how i can implement it into your engine
	int width=0,height=0;
	int map[MAX_TILES];//array to load the map data into
	memset(map,0,sizeof(int)*MAX_TILES);
				
				// Now open the actual new level file.
	sprintf(fname,"map/level%03i.lvl",level);
	FILE *file=fopen(fname,"r");
	if(!file) {
		insertMessage("Game Over: You win, you made it home!",tileset+18,(0xc0408f40),2000);
		setMode(GAME_SUMMARY);
		return -5;// less than 0 on error
	}

	// Clear out the old level, if any
	if(game.map) {
		free(game.map);
		game.map=0;
	}
	struct Character *next=game.firstCharacter;
	while(next) {
		struct Character *c=next;
		next=next->next;
		free(c);
	}
	game.characterCount=0;
	game.firstCharacter=0;
	game.player=0;
										
	// Now load the actual new level specified in level.
	while(1){//parse through comments and get size of level
 		ch = fgetc(file);//get next char
		if (ch == '#') fgets(strLine, 400, file); //if commented skip to next line
		
		if (ch == 'w') {
			ch = fgetc(file);//get next char
			if (ch == '=') {
				ch = fgetc(file);//get next char
				temp = atoi((char *)&ch);
				ch = fgetc(file);//get next char
					if ( ch==' '||ch =='\n'||ch=='\r'){
						width = temp;
					}
					else{
						temp1 = atoi((char *)&ch);
						width = temp*10 + temp1;
					}
					ch = fgetc(file);
					if (ch != '\n' && ch!='\r') {
						temp2 = atoi((char *)&ch);
						width = width*10 + temp2;
					}
					
			}
			
			printf("width=%d\n",width);
		}
		if (ch == 'h') {
			ch = fgetc(file);//get next char
			if (ch == '=') {
				ch = fgetc(file);//get next char
				temp = atoi((char *)&ch);
				ch = fgetc(file);//get next char
					if (ch ==' '||ch =='\n'||ch =='\r'){
						height = temp;
					}
					else{
						temp1 = atoi((char *)&ch);
						height = temp*10 + temp1;}

					ch = fgetc(file);
					if (ch != '\n' && ch!='\r') {
						temp2 = atoi((char *)&ch);
						height = height*10 + temp2;
					}
					
					
			}
			printf("height=%d\n",height);
		}
		
		if ((height > 0)&&(width > 0)){
			// fgets(strLine, 400, file);
			break;}
	}
	temp = 0;
	temp1 = 0;
	int i=0;
	ch = fgetc(file);
	while (i < MAX_TILES){
		if (ch == '#') fgets(strLine, 400, file); //if commented skip to next line
		while (ch == ',' || ch==' ' || ch=='\n' || ch=='\r') ch = fgetc(file);
		if(ch==EOF) break;
		temp = atoi((char *)&ch);
		ch = fgetc(file);//get next char
		if (ch==' ' || ch==',' || ch=='\n' || ch=='\r'){
			map[i] = temp;
			if(i==0) printf("tile %dx%d=%d\n",i%width,i/width,map[i]);
		}
		else{
			temp1 = atoi((char *)&ch);
			map[i] = temp*10 + temp1;
			if(i==0) printf("tile %dx%d=%d\n",i%width,i/width,map[i]);
		}
		i ++;
		ch = fgetc(file);//get next char
		if (map[width*height] > 0) break;
	}
	printf("read level\n");
    fclose(file);
						
	game.itemCount=0;
	game.mapWidth=width;
	game.mapHeight=height;
	game.map=calloc(sizeof(struct Map),width*height);
	for(i=0;i<width*height;i++) {
		if(map[i]>0 && map[i]<tilesetCount) {
			game.map[i].tile=&tileset[map[i]];
		} else if(map[i]==0) {
			game.map[i].tile=&tileset[1];	// carpet
			if(!game.player) {
				game.player=newCharacter(CHAR_PLAYER,&tileset[0],(i%width)*TILE_WIDTH,(i/width)*TILE_HEIGHT);
			}
		}
		if(game.map[i].tile->type==MAP_ITEM) {
			game.itemCount++;
		}
	}

	// Now generate the first character for the player.
	if(!game.player) {
		game.player=newCharacter(CHAR_PLAYER,&tileset[0],TILE_WIDTH,TILE_HEIGHT);
	}
	game.player->next=game.firstCharacter;
	game.firstCharacter=game.player;
	
	// populate the level with some objectives.
	populateKeys();
	populateItems(MAPF_CANDY);
	populateItems(MAPF_BOTTLE);
	populateItems(MAPF_SPECIAL);
	game.level=level;
	
	if(game.mode==GAME_PLAY) insertMessage("Find the exit",tileset+18,0x80<<24,2000);
	
	return 0;
}

void setMode(enum GameMode mode)
{
	if(game.mode==GAME_SUBWAY) {
		freeItems();
	} else if(game.mode==GAME_PLAYINTRO) {
		freeCells();
		freeWavs();
	}

	game.menuitem=0;
	game.mode=mode;
	game.menuTimer=0;
	game.name[0]=0;
	drawMode=DM_NORMAL;
	
	if(mode==GAME_PLAY) {
		printf("requesting sound\n");
		playSfx(S_LEVELSTART);
		newGame();
	} else if(mode==GAME_SUBWAY) {
		drawMode=DM_SUBWAY;
#ifdef _PSP
		guStart();
#endif		
		initItems();
#ifdef _PSP
		sceGuFinish();
		sceGuSync(0,0);
#endif
		loadItems();
	} else if(mode==GAME_PLAYINTRO) {
		drawMode=DM_INTRO;
		initIntro();
		updateIntro(0);
	}
}

int newhigh()
{
	int i;
	
	for(i=0;i<8;i++) {
		if(game.highscore[i].score<game.score) return 1;
	}
	return 0;
}

void saveHighScore()
{
	int pos;
	int i;
	
	for(pos=0;pos<8;pos++) {
		if(game.highscore[pos].score<game.score) break;
	}
	
	for(i=8;i>pos;i--) {
		game.highscore[i]=game.highscore[i-1];
	}
	game.highscore[pos].score=game.score;
	game.highscore[pos].level=game.level;
	memset(game.highscore[pos].name,0,16);
	strcpy(game.highscore[pos].name,game.name);
	FILE *file=fopen("data/highscore.bin","wb");
	if(file) {
		fwrite(game.highscore,sizeof(game.highscore),1,file);
		fclose(file);
	}
}
