#include <stdlib.h>
#include <math.h>

#include "game.h"

int pathCount;
static unsigned int eventColor=0x80<<24;

void updateMap(long elapsed,struct Map *map,int x,int y)
{
	static int id=0;
	// Update the map timer.
	if(map->tile->flags & TILEF_NPCSPAWNER) {
		map->spawnTimer-=elapsed;
		if(map->spawnTimer<0 && game.characterCount<game.itemCount/4) {
			// Time to spawn a new character
			
			id++;
			if(id==4) id=1;
			struct Character *c=newCharacter(CHAR_NPC,&playerTileDir[id*4],x*TILE_WIDTH,y*TILE_HEIGHT);
			c->next=game.firstCharacter;
			game.firstCharacter=c;
			game.characterCount++;
			map->spawnTimer+=map->tile->spawnDelay;
			if(map->spawnTimer<0) map->spawnTimer=map->tile->spawnDelay;
		}
	}
}

struct Tile *getTile(int x,int y)
{
	if(x<0 || y<0 || x>=game.mapWidth || y>=game.mapHeight) {
		return &tileset[10];	// outer space==top wall.
	}
	return game.map[x+game.mapWidth*y].tile;
}

int walkableTyped(enum CharacterType type,int x,int y)
{
	struct Tile *tile=getTile(x/TILE_WIDTH,y/TILE_HEIGHT);
	if(type==CHAR_POINTS) return 1;
	if(tile && (tile->type==MAP_FLOOR || tile->type==MAP_ITEM)) return 1;
	if(tile && type!=CHAR_PLAYER && tile->type==MAP_DOOR) return 1;
	return 0;
}

void chooseTarget(struct Character *character)
{
	int x,y;
	int items=0;
	int targetitem;

	items=game.itemCount;
	// Now choose a random item as a target.
	targetitem=rand()/(RAND_MAX/items);
	printf("Chose target %d of %d\n",targetitem,items);
	for(y=0;y<game.mapHeight;y++) {
		for(x=0;x<game.mapWidth;x++) {
			// Look for interesting features.
			struct Tile *tile=getTile(x,y);
			if(tile->type==MAP_ITEM) {
				targetitem--;
				if(targetitem==0) {
					character->tx=x*TILE_WIDTH;
					character->ty=y*TILE_HEIGHT;
					return;
				}
			}
		}
	}
}

int getPath(unsigned char *path,int x,int y)
{
	if(x<0 || y<0 || x>=game.mapWidth || y>=game.mapHeight) {
		return 255;
	}
	return path[x+game.mapWidth*y];
}

int dir_x[9]={-1,0,1,1, 1, 0,-1,-1,0};
int dir_y[9]={ 1,1,1,0,-1,-1,-1, 0,0};
	
int buildPath(unsigned char *path,int dist,int fromX,int fromY,int toX,int toY)
{
	// Grow a path -- would dynamic programming be quicker?
	int pos=fromX+fromY*game.mapWidth;
	if(fromX<0 || fromX>=game.mapWidth || fromY<0 || fromY>=game.mapHeight) return 0;
	if( game.map[pos].tile->type!=MAP_FLOOR && game.map[pos].tile->type!=MAP_ITEM && game.map[pos].tile->type!=MAP_DOOR) return 0;
	if(	dist<path[pos]) {
		// Seed
		path[pos]=dist;
	}
	int changed;
	do {
		dist++;
		changed=0;
		int x,y,dir;
		for(y=1;y<game.mapHeight-1;y++) {
			for(x=1;x<game.mapWidth-1;x++) {
				pos=x+y*game.mapWidth;
				if(!walkableTyped(CHAR_NPC,x*TILE_WIDTH,y*TILE_HEIGHT)) continue;
				for(dir=0;dir<8;dir++) {
					if(getPath(path,x+dir_x[dir],y+dir_y[dir])+1<path[pos] && getPath(path,x+dir_x[dir],y+dir_y[dir])+1==dist) {
						path[pos]=getPath(path,x+dir_x[dir],y+dir_y[dir])+1;
						changed++;
					}
				}
				// found a path, so cut out early.
				if(path[pos]<255 && x==toX && y==toY) {
#ifndef _PSP
					printf(">>complete path is %d,%d to %d,%d\n",fromX,fromY,toX,toY);
					for(y=0;y<game.mapHeight;y++) {
						for(x=0;x<game.mapWidth;x++) {
							unsigned char p=path[x+y*game.mapWidth];
							printf("%c",p<96?33+p:' ');
						}
						printf("\n");
					}
#endif
					return 1;
				}
			}
		}
	} while(changed>0);
#ifndef _PSP
printf("incomplete path is %d,%d to %d,%d\n",fromX,fromY,toX,toY);
int x,y;
for(y=0;y<game.mapHeight;y++) {
	for(x=0;x<game.mapWidth;x++) {
		unsigned char p=path[x+y*game.mapWidth];
printf("%c",p<96?33+p:' ');
	}
	printf("\n");
}
#endif
	return 0;
}

