
#include "Sockets.h"

	typedef struct PlayerStruct *Player, PlayerS;

	struct PlayerStruct {
		int id;
		int money;
		int dead;
		int x, y;			// (0 based)
		int positionKnown;	// Have x and y been sent?
		cstring moveIn;		// Currently requested move (NULL if dead)
		char lastMove[30];	// Whatever actually happened.
		File in, out;
		int bid;			// Temp var.
	};

	typedef struct PackageStruct *Package, PackageS;

	struct PackageStruct {
		int id;		// Unique id.
		int x, y;	// current location;	(0 based)
		int xd, yd;	// Destination location.(0 based)
		int weight;
		int state;	// 0 = on ground, 1 = carried, 2 = delivered
	};


#define MaxPlayers  (20)
#define MaxPackages (1000)
#define MaxHomes    (50)

	static int width, height, weight;

	static int homes[MaxHomes][2];	// x, y of each home.
	static int numHomes;

	static PlayerS players[MaxPlayers];
	static int numPlayers, numDead = 0;

	static int numPackages, numDelivered = 0;
	static PackageS packages[MaxPackages];

	static cstring *board;
	static cstring tokens = ".....................~#";

	int debug = 0;

static
int gameOver()
{
	if (numDead >= numPlayers)
		return 1;

	return 0;
}

// Return value only valid until next call.
static
cstring getLine(Player p)
{
	static char inbuf[2048];
	int len;

	if (!fgets(inbuf, 2048, p->in)) {
		printf("Player %d's socket died.\n", p->id);
		p->dead++;
		numDead++;
		return NULL;
	}

	len = strlen(inbuf);

	if (len > 0 && inbuf[len-1] == '\n')
		inbuf[len-1] = '\0';

	if (debug > 1)
		printf("Player%d:[%s]\n", p->id, inbuf);

	return inbuf;
}

static
void putString(Player p, int cr, cstring fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	vfprintf(p->out, fmt, arg);
	if (cr)
		fputc('\n', p->out);
	fflush(p->out);
	va_end(arg);
}

static
void sendBoard(Player p)
{
	int y;

	putString(p, 1, "%d %d", width, height);

	forn(y, height)
		putString(p, 1, "%s", board[y]);
}

static
void sendConfig(Player p)
{
	putString(p, 1, "%d %d %d", p->id, weight, p->money);
}

static
void sendStateOf(Player p1, Player p2)
{
	putString(p1, 0, " #%d", p2->id);

	if (!p2->positionKnown)
		putString(p1, 0, " X %d Y %d", p2->x + 1, p2->y + 1);

	if (p2->lastMove[0])
		putString(p1, 0, p2->lastMove);
}

static
void sendState(Player p)
{
	int pn;

	forn(pn, numPlayers) {
		Player p2 = &players[pn];

		if (p2->dead)
			continue;

		sendStateOf(p, p2);
	}
	putString(p, 1, "");
}

static
void initPlayer(Player p)
{
	cstring s;

	printf("Initting player %d:\n", p->id);

	if (!(s = getLine(p)))
		return;

	if (strcmp(s, "Player")) {
		printf("Player %d did not send 'Player' during init.\n", p->id);
		p->dead++;
		numDead++;
		return;
	}

	sendBoard(p);
	sendConfig(p);
	sendState(p);
}

static
void sendPackage(Player p, Package pk)
{
	putString(p, 0, " %d %d %d %d", pk->id, pk->xd+1, pk->yd+1, pk->weight);
}

static
void sendPackages(Player p)
{
	int pkn;

	forn(pkn, numPackages) {
		Package pk = &packages[pkn];

		if (pk->state)	// Not on the ground.
			continue;

		if (pk->x == p->x && pk->y == p->y)
			sendPackage(p, pk);
	}
	putString(p, 1, "");
}

static
void getMove(Player p)
{
	cstring s;

	if (!(s = getLine(p)))	// Player died?
		return;

	if (p->moveIn)
		free(p->moveIn);

	p->moveIn = crbncpy(s);
}

static
void doMove(Player p)
{
	static cstring moves[5] = { "N", "E", "S", "W", NULL };
	static int deltas[4][2] = { {0,1}, {1,0}, {0,-1}, {-1,0} };

	cstring seps[100];
	int n;

	n = sepn(p->moveIn, seps, 100);

	if (n < 2) {
		printf("Player %d issued bad command (%s).\n", p->id, p->moveIn);
		p->dead++;
		numDead++;
		return;
	}

	if (!strcmp(seps[1], "Move")) {
		int m;
		if (n < 3) {
			printf("Player %d issued bad Move (%s).\n", p->id, p->moveIn);
			p->dead++;
			numDead++;
			return;
		}
		m = lookup(seps[2], moves);
		if (m < 0 || m > 3) {
			printf("Player %d issued bad Move (%s).\n", p->id, p->moveIn);
			p->dead++;
			numDead++;
			return;
		}

		// Not going to emulate robot collisions; too much of a pain.
		p->x += deltas[m][0];
		p->y += deltas[m][1];

		if (p->x < 0 || p->x >= width || p->y < 0 || p->y >= height ||
			board[p->y][p->x] == '#') {
			if (debug > 1)
				printf("Player %d tried to walk through walls.\n", p->id);
			p->x -= deltas[m][0];
			p->y -= deltas[m][1];
			return;
		}

		strcat(p->lastMove, " ");
		strcat(p->lastMove, seps[2]);

		if (board[p->y][p->x] == '~') {
			printf("Player %d drown.\n", p->id);
			p->dead++;
			numDead++;
			return;
		}

		return;
	}
	if (!strcmp(seps[1], "Pick") || !strcmp(seps[1], "Drop")) {
		printf("Pick/Drop not handled yet.\n");
		return;
	}

	printf("Player %d issues invalid command (%s).\n", p->id, seps[1]);
	p->dead++;
	numDead++;
}

