
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include "bomber.h"
#include "draw.h"
#include "game.h"
#include "gfx.h"
#include "utils.h"

#ifndef DATADIR
#define DATADIR "data"
#endif

#define NUMCHARACTERS 50

static figure font[NUMCHARACTERS];
static figure bigfont[NUMCHARACTERS];

gfxset gfxsets[NUMGFX];
static char walkingname[256];
static char colorsetname[256];
static char backgroundname[256];
static char blocksname[256];
static char blocksxname[256];
static char bombs1name[256];
static char bombs2name[256];
static char flamesname[256];
static char tilesname[256];
static char deathname[256];
static char fontname[256];
static char bigfontname[256];

figure blocks[3];
figure blocksx[9];
figure bombs1[MAXSETS][NUMBOMBFRAMES];
figure bombs2[MAXSETS][NUMBOMBFRAMES];
figure flamefigs[MAXSETS][NUMFLAMEFRAMES];
figure tiles[15];
figure death[NUMDEATHFRAMES];

int fontxsize, fontysize;
int bigfontxsize, bigfontysize, bigfontyspace;

static int textx, texty;

static const unsigned char* remapstring = (const unsigned char*) "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.:!?\177/\\*-,>< =";
static char asciiremap[256];

/* On screen array variables */
int arraynumx = 15;
int arraynumy = 11;
int arraystartx = 20;
int arraystarty = 70;
int arrayspacex = 40;
int arrayspacey = 36;

static sprite sprites[MAXSPRITES];
static int spritesused = 0;

#define IBUFFLEN 1024
int ileft = 0, ihand = 0, byteswide;
unsigned char ibuff[IBUFFLEN], *itake;

static void freegfxset(gfxset* gs) {
	if (gs->gs_pic) free(gs->gs_pic);
	gs->gs_pic = 0;
}

static int myci() {
	if (!ileft) {
		ileft = read(ihand, ibuff, IBUFFLEN);

		if (!ileft) return -1;
		itake = ibuff;
	}
	ileft--;
	return *itake++;
}

static int dopcxreal(char* name, gfxset* gs) {
	int xs, ys;
	int i, j, k;
	int totalsize;
	int width, height;
	unsigned char *bm, *lp;
	char tname[256];

	memset(gs, 0, sizeof(gfxset));
	ileft = 0;
	snprintf(tname, sizeof(tname), DATADIR "/%s", name);
	ihand = open(tname, O_RDONLY);
	if (ihand < 0) {
		char tname2[260];
		snprintf(tname2, sizeof(tname2), "%s.pcx", tname);
		ihand = open(tname2, O_RDONLY);
		if (ihand < 0) return 1;
	}
	if (myci() != 10) {
		close(ihand);
		return 2;
	} // 10=zsoft .pcx
	if (myci() != 5) {
		close(ihand);
		return 3;
	} // version 3.0
	if (myci() != 1) {
		close(ihand);
		return 4;
	} // encoding method
	if (myci() != 8) {
		close(ihand);
		return 5;
	} // bpp
	xs = myci();
	xs |= myci() << 8;
	ys = myci();
	ys |= myci() << 8;
	width = myci();
	width |= myci() << 8;
	height = myci();
	height |= myci() << 8;
	width = width + 1 - xs;
	height = height + 1 - ys;
	for (i = 0; i < 48 + 4; ++i) myci();
	myci();
	if (myci() != 1) {
		close(ihand);
		return 6;
	} // # of planes
	byteswide = myci();
	byteswide |= myci() << 8;
	i = myci();
	i |= myci() << 8;
	//	if(i!=1) {close(ihand);return 7;} // 1=color/bw,2=grey
	for (i = 0; i < 58; ++i) myci();
	totalsize = height * byteswide;
	bm = malloc(totalsize + 1);
	if (!bm) {
		close(ihand);
		return 8;
	} // no memory
	gs->gs_pic = bm;
	gs->gs_xsize = width;
	gs->gs_ysize = height;
	while (height--) {
		lp = bm;
		i = byteswide;
		while (i > 0) {
			j = myci();
			if (j < 0xc0) {
				*lp++ = j;
				--i;
			} else {
				j &= 0x3f;
				k = myci();
				while (j-- && i) {
					*lp++ = k;
					--i;
				}
			}
		}
		bm += width;
	}
	lseek(ihand, -0x300, SEEK_END);
	if (read(ihand, gs->gs_colormap, 0x300) != 0x300) {
		close(ihand);
		return 9;
	}
	close(ihand);
	return 0;
}