void choosePath(struct Character *character)
{
	unsigned char *path=0;

	int diffx=character->x+character->tile->cx-character->tx;
	if(diffx<0) diffx=-diffx;
	int diffy=character->y+character->tile->cy-character->ty;
	if(diffy<0) diffy=-diffy;
	if((character->tx==0 && character->ty==0)) {
		// New task
#ifndef _PSP
		printf("Base task for %08x\n",(int)character);
#endif
		chooseTarget(character);
		if(character->path) free(character->path);
		character->path=0;
	}
	if((diffx<=TILE_WIDTH/4 && diffy<=TILE_HEIGHT/4)) {
		if(character->path) free(character->path);
		character->path=0;

		character->tx=0;
		character->ty=0;
		character->dx=0;
		character->dy=0;
		character->walkTimer+=2000;	// Hang our for 2 seconds.
		return;
	}
	// Now build a path there.
	int toX=character->tx/TILE_WIDTH;
	int toY=character->ty/TILE_HEIGHT;
	int fromX=(character->x+character->tile->cx)/TILE_WIDTH;
	int fromY=(character->y+character->tile->cy)/TILE_HEIGHT;
	if(!character->path) {
		if(pathCount>=1) return;
		
		pathCount++;		
		path=(unsigned char *)malloc(game.mapWidth*game.mapHeight);
		memset(path,255,game.mapWidth*game.mapHeight);
		buildPath(path,0,toX,toY,fromX,fromY);
		character->path=path;
	} else {
		path=character->path;	// Use cached path for speed.
	}

	// Now, which way to head to next.
	int basedir=rand()/(RAND_MAX/8);
	int i;
	int newdir=-1;
	int newdist=getPath(path,fromX,fromY);
	for(i=0;i<8;i++) {
		int dir=(i+basedir)%8;
		if(getPath(path,fromX+dir_x[dir],fromY+dir_y[dir])<newdist) {
			newdist=getPath(path,fromX+dir_x[dir],fromY+dir_y[dir]);
			newdir=dir;
		}
	}
	if(newdir==-1) {
		character->tx=0;
		character->ty=0;
		character->dx=0;
		character->dy=0;
#ifndef _PSP
		printf("Dead end for character %08x\n",(int)character);
#endif
		if(character->path) free(character->path);
		character->path=0;
		character->walkTimer+=4000;	// Hang our for 2 seconds.
		return;	// Couldn't figure it out, new target next time.
	}
	int speed=MAXDELTA/2;
	character->dx=speed*dir_x[newdir];
	character->dy=speed*dir_y[newdir];
	character->walkTimer+=2*TILE_WIDTH*16;	// about 2/3 of a second to walk a tile.
	
	//free(path);
}

void updateFacing(struct Character *character)
{
	int id=character->tile->id;
	int base=0;
	if(character->tile->id<0) base=4*((-1-id)/4);
	if(base<0 || base>15) base=0;
	
	// figure out in what quadrant the player is in
	if(character->dy>0 && character->dx>character->dy) character->tile=&playerTileDir[base+TRIGHT];
	else if(character->dy>0 && character->dx<-character->dy) character->tile=&playerTileDir[base+TLEFT];
	else if(character->dy>0) character->tile=&playerTileDir[base+TDOWN];
	else if(character->dy<0 && character->dx>-character->dy) character->tile=&playerTileDir[base+TRIGHT];
	else if(character->dy<0 && character->dx<character->dy) character->tile=&playerTileDir[base+TLEFT];
	else if(character->dy<0) character->tile=&playerTileDir[base+TUP];
	else if(character->dy==0 && character->dx<0) character->tile=&playerTileDir[base+TLEFT];
	else if(character->dy==0 && character->dx>0) character->tile=&playerTileDir[base+TRIGHT];
	// Default: leave what it was.
}

