#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>

#include "game.h"
#include "subway.h"

/* The screen surface */
SDL_Surface *screen = NULL;
SDL_Surface *spritesheet = NULL;

Mix_Chunk *sfx[MAX_SFX];
Mix_Music *music[MAX_MUSIC];

#if USE_TTF_FONT
TTF_Font *font[6];
unsigned int fgcolor[]={ 0xcfcfff, 0xcfcfff, 0xcfcfff, 0x00ff80, 0xffff80};
#endif

const char *sfxfilename[]={"data/locked.wav","data/opening.wav",
	"data/drink.wav","data/special.wav","data/gameover.wav",
	"data/donelevel.wav","data/readygo.wav" };
const char *musicfilename[]={ "data/intro.mod", "data/even.it", "data/odd.it",
	"data/menu.it" };

void playSfx(enum Sfx id)
{
	Mix_PlayChannel(1,sfx[id],0);
}

#define Color unsigned long
/* tga header */
typedef struct
{
  unsigned char id_length;          /* size of image id */
  unsigned char colormap_type;      /* 1 is has a colormap */
  unsigned char image_type;         /* compression type */

  short	cm_first_entry;       /* colormap origin */
  short	cm_length;            /* colormap length */
  unsigned char cm_size;            /* colormap size */

  short	x_origin;             /* bottom left x coord origin */
  short	y_origin;             /* bottom left y coord origin */

  short	width;                /* picture width (in pixels) */
  short	height;               /* picture height (in pixels) */

  unsigned char pixel_depth;        /* bits per pixel: 8, 16, 24 or 32 */
  unsigned char image_descriptor;   /* 24 bits = 0x00; 32 bits = 0x80 */

} tga_header_t;

void saveImage(const char* filename, Color* data, int width, int height, int lineSize, int saveAlpha)
{
	FILE* fp;
	int x, y;
	tga_header_t head;
	
	if ((fp = fopen(filename, "wb")) == NULL) return;

	// Put the header.
	head.id_length=0;
	fwrite(&head.id_length,1,1,fp);
	head.colormap_type=0;
	fwrite(&head.colormap_type,1,1,fp);
	head.image_type=2;
	fwrite(&head.image_type,1,1,fp);
	head.cm_first_entry=0;
	fwrite(&head.cm_first_entry,2,1,fp);
	head.cm_length=0;
	fwrite(&head.cm_length,2,1,fp);
	head.cm_size=0;
	fwrite(&head.cm_size,1,1,fp);
	head.x_origin=0;
	fwrite(&head.x_origin,2,1,fp);
	head.y_origin=0;
	fwrite(&head.y_origin,2,1,fp);
	head.width=width;
	fwrite(&head.width,2,1,fp);
	head.height=height;
	fwrite(&head.height,2,1,fp);
	head.pixel_depth=24;
	fwrite(&head.pixel_depth,1,1,fp);
	head.image_descriptor=0x80;
	fwrite(&head.image_descriptor,1,1,fp);
	
	for(y=height-1;y>=0;y--) {
		for(x=0;x<width;x++) {
			fwrite((char *)(data+y*lineSize+x)+2,1,1,fp);
			fwrite((char *)(data+y*lineSize+x)+1,1,1,fp);
			fwrite((char *)(data+y*lineSize+x)+0,1,1,fp);
		}
	}
	
	fclose(fp);
}


#if USE_TTF_FONT
void extentMessage(int *w,int *h, enum FontId fontId, const char *message)
{
	if(!font[fontId]) {
		*w=0;
		*h=0;
		return;
	}
	TTF_SizeText(font[fontId],message,w,h);
}

void drawMessage(int x,int y, enum FontId fontId, const char *message)
{
	SDL_Surface *surf;
	SDL_Rect rect;
	SDL_Color fg={(fgcolor[fontId]>>16)&255,(fgcolor[fontId]>>8)&255,fgcolor[fontId]&255,255};
	
	if(!font[fontId]) {
		printf("message at %d,%d: '%s'\n",x,y,message);
		return;
	}
	surf=TTF_RenderText_Solid(font[fontId],message,fg);
	rect.x=x;
	rect.y=y;
	rect.w=0;
	rect.h=0;
	SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0);
	SDL_BlitSurface(surf, NULL, screen, &rect);
	SDL_FreeSurface(surf);
}
#else
struct FastFont {
	SDL_Surface *texture;
	int height;
	struct Glyph {
		int active;
		int x,y,w,h;
		int kern_left,kern_right,kern_bottom;
	} glyph[256];
} fastFont[6];