static int dopcx(char* name, gfxset* gs) {
	int err;

	err = dopcxreal(name, gs);
	if (err) printf("Error loading \"%s\":code %d\n", name, err);
	return err;
}

static void getgroup(char* name, gfxset* colorgs, figure* fig, int count) {
	int err;
	int i;
	gfxset gs;

	err = dopcx(name, &gs);
	if (err) exit(1000 + err);
	createinout(&gs);
	for (i = 0; i < MAXSETS; ++i, fig += count, ++colorgs) {
		if (!colorgs->gs_pic) continue;
		memmove(gs.gs_colormap, colorgs->gs_colormap, sizeof(gs.gs_colormap));
		createinout(&gs);
		gfxfetch(&gs, fig, count);
	}
	freegfxset(&gs);
}

static void getsingle(char* name, figure* fig, int count) {
	gfxset gs;
	int err;

	err = dopcx(name, &gs);
	if (err) exit(1000 + err);
	createinout(&gs);
	gfxfetch(&gs, fig, count);
	freegfxset(&gs);
}

static void texthome(void) {
	textx = texty = 10;
}

void drawstring(int xpos, int ypos, const char* str) {
	char ch;

	while ((ch = *str++)) {
		drawfigure(xpos, ypos, font + asciiremap[toupper(ch)]);
		xpos += fontxsize;
	}
}

void drawbigstring(int xpos, int ypos, const char* str) {
	char ch;

	while ('\0' != (ch = *str++)) {
		drawfigure(xpos, ypos, bigfont + asciiremap[toupper(ch)]);
		xpos += bigfontxsize;
	}
}

void centerbig(int y, const char* str) {
	int w;

	w = strlen(str) * bigfontxsize;
	drawbigstring((IXSIZE - w) >> 1, y, str);
}

// static void scrprintf(char *str,...) {
// 	char output[256],*p,*p2;
// 	va_list ap;
//
// 	va_start(ap, str);
//
// 	vsprintf(output,str,ap);
// 	p=output;
// 	for(;;) {
// 		p2=p;
// 		while(*p2 && *p2!='\n') ++p2;
// 		if(*p2) {
// 			*p2=0;
// 			drawstring(textx,texty,p);
// 			texty+=fontysize;
// 			textx=10;
// 			p=p2+1;
// 		} else {
// 			drawstring(textx,texty,p);
// 			textx+=fontxsize*(p2-p);
// 			break;
// 		}
// 	}
// 	copyup();
// }

static void bigscrprintf(char* str, ...) {
	char output[256], *p, *p2;
	va_list ap;

	va_start(ap, str);
	vsnprintf(output, sizeof(output), str, ap);
	p = output;
	for (;;) {
		p2 = p;
		while (*p2 && *p2 != '\n') ++p2;
		if (*p2) {
			*p2 = 0;
			drawbigstring(textx, texty, p);
			texty += bigfontysize;
			textx = 10;
			p = p2 + 1;
		} else {
			drawbigstring(textx, texty, p);
			textx += bigfontxsize * (p2 - p);
			break;
		}
	}
	copyup();
}

void addsprite(int x, int y, figure* fig) {
	sprite* sp;

	if (spritesused == MAXSPRITES) return;
	sp = sprites + spritesused++;
	sp->flags = 0;
	sp->xpos = x;
	sp->ypos = y;
	sp->fig = fig;
}

void plotsprites(void) {
	int i;
	sprite* sp;

	sp = sprites;
	for (i = 0; i < spritesused; ++i) {
		drawfigure(sp->xpos, sp->ypos, sp->fig);
		++sp;
	}
}

void erasesprites(void) {
	int i;
	sprite* sp;
	figure* fig;

	sp = sprites;
	for (i = 0; i < spritesused; ++i) {
		fig = sp->fig;

		solidcopy(&background, sp->xpos + fig->xdelta, sp->ypos + fig->ydelta, fig->xsize, fig->ysize);
		++sp;
	}
}

void clearsprites(void) {
	int i;
	sprite* sp;
	figure* fig;

	sp = sprites;
	for (i = 0; i < spritesused; ++i) {
		fig = sp->fig;

		clearrect(sp->xpos + fig->xdelta, sp->ypos + fig->ydelta, fig->xsize, fig->ysize);
		++sp;
	}
}

void clearspritelist(void) {
	spritesused = 0;
}