struct Stagger {
	int olddx,olddy;
	float angle;	// how much to offset the direction by.
	float c,s;	// cos and sin of the angle.
	int dx;			// direction x
	int dy;			// direction y
	int rx;			// remainder x component
	int ry;			// remainder y component
	int delay;		// Frames to next stagger direction.
} stagger;

void updateCharacter(long elapsed,struct Character *character)
{
	if(character->type==CHAR_PLAYER) {
		//printf("Stagger: %d of %d\n",elapsed,stagger.delay);
		stagger.delay-=elapsed;
		if(stagger.delay<1) {
			// Choose a new stagger direction.
			float angle=rand();
			angle=angle/(float)RAND_MAX-0.5f;	// 30 degrees, or +/-0.5 radians
			stagger.angle=angle*1.5f;	// 45 degrees :-) -- should be a drunkness factor.
			//printf("Angle %.3f\n",stagger.angle);
			stagger.c=cosf(stagger.angle);
			stagger.s=sinf(stagger.angle);
			stagger.olddx=0;
			stagger.olddy=0;
			stagger.delay+=333;	// 1/3 of a second, approx.
		}
		
		if(game.player->dx!=stagger.olddx || game.player->dy!=stagger.olddy) {
			// Update the stagger direction.
			float dx,dy;
			stagger.olddx=character->dx;
			stagger.olddy=character->dy;
			stagger.dx=character->dx;
			stagger.dy=character->dy;
			// Rotate the stagger angle.
			dx=stagger.dx*stagger.c+stagger.dy*stagger.s;
			dy=stagger.dx*-stagger.s+stagger.dy*stagger.c;
			stagger.dx=dx;
			stagger.dy=dy;
		}
	}
	
	// Update the character
	if(character->type==CHAR_PLAYER && game.mode==GAME_PLAY)
		character->fx+=stagger.dx*elapsed/16;		// roughly 1 pixel ever 60th of a second
	else
		character->fx+=character->dx*elapsed/16;		// roughly 1 pixel ever 60th of a second
	while(character->fx>MAXDELTA) {
		character->fx-=MAXDELTA;
		if(game.mode!=GAME_PLAY || walkableTyped(character->type,character->x+character->tile->cx+1,character->y+character->tile->cy))
			character->x++;
	}
	while(character->fx<-MAXDELTA) {
		character->fx+=MAXDELTA;
		if(game.mode!=GAME_PLAY || walkableTyped(character->type,character->x+character->tile->cx-1,character->y+character->tile->cy))
			character->x--;
	}
	if(character->type==CHAR_PLAYER && game.mode==GAME_PLAY)
		character->fy+=stagger.dy*elapsed/16;		// roughly 1 MAXDELTA ever 60th of a second
	else
		character->fy+=character->dy*elapsed/16;		// roughly 1 MAXDELTA every 60th of a second
	while(character->fy>MAXDELTA) {
		character->fy-=MAXDELTA;
		if(game.mode!=GAME_PLAY || walkableTyped(character->type,character->x+character->tile->cx,character->y+character->tile->cy+1))
			character->y++;
	}
	while(character->fy<-MAXDELTA) {
		character->fy+=MAXDELTA;
		if(game.mode!=GAME_PLAY || walkableTyped(character->type,character->x+character->tile->cx,character->y+character->tile->cy-1))
			character->y--;
	}
	if(character->type==CHAR_NPC) {
		// use walk timer.
		character->walkTimer-=elapsed;
		if(character->walkTimer<0) {
			choosePath(character);
			updateFacing(character);
		}
	}
	// Animation:
#if 1
	if(character->dx!=0 || character->dy!=0) {
		character->animatedTimer-=elapsed;
		if(character->animatedTimer<0) {
			character->animatedTimer+=character->tile->animDelay;
			if(character->animatedTimer<0) character->animatedTimer=character->tile->animDelay;
			character->frame=(character->frame+1)%(character->tile->frames*(character->tile->flags&TILEF_OSCILLATE)?2:1);
		}
	}
#endif
}


struct Message *insertMessage(const char *text,struct Tile *tile,unsigned long bgColor,long duration)
{
	int w,h;
	