int loadFastFont(struct FastFont *font,const char *filename)
{
	font->height=1;
	printf("Loading font '%s'\n",filename);
	if(font->texture) return 1;	// already loaded.
	FILE *file=fopen(filename,"r");
	if(!file) {
		font->texture=0;
		return 0;
	}
	font->texture=0;
	char line[256];
	line[255]=0;
	while( fgets(line,255,file) ) {
		if(strncmp(line,"Char=",5)==0) {
			int ch=0;
			int x=0,y=0,w=0,h=0,kern_left=0,kern_right=0,kern_bottom=0;
			// Char="z"; or Char=0020;
			char c=0;
			int res=sscanf(line,"Char=\"%c\"; %d,%d,%d,%d; %d,%d,%d;",&c,&x,&y,&w,&h,&kern_left,&kern_right,&kern_bottom);
			ch=c;
			if(res<5) {
				res=sscanf(line,"Char=%x; %d,%d,%d,%d; %d,%d,%d;",&ch,&x,&y,&w,&h,&kern_left,&kern_right,&kern_bottom);
			}
			if(res>=5) {
				font->glyph[(unsigned int)ch].active=1;
				font->glyph[(unsigned int)ch].x=x;
				font->glyph[(unsigned int)ch].y=y;
				font->glyph[(unsigned int)ch].w=w;
				font->glyph[(unsigned int)ch].h=h;
				font->glyph[(unsigned int)ch].kern_left=kern_left;
				font->glyph[(unsigned int)ch].kern_right=kern_right;
				font->glyph[(unsigned int)ch].kern_bottom=kern_bottom;
			}
		} else if(strncmp(line,"Height=",7)==0) {
			font->height=1;
			sscanf(line,"Height=%d",&font->height);
		} else if(strncmp(line,"Bitmap=",7)==0) {
			char imagePath[256];
			if(strstr(line,"\n")) strstr(line,"\n")[0]=0;
			if(strstr(line,"\r")) strstr(line,"\r")[0]=0;
			strcpy(imagePath,line+7);
			font->texture=IMG_Load(imagePath);
			if(!font->texture) {
				strcpy(imagePath,"data/");
				strcat(imagePath,line+7);
				font->texture=IMG_Load(imagePath);
			}
			if(font->texture) printf("Loaded %s\n",imagePath);
			else printf("Could not find '%s'\n",imagePath);
		}
	}
	if(!font->texture) return 0;
//	printf("Success for font '%s'\n",filename);
	return 1;
}

void extentMessage(int *w,int *h, enum FontId fontId, const char *message)
{
	struct FastFont *font=&fastFont[fontId];
	unsigned int i;
	if(!font->texture) return;
	*w=0;
	for(i=0;i<strlen(message);i++) {
		int ch=(unsigned char)message[i];
		if(font->glyph[ch].active) {
			*w+=font->glyph[ch].w+font->glyph[ch].kern_right+1;
		} else {
			printf("Missing font char: '%c'\n",(char)ch);
			*w+=font->height;
		}
	}
	*h=fastFont[fontId].height;
}

void drawGlyph(int sx,int sy,int width,int height,SDL_Surface *source,int dx,int dy)
{
	SDL_Rect rect;
	SDL_Rect tileRect;
	
	if(!source) return;
	
	// Screen target
	rect.x=dx;
	rect.y=dy;
	rect.w=width;
	rect.h=height;
	// Sprite sheet source
	tileRect.x=sx;
	tileRect.y=sy;
	tileRect.w=width;
	tileRect.h=height;
	SDL_BlitSurface(source,&tileRect,screen,&rect);	
}