static
void doMoves()
{
	int pn;

	forn(pn, numPlayers) {
		Player p = &players[pn];

		if (p->dead)
			continue;

		if (!p->moveIn ||
				sscanf(p->moveIn, "%d", &p->bid) != 1 ||
				!p->bid ||
				absi(p->bid) > p->money) {
			printf("Player %d bid bad (or ran outta dough): [%s]\n",
					p->id, p->moveIn);
			p->dead++;
			numDead++;
			continue;
		}

		p->money -= absi(p->bid);
	}

	while (1) {

		int maxBid;
		Player maxp;

		maxBid = 0;
		maxp   = NULL;

		forn(pn, numPlayers) {
			Player p = &players[pn];

			if (p->dead || !p->bid)	// !p->bid means already moved.
				continue;

			if (p->bid > maxBid) {
				maxBid = p->bid;
				maxp   = p;
			}
		}

		if (!maxp)	// No more players left who haven't moved.
			break;

		// Now maxp is the player who bid most.
		doMove(maxp);
		maxp->bid = 0;	// Tag as already moved.
	}
}

static
void doTurn()
{
	int pn;

	forn(pn, numPlayers) {
		Player p = &players[pn];

		if (p->dead)
			continue;

		sendPackages(p);
		getMove(p);
	}

	doMoves();

	forn(pn, numPlayers) {
		Player p = &players[pn];

		if (p->dead)
			continue;

		sendState(p);
	}

	forn(pn, numPlayers) {
		Player p = &players[pn];

		if (p->dead)
			continue;

		p->lastMove[0] = '\0';
	}
}


loadBoardFromFile(cstring boardFile)
{
	File fl;
	char buf[1024];
	int x, y, yy;

	fl = fopen_read(boardFile);

	if (!(fgets(buf, 1024, fl)))
		ErCreateFatal(666, "EOF reading board file header.");

	if (sscanf(buf, "%d %d", &width, &height) != 2)
		ErCreateFatal(666, "Error reading board size from file.");

	numHomes = 0;

	board = zalloc(cstring, height);
	forn(yy, height) {
		y = height-1-yy;
		board[y] = zalloc(char, width+1);
		if (!(fgets(buf, 1024, fl)))
			ErCreateFatal(666, "EOF reading row of board from file.");
		if (strlen(buf)-1 < width)
			ErCreateFatal(666, "Short row in board file.");
		forn(x, width) {
			board[y][x] = buf[x];
			if (buf[x] == '@') {
				homes[numHomes][0] = x;
				homes[numHomes][1] = y;
				numHomes++;
			}
		}
		board[y][x] = '\0';
	}
}

Main()
{
	int port;
	File infl, outfl;
	int pn, y, x, money, pkn, h;
	cstring boardFile;

	Aparse(argv, NULL,
			"port=%i(Port,20005)", &port,
			"n=%i(NumPlayers,1)", &numPlayers,
			"w=%i(Width,20)", &width,
			"h=%i(Height,15)", &height,
			"p=%i(NumPackages,10)", &numPackages,
			"homes=%i(NumHomes,3)", &numHomes,
			"weight=%i(MaxWeight,100)", &weight,
			"money=%i(InitialMoney,1000)", &money,
			"debug=%i(DebugLevel,1)", &debug,
			"board=%f(BoardFile,-)", &boardFile,
			NULL);

	printf("Initializing board...\n");
	if (boardFile) {
		loadBoardFromFile(boardFile);
	} else {
		board = zalloc(cstring, height);
		forn(y, height) {
			board[y] = zalloc(char, width+1);
			forn(x, width)
				board[y][x] = tokens[ranval2i(0, strlen(tokens))];
			board[y][x] = '\0';
		}

		forn(h, numHomes) {
			homes[h][0] = ranval2i(0, width);
			homes[h][1] = ranval2i(0, height);
			board[homes[h][1]][homes[h][0]] = '@';
		}
	}

	printf("Initializing packages...\n");
	forn(pkn, numPackages) {
		Package pk = &packages[pkn];
		int h;

		h = ranval2i(0, numHomes);

		pk->id     = pkn;
		pk->weight = ranval2i(0, weight);
		pk->x      = homes[h][0];
		pk->y      = homes[h][1];
		pk->state  = 0;

		do {
			pk->xd = ranval2i(0, width);
			pk->yd = ranval2i(0, height);
		} while (board[pk->yd][pk->xd] != '.');
	}


	printf("Initializing players...\n");
	forn(pn, numPlayers) {

		Player p = &players[pn];

		p->id    = pn;
		p->dead  = 0;
		p->money = money;
		p->positionKnown = 0;
		p->moveIn   = NULL;
		p->lastMove[0] = '\0';
		p->bid = 0;
		do {
			p->x = ranval2i(0, width);
			p->y = ranval2i(0, height);
		// Need to check for robot overlap too:
		} while (board[p->y][p->x] != '.');

		printf("Waiting for connection on %d...", port);
		fflush(stdout);
		if (ServerSocketFopen(port, &p->in, &p->out))
			ErCreateFatal(1, "Couldn't open server socket.");
		printf("got it! (player %d)\n", p->id);
	}

	forn(pn, numPlayers)
		initPlayer(&players[pn]);

	forn(pn, numPlayers)
		players[pn].positionKnown++;

	while (!gameOver())
		doTurn();

	// Shutdown..

	forn(pn, numPlayers) {
		Player p = &players[pn];
		fclose(p->in);
		fclose(p->out);
	}
	ServerSocketClose(port);
}