	struct Message *m=(struct Message *)calloc(sizeof(struct Message),1);
	struct Message *last=game.firstMessage;
	int miny=252;
	int maxy=0;
	for(last=game.firstMessage;last;last=last->next) {
		if(last->y<miny) miny=last->y;
		if(last->y+last->h>maxy) maxy=last->y+last->h;
	}
	m->tile=tile;
	m->message=strdup(text);
	m->next=game.firstMessage;
	game.firstMessage=m;
	extentMessage(&w,&h, FONT_MESSAGE, text);
	// if(tile->w+w>480) split into multi line text.
	w+=6;
	if(tile) w+=tile->w;	// Small border around text.
	h+=4;	// Small border around text.
	m->w=w;
	m->h=h;
	if(tile && h<tile->h+4) m->h=tile->h+4;
	// Centre it horizontally.
	m->x=(480-w)/2;
	// Now if it doesn't fit at the bottom, put it up high.
	if(game.firstMessage==0) {	// || maxy<252-h-2) {
		m->y=252-m->h-2;
	} else {
		m->y=miny-m->h-2;
	}
	m->mx=2;
	if(tile) m->mx+=tile->w+2;
	m->my=2;
	if(m->h>h) m->my=2+(m->h-h)/2;
	m->timeToLiveTimer=50*strlen(text)+duration;
	m->teletypeDelay=50;
	m->color=bgColor;
	return m;
}

// Returns: 1 of message valid, and 0 if it is expired.
int updateMessage(struct Message *m,long elapsed)
{
	m->animatedTimer-=elapsed;
	if(m->tile && m->animatedTimer<0) {
		m->animatedTimer+=m->tile->animDelay;
		if(m->animatedTimer<0) m->animatedTimer=m->tile->animDelay;
		m->frame=(m->frame+1)%(m->tile->frames*(m->tile->flags&TILEF_OSCILLATE)?2:1);
	}
	if(m->teletypePos<strlen(m->message)) {
		m->teletypeTimer-=elapsed;
		if(m->teletypeTimer<0) {
			m->teletypeTimer+=m->teletypeDelay;
			m->teletypePos++;
		}
	}
	m->timeToLiveTimer-=elapsed;	
	
	return m->timeToLiveTimer>0;
}