void drawMessage(int x,int y, enum FontId fontId, const char *message)
{
	struct FastFont *font=&fastFont[fontId];
	unsigned int i;
	if(!font->texture) return;
	//printf("Message '%s'\n",message);
	for(i=0;i<strlen(message);i++) {
		int ch=(unsigned char)message[i];
		if(font->glyph[ch].active) {
			drawGlyph(font->glyph[ch].x,
				font->glyph[ch].y,font->glyph[ch].w,
				font->glyph[ch].h,font->texture,
				x+font->glyph[ch].kern_left,
				y+font->glyph[ch].kern_bottom);
			x+=font->glyph[ch].w+font->glyph[ch].kern_right+1;
		} else {
			printf("Missing font char: '%c'\n",(char)ch);
			x+=font->height;
		}
	}
}
#endif
	
void drawFilledRect(int x,int y,int width,int height,unsigned int color)
{
        SDL_Rect rect={x,y,width,height};

        SDL_FillRect(screen,&rect,color);
}

void drawTile(int x,int y,struct Tile *tile,int frame)
{
	SDL_Rect rect;
	SDL_Rect tileRect;
	x-=game.viewLeft;
	y-=game.viewTop;
	
	if(!spritesheet) {
		spritesheet=IMG_Load("data/gfx.png");
	}
	if(!spritesheet) return;
	if(!tile) return;
	if(x+tile->w<0 || y+tile->h<0 || x>SCREEN_WIDTH || y>SCREEN_HEIGHT) return;
	
	// Screen target
	rect.x=x;
	rect.y=y;
	rect.w=tile->w;
	rect.h=tile->h;
	// Sprite sheet source
	tileRect.x=tile->x+frame*(tile->w+2);
	tileRect.y=tile->y;
	tileRect.w=tile->w;
	tileRect.h=tile->h;
	SDL_BlitSurface(spritesheet,&tileRect,screen,&rect);
}

struct ImageCache {
	char fname[256];
	SDL_Surface *image;
} cell[32];

int cellNext=0;

void drawCell(int x,int y,const char *fname)
{
	SDL_Rect rect;
	SDL_Surface *image=0;
	int i;
	
	for(i=0;i<cellNext;i++) {
		if(strcmp(cell[i].fname,fname)==0) {
			image=cell[i].image;
		}
	}
	
	if(!image) {
		image=IMG_Load(fname);
		if(!image) {
			char buf[256];
			strcpy(buf,"data/intro/");
			strcat(buf,fname);
			image=IMG_Load(buf);				
		}
		if(image) {
			strcpy(cell[cellNext].fname,fname);
			cell[cellNext++].image=image;
		}
	}
	if(!image) printf("Could not load cell '%s'\n",fname);
	if(!image) return;
	if(x+image->w<0 || y+image->h<0 || x>SCREEN_WIDTH || y>SCREEN_HEIGHT) return;
	
	// Screen target
	rect.x=x;
	rect.y=y;
	rect.w=image->w;
	rect.h=image->h;
	SDL_BlitSurface(image,0,screen,&rect);
}

void freeCells()
{
	int i;
	for(i=0;i<cellNext;i++) {
		SDL_FreeSurface(cell[i].image);
		cell[i].image=0;
		cell[i].fname[0]=0;
	}
	cellNext=0;
}

Mix_Chunk *wav=0;

void playWav(const char *fname)
{
	if(wav) {
		Mix_FreeChunk(wav);
		wav=0;
	}
	wav=Mix_LoadWAV(fname);
	if(!wav) {
		char buf[256];
		strcpy(buf,"data/intro/");
		strcat(buf,fname);
		wav=Mix_LoadWAV(buf);				
	}
	if(!wav) return;
	Mix_PlayChannel(1,wav,0);
}

void freeWavs()
{
	if(wav)	Mix_FreeChunk(wav);
	wav=0;
}

int frameNo=0;