int tovideox(int x) {
	return (x >> FRACTION) + arraystartx;
}

int tovideoy(int y) {
	return (y >> FRACTION) + arraystarty;
}

int screentoarrayx(int x) {
	x += arrayspacex << (FRACTION + 2);
	return ((x >> FRACTION) + (arrayspacex >> 1)) / arrayspacex - 4;
}

int screentoarrayy(int y) {
	y += arrayspacey << (FRACTION + 2);
	return ((y >> FRACTION) + (arrayspacey >> 1)) / arrayspacey - 4;
}

int arraytoscreenx(int x) {
	return arrayspacex * x << FRACTION;
}

int arraytoscreeny(int y) {
	return arrayspacey * y << FRACTION;
}

static void loadfonts(void) {
	int i, j;
	const unsigned char* p;

	getsingle(fontname, font, NUMCHARACTERS);
	getsingle(bigfontname, bigfont, NUMCHARACTERS);
	fontxsize = 8;
	fontysize = 12;
	bigfontxsize = 16;
	bigfontysize = 24;
	bigfontyspace = 32;
	p = remapstring;
	j = 0;
	while (*p && *p != ' ') ++p, ++j;
	memset(asciiremap, j, sizeof(asciiremap));
	p = remapstring;
	i = 0;
	while (*p && i < NUMCHARACTERS) asciiremap[(int) *p++] = i++;
}

void loadgfx() {
	gfxset* gs;
	gfxset* colorgs;
	int err;
	int i;
	char name[267];

	strcpy(walkingname, "walk");
	strcpy(colorsetname, "pal");
	strcpy(backgroundname, "field0");
	strcpy(blocksname, "blocks3");
	strcpy(blocksxname, "blocks3x");
	strcpy(bombs1name, "bomb1");
	strcpy(bombs2name, "bomb2");
	strcpy(flamesname, "flames");
	strcpy(tilesname, "tiles");
	strcpy(deathname, "death1");
	strcpy(fontname, "font");
	strcpy(bigfontname, "bigfont");

	gs = malloc((MAXSETS + 1) * sizeof(gfxset));
	if (!gs) nomem("loadgfx");
	colorgs = gs + 1;

	for (i = 0; i < MAXSETS; ++i) {
		snprintf(name, sizeof(name), "%s%d", colorsetname, i);
		err = dopcx(name, colorgs + i);
		if (err) continue;
	}

	loadfonts();
	texthome();
	bigscrprintf("Loading graphics...\n");

	err = dopcx(backgroundname, gs);
	if (err) exit(1000 + err);
	createinout(gs);
	solidfetch(gs, &background);
	solidfetch(gs, &backgroundoriginal);
	freegfxset(gs);

	bigscrprintf("Loading blocks\n");
	getsingle(blocksname, blocks, 3);
	bigscrprintf("Loading block explosions\n");
	getsingle(blocksxname, blocksx, 9);
	bigscrprintf("Loading walking figures\n");
	getgroup(walkingname, colorgs, walking[0], NUMWALKFRAMES);
	bigscrprintf("Loading normal bombs\n");
	getgroup(bombs1name, colorgs, bombs1[0], NUMBOMBFRAMES);
	bigscrprintf("Loading controlled bombs\n");
	getgroup(bombs2name, colorgs, bombs2[0], NUMBOMBFRAMES);
	bigscrprintf("Loading flames\n");
	//	getgroup(flamesname,colorgs,flamefigs[0],NUMFLAMEFRAMES);
	getsingle(flamesname, flamefigs[0], NUMFLAMEFRAMES);
	bigscrprintf("Loading bonus tiles\n");
	getsingle(tilesname, tiles, 15);
	bigscrprintf("Loading death sequence\n");
	getsingle(deathname, death, NUMDEATHFRAMES);

	for (i = 0; i < MAXSETS; ++i) freegfxset(colorgs + i);
	free(gs);
	bigscrprintf("Done loading graphics\n");
}

void failure(char* str, ...) {
	char output[256];
	va_list ap;
	int len;
	long now;

	va_start(ap, str);

	len = vsnprintf(output, sizeof(output), str, ap);
	if (len >= 256) len = 255; /* truncated string */
	clear();
	drawbigstring((IXSIZE - len * bigfontxsize) / 2, (IYSIZE - bigfontysize) / 2, output);
	copyup();

	now = longtime();
	while (!exitflag && longtime() - now < 3) {
		scaninput();
		if (anydown()) {
			takedown();
			return;
		}
	}
}