void update(long elapsed)
{
	struct Character *next,**old;
	struct Message *mnext,**mold;
	int x,y;
	
	if(elapsed>1000) return;	// Discard out of range readings

	if(game.mode==GAME_PLAYINTRO) {
		int done=updateIntro(elapsed);
		if(done) setMode(GAME_INTRO);
		return;
	} else if(game.mode==GAME_SUBWAY) {
		int done=updateItems(elapsed);
		if(done) setMode(GAME_INTRO);
		return;
	}

	pathCount=0;
	game.menuTimer+=elapsed;

	if(game.mode!=GAME_RULESPAUSE && game.mode!=GAME_PAUSE) {	
		// Update the map tile animations
		for(y=0;y<game.mapHeight;y++) {
			for(x=0;x<game.mapWidth;x++) {
				updateMap(elapsed,&game.map[y*game.mapWidth+x],x,y);
			}
		}
	}
	if(game.mode!=GAME_RULESPAUSE && game.mode!=GAME_PAUSE) {	
		// Update all of the characters, handling collisions with map elements and the player
		for(next=game.firstCharacter,old=&game.firstCharacter;next!=NULL;old=&next->next,next=next->next) {
	updatechar:
			updateCharacter(elapsed,next);
			if(next->type==CHAR_POINTS) {
				int deltax=next->x-next->tx;
				int deltay=next->y-next->ty;
				if(deltax<0) deltax=-deltax;
				if(deltay<0) deltay=-deltay;
				if(deltax<TILE_WIDTH/4 && deltay<TILE_HEIGHT/4) {
					*old=next->next;
					free(next);
					next=*old;
					if(next) goto updatechar; else break;
				}
			}
		}
	}
	
	mold=&game.firstMessage;
	for(mnext=game.firstMessage;mnext!=NULL;mold=&mnext->next,mnext=mnext->next) {
		while(mnext && !updateMessage(mnext,elapsed)) {
			// Delete the old thing
			*mold=mnext->next;
			free(mnext->message);	// Free alert!
			free(mnext);
			mnext=*mold;
		}
		if(!mnext) break;
	}

	if(!game.player) return;	
	// Update the view, based on the location of the player.
	x=game.player->x+game.player->tile->cx;
	y=game.player->y+game.player->tile->cy;
	// Keep player in the middle 1/3 of the screen
	if(x<game.viewLeft+SCREEN_WIDTH/3) game.viewLeft=x-SCREEN_WIDTH/3;
	else if(x>game.viewLeft+2*SCREEN_WIDTH/3) game.viewLeft=x-2*SCREEN_WIDTH/3;
	if(y<game.viewTop+SCREEN_HEIGHT/3) game.viewTop=y-SCREEN_HEIGHT/3;
	else if(y>game.viewTop+2*SCREEN_HEIGHT/3) game.viewTop=y-2*SCREEN_HEIGHT/3;
	// Unless we are at the edge of the map
	if(game.viewLeft+SCREEN_WIDTH>game.mapWidth*TILE_WIDTH) game.viewLeft=game.mapWidth*TILE_WIDTH-SCREEN_WIDTH;
	if(game.viewLeft<0) game.viewLeft=0;
	if(game.viewTop+SCREEN_HEIGHT>game.mapHeight*TILE_HEIGHT) game.viewTop=game.mapHeight*TILE_HEIGHT-SCREEN_HEIGHT;
	if(game.viewTop<0) game.viewTop=0;

	if(game.mode==GAME_PLAY) {	
		game.healthTimer-=elapsed;
		if(game.healthTimer<0) {
			game.healthTimer+=1500;
			game.health++;
			if(game.health==100) {
				playSfx(S_GAMEOVER);
				insertMessage("Game over.  You are a pathetic sober drunk.",tileset+0,eventColor,1000);
				setMode(GAME_SUMMARY);
			}
		}
		if(game.newHealthTimer>0) game.newHealthTimer-=elapsed;
		if(game.newHealthTimer<0) game.newHealthTimer=0;
		if(game.newScoreTimer>0) game.newScoreTimer-=elapsed;
		if(game.newScoreTimer<0) game.newScoreTimer=0;
	}
}

#if 0
#define GRIDFACTOR 12
struct DrinkStatus {
	long drinkTimer;
	struct DrinkParticle *firstParticle;
	struct DrinkParticle *grid[24][24];
} drinkStatus;

void posToGrid(int x,int y,int *gx,int *gy)
{
	*gx=x/GRIDFACTOR;
	*gy=y/GRIDFACTOR;
	if(*gx<0) *gx=0;
	if(*gx>23) *gx=23;
	if(*gy<0) *gy=0;
	if(*gy>23) *gy=23;
}	

// Remove a particle from the grid without freeing it.
void removeGrid(struct DrinkParticle *particle)
{
	// Remove it if it is in the last known grid.
	if(particle->lastgridx<0 || particle->lastgridy<0) return;
	struct DrinkParticle **node=&drinkStatus.grid[particle->lastgridy][particle->lastgridx];
	while(*node!=0) {
		if(*node==particle) {
			*node=particle->gridNext;	// Here is the one to remove.
			particle->gridNext=0;
			return;
		}
	}
	// Hope it worked.
}

void updateGrid(struct DrinkParticle *particle)
{
	int gx,gy;
	posToGrid(particle->x,particle->y,&gx,&gy);
	if(gx!=particle->lastgridx || gy!=particle->lastgridy) {
		removeGrid(particle);
		particle->lastgridx=gx;
		particle->lastgridy=gy;
		// now insertGrid(particle);
		particle->gridNext=drinkStatus.grid[gy][gx];
		drinkStatus.grid[gy][gx]=particle;
	}
}

void updateDrink(long elapsed)
{
	if(	drinkStatus.drinkTimer>0) {
		drinkStatus.drinkTimer-=elapsed;
	} else {	// Emit new drink particle.
		insertDrinkParticle(200,50);
		drinkStatus.drinkTimer+=200;
	}
	// Apply gravity.
	
}

// Only to be used with freeDrink.
void freeParticle(struct DrinkParticle *particle)
{
	removeGrid(particle);
	free(particle);
}

void freeDrink()
{
	struct DrinkParticle *node,*next;
	
	node=drinkStatus.firstParticle;
	while(node) {
		next=node->next;
		freeParticle(node);
		node=next;
	}
}
#endif