/* This function draws to the screen; replace this with your own code! */
void draw ()
{
	struct Character *next;
	int x,y;
	
    //SDL_Rect rect;
    Uint32 color;

    /* Create a black background */
    color = SDL_MapRGB (screen->format, 0, 0, 0);
    SDL_FillRect (screen, NULL, color);

	switch(drawMode) {
	case DM_NORMAL:
		for(y=0;y<game.mapHeight;y++) {
			for(x=0;x<game.mapWidth;x++) {
				drawMap(x*TILE_WIDTH,y*TILE_HEIGHT,&game.map[y*game.mapWidth+x]);
			}
		}
		for(next=game.firstCharacter;next!=NULL;next=next->next) {
			drawCharacter(next);
		}
		
		// Finally draw the hud.
		drawHud();
		break;
	case DM_INTRO:
		drawIntro();
		break;
	case DM_SUBWAY:
		drawItems();
		break;
	}

	/* Make sure everything is displayed on screen */
	SDL_Flip (screen);
	/* Don't run too fast */
	SDL_Delay (1);
	
#if 0
	char fname[256];
	sprintf(fname,"lastcall%04d.bmp",frameNo++);
	SDL_SaveBMP(screen,fname);
#endif
}

void drawSubwayLoading()
{
	
}

int main (int argc, char *argv[])
{
    int done;
    SDL_Joystick *joy;
    SDL_Surface *title=0;
    int oldTicks,newTicks;
    int i;

    /* Initialize SDL */
    if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0)
    {
        printf ("Couldn't initialize SDL: %s\n", SDL_GetError ());
        exit (1);
    }
    atexit (SDL_Quit);

    /* Set 480x272 32-bits video mode */
    screen = SDL_SetVideoMode (480, 272, 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
    if (screen == NULL)
    {
        printf ("Couldn't set 480x272x32 video mode: %s\n", SDL_GetError ());
        exit (2);
    }
    SDL_WM_SetCaption ("Last Call", NULL);
    
    joy=0;
    if(SDL_NumJoysticks()>0) joy=SDL_JoystickOpen(0);

	title=IMG_Load("data/title.png");
	if(title) {
		SDL_Rect rect;
		rect.x=0;
		rect.y=0;
		rect.w=480;
		rect.h=272;
		SDL_BlitSurface(title,0,screen,&rect);
		
		SDL_Flip(screen);
		SDL_BlitSurface(title,0,screen,&rect);
		
		SDL_Flip(screen);

#ifdef _PSP
//		saveImage("test.tga",(Color *)(0x04000000),480,272,512,0);
#endif
	}
	
//#define TEST_SUBWAY
#ifdef TEST_SUBWAY
	loadItems();
	initItems();
	done=0;
	while(!done) 
	{
		drawItems();
break;
		done=updateItems(18);
	}
	freeItems();
	printf("subway test complete\n");
	return 0;
#endif
	
	// Load in the intro song
	if(Mix_OpenAudio(44100,AUDIO_S16SYS, 2, 2048)<0) {
		// no sound.  oh well.
		printf("Couldn't start sound.\n");
	}
	music[MUS_INTRO]=Mix_LoadMUS(musicfilename[MUS_INTRO]);
	if(music) Mix_PlayMusic(music[MUS_INTRO],1);
	for(i=1;i<MAX_MUSIC;i++) {
		music[i]=Mix_LoadMUS(musicfilename[MUS_INTRO]);
		if(music[i]==NULL) printf("Could not read '%s'\n",musicfilename[i]);
	}
	for(i=0;i<MAX_SFX;i++) {
		sfx[i]=Mix_LoadWAV(sfxfilename[i]);
		if(sfx[i]==NULL) printf("Could not read '%s'\n",sfxfilename[i]);
	}
	
#if 0
	TTF_Init();
	font[FONT_SMALL]=TTF_OpenFont("data/arial.ttf",10);
	font[FONT_HEADLINE]=TTF_OpenFont("data/arial.ttf",32);
	font[FONT_BODY]=TTF_OpenFont("data/arial.ttf",15);
	font[FONT_BODYHIGHLIGHT]=TTF_OpenFont("data/arial.ttf",17);
	font[FONT_MESSAGE]=TTF_OpenFont("data/arial.ttf",16);
#else
	loadFastFont(&fastFont[FONT_HEADLINE],"data/Headline32bluered.fnt");
	loadFastFont(&fastFont[FONT_BODY],"data/Body20blue.fnt");
	loadFastFont(&fastFont[FONT_BODYHIGHLIGHT],"data/Body20brown.fnt");
	loadFastFont(&fastFont[FONT_MESSAGE],"data/Body14blue.fnt");
	loadFastFont(&fastFont[FONT_SMALL],"data/Small10red.fnt");
	loadFastFont(&fastFont[FONT_SMALLHIGHLIGHT],"data/Small10yellow.fnt");
#endif

	spritesheet=IMG_Load("data/gfx.png");

	// Now we're actually ready to play, but maybe we could show a cool intro
	initGame();

	if(title) {
		// Highly coveted fade to black.
#ifndef _PSP
		int alpha;
		for(alpha=0xff;alpha>=0;alpha-=8) {
			SDL_Rect rect;
			rect.x=0;
			rect.y=0;
			rect.w=480;
			rect.h=272;
printf("alpha at %d...\n",alpha);
			SDL_FillRect(screen,&rect,SDL_MapRGB(screen->format,0x00,0x00,0x00));
			SDL_SetAlpha(title,SDL_SRCALPHA,0xff-alpha);
			SDL_BlitSurface(title,0,screen,&rect);
			
			SDL_Flip(screen);
			SDL_Delay(16);	// make it slow!
		}
		SDL_SetAlpha(title,SDL_SRCALPHA,SDL_ALPHA_OPAQUE);
#endif		
	}

	newTicks=SDL_GetTicks();
	initTitle();
	done=0;
	do {
        SDL_Event event;
		if(title) SDL_BlitSurface(title,0,screen,0);

		oldTicks=newTicks;
		newTicks=SDL_GetTicks();

		done=updateDrawTitle(newTicks-oldTicks);
		if(done) break;
		SDL_Flip(screen);
		SDL_Delay(16);	// make it slow!

#if 0
		char fname[256];
		sprintf(fname,"lastcall%04d.bmp",frameNo++);
		SDL_SaveBMP(screen,fname);
#endif
		
        /* Check for events */
        while (SDL_PollEvent (&event))
        {
            switch (event.type)
            {
            case SDL_KEYUP:
            case SDL_JOYBUTTONUP:
                done = 1;
                break;
            case SDL_QUIT:
				return 0;
            default:
                break;
            }
        }

	} while(!done);
	
	if(title) {
		SDL_FreeSurface(title);
		title=0;	
	}
printf("intro anim\n");
	newTicks=SDL_GetTicks();
	initIntro();
	done=0;
	do {
        SDL_Event event;

		oldTicks=newTicks;
		newTicks=SDL_GetTicks();

		drawIntro();
		done=updateIntro(newTicks-oldTicks);
		if(done) break;
		SDL_Flip(screen);
		SDL_Delay(16);	// make it slow!

#if 0
		char fname[256];
		sprintf(fname,"lastcall%04d.bmp",frameNo++);
		SDL_SaveBMP(screen,fname);
#endif
		
        /* Check for events */
        while (SDL_PollEvent (&event))
        {
            switch (event.type)
            {
            case SDL_KEYUP:
            case SDL_JOYBUTTONUP:
                done = 1;
                break;
            case SDL_QUIT:
				return 0;
            default:
                break;
            }
        }

	} while(!done);
	freeCells();
	freeWavs();

printf("event loop\n");
	oldTicks=SDL_GetTicks();
    done = 0;
    while (!done)
    {
        SDL_Event event;
        newTicks=SDL_GetTicks();

        /* Check for events */
        while (SDL_PollEvent (&event))
        {
            switch (event.type)
            {
            case SDL_KEYDOWN:
				done=handleKey(event.key.keysym.sym,0);
                break;
            case SDL_KEYUP:
				done=handleKey(event.key.keysym.sym,1);
                break;
            case SDL_JOYBUTTONDOWN:
				done=handleJoy(event.jbutton.button,0);
				break;
            case SDL_JOYBUTTONUP:
				done=handleJoy(event.jbutton.button,1);
				break;
			case SDL_JOYAXISMOTION:
//				done=handleAnalog(event.jaxis.axis,event.jaxis.value);
				break;
            case SDL_QUIT:
                done = 1;
                break;
            default:
                break;
            }
        }

		update(newTicks-oldTicks);
//if(newTicks!=oldTicks) printf("draw done %.3f frame rate\n",1000.0f/(newTicks-oldTicks));
		oldTicks=newTicks;
		
        /* Draw to screen */
        draw ();
    }
printf("quit requested.\n");

    return 0;
}
