#include "handler.h"
#include "level.h"
#include "protocol.h"
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include "config.h"
#include "qcanvas.h"
#include "ai_player.h"
#include "item.h"
#include "cell.h"
#include "knet.h"
#include "bomb.h"
#include "exit.h"
#include "blast.h"
#include "rule.h"
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>

using namespace std;

static Handler *o;
static GGZdMod *globalmod;

template <class T>
T at(std::list<T> l, int position)
{
	typename std::list<T>::iterator it;
	for(it = l.begin(); it != l.end() && position; it++, position--);
	return (*it);
}

Handler::Handler()
{
	level = NULL;
	o = this;
	m_state = state_launched;
	net = new KNet();

	exit = NULL;
	rules = NULL;

	selected_map = NULL;
	selected_bots = 0;
}

Handler::~Handler()
{
	if(exit) delete exit;

	if(level) delete level;
	delete net;
}

bool Handler::walkableTile(int x, int y)
{
	list<Bomb*>::iterator bit;
	list<Item*>::iterator iit;
	bool valid = true;
	Item *item;

	if((x >= 0) && (y >= 0) && (x < level->width()) && (y < level->height()))
	{
		if((level->tile(x, y) == Level::wall)
		|| (level->tile(x, y) == Level::invalid)
		|| (level->tile(x, y) == Level::space))
			return false;

		if((level->tile(x, y) == Level::itemBomb)
		|| (level->tile(x, y) == Level::itemLive)
		|| (level->tile(x, y) == Level::itemPower))
		{
			for(iit = itemlist.begin(); iit != itemlist.end(); iit++)
				if(((*iit)->x() == x) && ((*iit)->y() == y))
					if(!(*iit)->isActive())
						return false;
		}

		if(exit)
		{
			if (exit->x() == x && exit->y() == y && !exit->isHighlighted())
				return false;
		}

		if(level->tile(x, y) == Level::itemScree)
		{
			if((item = findItem(x, y)) != NULL)
				return false;
		}

		for(bit = bomblist.begin(); bit != bomblist.end(); bit++)
		{
			if(((*bit)->x() == x) && ((*bit)->y() == y))
				return false;
		}
	}
	else
	{
		return false;
	}

	return valid;
}

Item *Handler::findItem(int x, int y)
{
	list<Item*>::iterator it;

	for(it = itemlist.begin(); it != itemlist.end(); it++)
		if(((*it)->x() == x) && ((*it)->y() == y))
			return (*it);
	return NULL;
}

void Handler::checkItem(Player *p, bool isplayer)
{
	Item *item;

	if((item = findItem(p->x(), p->y())) != NULL)
	{
		switch(item->type())
		{
			case Level::itemBomb:
				p->numBombs(p->numBombs() + 1);
				*net << MNet::multicast;
				*net << Net::begin << opcode_action << action_item << p->id() << Level::itemBomb << 1 << Net::end << Net::flush;
				*net << MNet::peer;
				break;
			case Level::itemLive:
				p->numLives(p->numLives() + 1);
				*net << MNet::multicast;
				*net << Net::begin << opcode_action << action_item << p->id() << Level::itemLive << 1 << Net::end << Net::flush;
				*net << MNet::peer;
				break;
			case Level::itemPower:
				p->power(p->power() + 1);
				*net << MNet::multicast;
				*net << Net::begin << opcode_action << action_item << p->id() << Level::itemPower << 1 << Net::end << Net::flush;
				*net << MNet::peer;
				break;
		}
		itemlist.remove(item);
		level->setTile(p->x(), p->y(), Level::ground);
		freecelllist.push_back(new Cell(p->x(), p->y()));
		// iterate over all players
		*net << MNet::multicast;
		*net << Net::begin << opcode_action << action_removeitem << item->id() << Net::end << Net::flush;
		*net << MNet::peer;
	}

	if(isplayer)
	{
		if((exit) && (exit->isActive()))
		{
			if((p->x() == exit->x()) && (p->y() == exit->y()))
			{
				*net << MNet::multicast;
				*net << Net::begin << opcode_state << state_stop << Net::end << Net::flush;
				*net << MNet::peer;
				// FIXME - erm, what else to do here, if player has won? :)

				*net << Net::begin << opcode_over << 1 << Net::end << Net::flush;
				gameOver(true);
			}
		}
	}
}

void Handler::moveMonsters()
{
	list<AI_Player*>::iterator it;
	int direction;
	bool hasmoved;

	for(it = monsterlist.begin(); it != monsterlist.end(); it++)
	{
		AI_Player *monster = (*it);
		int x = monster->x();
		int y = monster->y();

		if(monster->counter() == 3)
		{
			if(rand() % 2 == 0)
			{
				switch(monster->direction())
				{
					case AI_Player::Up:
						monster->direction(AI_Player::Down);
						break;
					case AI_Player::Down:
						monster->direction(AI_Player::Up);
						break;
					case AI_Player::Left:
						monster->direction(AI_Player::Right);
						break;
					case AI_Player::Right:
						monster->direction(AI_Player::Left);
						break;
				}
			}
		}

		if(monster->counter() == 4)
		{
			if(rand() % 2 == 0)
			{
				switch(monster->direction())
				{
					case AI_Player::Up:
						monster->direction(AI_Player::Left);
						break;
					case AI_Player::Down:
						monster->direction(AI_Player::Right);
						break;
					case AI_Player::Left:
						monster->direction(AI_Player::Down);
						break;
					case AI_Player::Right:
						monster->direction(AI_Player::Up);
						break;
				}
			}
			monster->counter(0);
		}
		else
		{
			monster->counter(monster->counter() + 1);
		}

		hasmoved = false;

		if(monster->direction() == AI_Player::Up)
		{
			if(walkableTile(x + 1, y))
			{
				monster->move(x + 1, y);
				monster->direction(AI_Player::Right);
				hasmoved = true;
			}
			else if(walkableTile(x, y - 1))
			{
				monster->move(x, y - 1);
				monster->direction(AI_Player::Up);
				hasmoved = true;
			}
			else if(walkableTile(x - 1, y))
			{
				monster->move(x - 1, y);
				monster->direction(AI_Player::Left);
				hasmoved = true;
			}
			else if(walkableTile(x, y + 1))
			{
				monster->move(x, y + 1);
				monster->direction(AI_Player::Down);
				hasmoved = true;
			}
		}
		else if(monster->direction() == AI_Player::Down)
		{
			if(walkableTile(x - 1, y))
			{
				monster->move(x - 1, y);
				monster->direction(AI_Player::Left);
				hasmoved = true;
			}
			else if(walkableTile(x, y + 1))
			{
				monster->move(x, y + 1);
				monster->direction(AI_Player::Down);
				hasmoved = true;
			}
			else if(walkableTile(x + 1, y))
			{
				monster->move(x + 1, y);
				monster->direction(AI_Player::Right);
				hasmoved = true;
			}
			else if(walkableTile(x, y - 1))
			{
				monster->move(x, y - 1);
				monster->direction(AI_Player::Up);
				hasmoved = true;
			}
		}
		else if(monster->direction() == AI_Player::Left)
		{
			if(walkableTile(x, y - 1))
			{
				monster->move(x, y - 1);
				monster->direction(AI_Player::Up);
				hasmoved = true;
			}
			else if(walkableTile(x - 1, y))
			{
				monster->move(x - 1, y);
				monster->direction(AI_Player::Left);
				hasmoved = true;
			}
			else if(walkableTile(x, y + 1))
			{
				monster->move(x, y + 1);
				monster->direction(AI_Player::Down);
				hasmoved = true;
			}
			else if(walkableTile(x + 1, y))
			{
				monster->move(x + 1, y);
				monster->direction(AI_Player::Right);
				hasmoved = true;
			}
		}
		else if(monster->direction() == AI_Player::Right)
		{
			if(walkableTile(x, y + 1))
			{
				monster->move(x, y + 1);
				monster->direction(AI_Player::Down);
				hasmoved = true;
			}
			else if(walkableTile(x + 1, y))
			{
				monster->move(x + 1, y);
				monster->direction(AI_Player::Right);
				hasmoved = true;
			}
			else if(walkableTile(x, y - 1))
			{
				monster->move(x, y - 1);
				monster->direction(AI_Player::Up);
				hasmoved = true;
			}
			else if(walkableTile(x - 1, y))
			{
				monster->move(x - 1, y);
				monster->direction(AI_Player::Left);
				hasmoved = true;
			}
		}

		direction = monster->direction();

		if(hasmoved)
		{
			/*if(direction == AI_Player::Up) direction = move_up;
			else if(direction == AI_Player::Down) direction = move_down;
			else if(direction == AI_Player::Left) direction = move_left;
			else if(direction == AI_Player::Right) direction = move_right;*/
			*net << MNet::multicast;
			*net << Net::begin << opcode_move << /*FIXME*//*gen*/0 << move_monster << monster->id() << direction << Net::end << Net::flush;
			*net << MNet::peer;
			checkItem(monster, false);
		}
	}
}

bool Handler::placeBlast(int x, int y)
{
	Blast *blast;

	if((level->tile(x, y) == Level::invalid) || (level->tile(x, y) == Level::wall))
		return false;

	blast = new Blast(generator(), x, y, NULL);
	blastlist.push_back(blast);

	// iterate over all players
	*net << MNet::multicast;
	*net << Net::begin << opcode_action << action_newblast << blast->id() << x << y << Net::end << Net::flush;
	*net << MNet::peer;

	if(level->tile(x, y) > Level::space)
		return false;
	else
		return true;
}

void Handler::createMonster(int id, int x, int y)
{
	AI_Player *monster = new AI_Player(id, x, y, NULL);
	monster->numLives(rules->monster_lives);
	monster->numBombs(rules->monster_bombs);
	monster->power(rules->monster_power);
	monsterlist.push_back(monster);

	//iterate over all players
	*net << MNet::multicast;
	*net << Net::begin << opcode_action << action_newmonster << monster->id() << x << y << Net::end << Net::flush;
	*net << MNet::peer;
}

void Handler::createMonster(int count)
{
	for(int i = 0; i < count; i++)
	{
		int pos = rand () % freecelllist.size();
		Cell *c = at(freecelllist, pos);

		createMonster(generator(), c->x(), c->y());

		freecelllist.remove(c);
	}
}

void Handler::gameOver(bool playerwins)
{
	// if a monster wins, all monsters win, but only those with seats
cout << "winner! " << playerwins << endl;

	GGZGameResult results[ggzdmod_get_num_seats(globalmod)];
	for(int i = 0; i < ggzdmod_get_num_seats(globalmod); i++)
	{
		results[i] = (playerwins ? GGZ_GAME_LOSS : GGZ_GAME_WIN);
	}
	results[0] = (playerwins ? GGZ_GAME_WIN : GGZ_GAME_LOSS);
	ggzdmod_report_game(globalmod, NULL, results, NULL);
}

void Handler::progress()
{
	Item *item;
	list<Blast*>::iterator blit, xblit;
	list<Bomb*>::iterator bit, xbit;
	list<Player*>::iterator pit, xpit;
	list<AI_Player*>::iterator apit, xapit;
	list<Item*>::iterator iit;

	// proceed blasts
	for(blit = blastlist.begin(); blit != blastlist.end(); blit++)
	{
		(*blit)->proceed();

		// check for player, standing in a blast area
		for(pit = playerlist.begin(); pit != playerlist.end(); pit++)
		{
			if(((*pit)->x() == (*blit)->x()) && ((*pit)->y() == (*blit)->y()) && ((*blit)->step() == 1))
			{
cout << "death caused by blast" << endl;
cout << "player: " << (*pit)->id() << " - " << (*pit)->x() << "/" << (*pit)->y() << endl;
cout << "blast: " << (*blit)->x() << "/" << (*blit)->y() << endl;
				(*pit)->numLives((*pit)->numLives() - 1);
cout << "lives left: " << (*pit)->numLives() << endl;
				*net << MNet::multicast;
				*net << Net::begin << opcode_action << action_item << (*pit)->id() << Level::itemLive << -1 << Net::end << Net::flush;
				*net << MNet::peer;
			}
		}

		for(apit = monsterlist.begin(); apit != monsterlist.end(); apit++)
		{
			if(((*apit)->x() == (*blit)->x()) && ((*apit)->y() == (*blit)->y()) && ((*blit)->step() == 1))
			{
cout << "monster death caused by blast" << endl;
				(*apit)->numLives((*apit)->numLives() - 1);
				*net << MNet::multicast;
				*net << Net::begin << opcode_action << action_item << (*apit)->id() << Level::itemLive << -1 << Net::end << Net::flush;
				*net << MNet::peer;
			}
		}

		// check for other bombs, lieing in a blast area
		for(bit = bomblist.begin(); bit != bomblist.end(); bit++)
		{
			if(((*bit)->x() == (*blit)->x()) && ((*bit)->y() == (*blit)->y()))
				(*bit)->setStep(0);
		}

		// when exit is hit, generate a new monster
		if(exit)
		{
			if((exit->x() == (*blit)->x()) && (exit->y() == (*blit)->y()) && ((*blit)->step() == 1))
			{
				if (!exit->isActive())
				{
					exit->activate(true);
					*net << MNet::multicast;
					*net << Net::begin << opcode_action << action_activateexit << Net::end << Net::flush;
					*net << MNet::peer;
				}
				if (!exit->isHighlighted())
					createMonster(generator(), exit->x(), exit->y());
			}
		}

		// check for other items, lieing in a blast area
		if((item = findItem((*blit)->x(), (*blit)->y())) != NULL)
		{
			if((*blit)->step() == 1)
			{
				if (!item->isActive())
				{
					if(item->type() == Level::itemScree)
					{
						level->setTile(item->x(), item->y(), Level::ground);
						*net << MNet::multicast;
						*net << Net::begin << opcode_action << action_removeitem << item->id() << Net::end << Net::flush;
						*net << MNet::peer;
						itemlist.remove(item);
					}
					else
					{
						if(!item->isActive())
						{
							item->activate(true);
							*net << MNet::multicast;
							*net << Net::begin << opcode_action << action_activateitem << item->id() << Net::end << Net::flush;
							*net << MNet::peer;
						}
					}
				}
				else
				{
					*net << MNet::multicast;
					*net << Net::begin << opcode_action << action_removeitem << item->id() << Net::end << Net::flush;
					*net << MNet::peer;
					itemlist.remove(item);
				}
			}
		}

		if((*blit)->finished())
		{
			*net << MNet::multicast;
			*net << Net::begin << opcode_action << action_removeblast << (*blit)->id() << Net::end << Net::flush;
			*net << MNet::peer;
			xblit = blit--;
			blastlist.remove((*xblit));
		}
	}

	// let bombs tick
	for(bit = bomblist.begin(); bit != bomblist.end(); bit++)
	{
		(*bit)->proceed();

		if((*bit)->finished())
		{
			(*bit)->player()->numBombs((*bit)->player()->numBombs() + 1);
			*net << MNet::multicast;
			*net << Net::begin << opcode_action << action_item << (*bit)->player()->id() << Level::itemBomb << 1 << Net::end << Net::flush;
			*net << MNet::peer;

			// create explosion
			bool left, right, top, bottom;

			placeBlast((*bit)->x(), (*bit)->y());

			left = right = top = bottom = true;
			for(int i = 1; i < (*bit)->player()->power() + 1; i++)
			{
				if(right)
					right = placeBlast((*bit)->x() + i, (*bit)->y());
				if(left)
					left = placeBlast((*bit)->x() - i, (*bit)->y());
				if(top)
					top = placeBlast((*bit)->x(), (*bit)->y() + i);
				if(bottom)
					bottom = placeBlast((*bit)->x(), (*bit)->y() - i);
			}

			*net << MNet::multicast;
			*net << Net::begin << opcode_action << action_removebomb << (*bit)->id() << Net::end << Net::flush;
			*net << MNet::peer;
			xbit = bit--;
			bomblist.remove((*xbit));
		}
	}

	// clashes between players and monsters
	for(pit = playerlist.begin(); pit != playerlist.end(); pit++)
	{
		for(apit = monsterlist.begin(); apit != monsterlist.end(); apit++)
		{
			if(((*apit)->x() == (*pit)->x()) && ((*apit)->y() == (*pit)->y()))
			{
cout << "death caused by clash with monster " << (*apit)->id() << endl;
				(*pit)->numLives((*pit)->numLives() - 1);
				*net << MNet::multicast;
				*net << Net::begin << opcode_action << action_item << (*pit)->id() << Level::itemLive << -1 << Net::end << Net::flush;
				*net << MNet::peer;
			}
		}

		/* player died*/
		if((*pit)->numLives() == 0)
		{
cout << "remove fd " << (*pit)->fd() << " completely from mnet" << endl;
			*net << Net::channel << (*pit)->fd();
			*net << Net::begin << opcode_state << state_stop << Net::end << Net::flush;
			*net << Net::begin << opcode_over << 0 << Net::end << Net::flush;
			*net << MNet::remove << (*pit)->fd();
			// FIXME - erm, what else to do here? :)
			xpit = pit--;
			playerlist.remove((*xpit));

			gameOver(false);
		}
	}

	// check for monster deaths
	for(apit = monsterlist.begin(); apit != monsterlist.end(); apit++)
	{
		if((*apit)->numLives() == 0)
		{
			*net << MNet::multicast;
			*net << Net::begin << opcode_action << action_removemonster << (*apit)->id() << Net::end << Net::flush;
			*net << MNet::peer;
			xapit = apit--;
			monsterlist.remove((*xapit));
		}
	}

	// no more monsters, level is finished
	if(monsterlist.size() == 0)
	{
		if(exit)
		{
			if(!exit->isHighlighted())
			{
				exit->setHighlighted(true);
				*net << MNet::multicast;
				*net << Net::begin << opcode_action << action_highlightexit << Net::end << Net::flush;
				*net << MNet::peer;
			}
		}
		for(iit = itemlist.begin(); iit != itemlist.end(); iit++)
		{
			if((*iit)->type() >= Level::itemBomb)
			{
				if(!(*iit)->isHighlighted())
				{
					(*iit)->setHighlighted(true);
					*net << MNet::multicast;
					*net << Net::begin << opcode_action << action_highlightitem << (*iit)->id() << Net::end << Net::flush;
					*net << MNet::peer;
				}
			}
		}
	}

	// proceed items
	for(iit = itemlist.begin(); iit != itemlist.end(); iit++)
		(*iit)->proceed();
}

void Handler::checkStart(GGZdMod *mod)
{
//	int fd;

	cout << "checkStart()" << endl;
	cout << "open seats: " << ggzdmod_count_seats(mod, GGZ_SEAT_OPEN) << endl;

	if(m_state != state_waiting) return;

	if(ggzdmod_count_seats(mod, GGZ_SEAT_OPEN) == 0)
	{
//		// setup broadcasting
//		for(int i = 0; i < ggzdmod_get_num_seats(mod); i++)
//		{
//			// inform of game start
//			fd = ggzdmod_get_seat(mod, i).fd;
//			if(fd < 0) continue;
//			*net << MNet::add << fd;
//		}

		// start broadcasting
		selected_players = ggzdmod_count_seats(mod, GGZ_SEAT_PLAYER);
		*net << MNet::multicast;
		*net << Net::begin << opcode_state << state_start << Net::end << Net::flush;

		// determine free cells
		for(int j = 0; j < level->height(); j++)
			for(int i = 0; i < level->height(); i++)
				if((level->tile(i, j) != Level::invalid)
				&& (level->tile(i, j) != Level::wall)
				&& (level->tile(i, j) != Level::space))
				{
					Cell *c = new Cell(i, j);
					freecelllist.push_back(c);
				}

		// fill up one quarter of them with scree
		int size = (int)(freecelllist.size() * rules->scree_percentage / 100);
		for(int i = 0; i < size; i++)
		{
			int pos = rand() % freecelllist.size();
			Cell *c = at(freecelllist, pos);
			level->setTile(c->x(), c->y(), Level::itemScree);
			Item *item = new Item(generator(), c->x(), c->y(), NULL, Level::itemScree);
			itemlist.push_back(item);

			*net << Net::begin << opcode_action << action_newitem << Level::itemScree << item->id() << c->x() << c->y() << Net::end;

			freecelllist.remove(c);
		}
		*net << Net::flush;

		// randomly place 10 items
		if(!rules->custom())
		{
			for(int i = 0; i < 10; i++)
			{
				int pos = rand() % freecelllist.size();
				Cell *c = at(freecelllist, pos);
				int type = Level::itemScree;
				switch(rand() % 3)
				{
					case 0:
						type = Level::itemBomb;
						break;
					case 1:
						type = Level::itemPower;
						break;
					case 2:
						type = Level::itemLive;
						break;
				}
				level->setTile(c->x(), c->y(), type);
				Item *item = new Item(generator(), c->x(), c->y(), NULL, type);
				itemlist.push_back(item);

				*net << Net::begin << opcode_action << action_newitem << type << item->id() << c->x() << c->y() << Net::end;

				freecelllist.remove(c);
			}

			// place a grail

			int pos = rand() % freecelllist.size();
			Cell *c = at(freecelllist, pos);
			Item *item = new Item(generator(), c->x(), c->y(), NULL, Level::itemGrail);
			itemlist.push_back(item);

			*net << Net::begin << opcode_action << action_newitem << Level::itemGrail << item->id() << c->x() << c->y() << Net::end;

			freecelllist.remove(c);

			*net << Net::flush;
		}
		else
		{
			for(int i = 0; i < rules->bomb_number; i++)
			{
				int pos = rand() % freecelllist.size();
				Cell *c = at(freecelllist, pos);
				Item *item = new Item(generator(), c->x(), c->y(), NULL, Level::itemBomb);
				itemlist.push_back(item);

				*net << Net::begin << opcode_action << action_newitem << Level::itemBomb << item->id() << c->x() << c->y() << Net::end;

				freecelllist.remove(c);
			}

			for(int i = 0; i < rules->life_number; i++)
			{
				int pos = rand() % freecelllist.size();
				Cell *c = at(freecelllist, pos);
				Item *item = new Item(generator(), c->x(), c->y(), NULL, Level::itemLive);
				itemlist.push_back(item);

				*net << Net::begin << opcode_action << action_newitem << Level::itemLive << item->id() << c->x() << c->y() << Net::end;

				freecelllist.remove(c);
			}

			for(int i = 0; i < rules->power_number; i++)
			{
				int pos = rand() % freecelllist.size();
				Cell *c = at(freecelllist, pos);
				Item *item = new Item(generator(), c->x(), c->y(), NULL, Level::itemPower);
				itemlist.push_back(item);

				*net << Net::begin << opcode_action << action_newitem << Level::itemPower << item->id() << c->x() << c->y() << Net::end;

				freecelllist.remove(c);
			}

			for(int i = 0; i < rules->grail_number; i++)
			{
				int pos = rand() % freecelllist.size();
				Cell *c = at(freecelllist, pos);
				Item *item = new Item(generator(), c->x(), c->y(), NULL, Level::itemGrail);
				itemlist.push_back(item);

				*net << Net::begin << opcode_action << action_newitem << Level::itemGrail << item->id() << c->x() << c->y() << Net::end;

				freecelllist.remove(c);
			}

			for(int i = 0; i < rules->speed_number; i++)
			{
				int pos = rand() % freecelllist.size();
				Cell *c = at(freecelllist, pos);
				Item *item = new Item(generator(), c->x(), c->y(), NULL, Level::itemSpeed);
				itemlist.push_back(item);

				*net << Net::begin << opcode_action << action_newitem << Level::itemSpeed << item->id() << c->x() << c->y() << Net::end;

				freecelllist.remove(c);
			}

			*net << Net::flush;
		}

		// create players
		// ggzdmod_get_num_seats(mod) with GGZ_SEAT_PLAYER should equal selected_players !!!
		for(int i = 0; i < ggzdmod_get_num_seats(mod); i++)
		{
			int fd = ggzdmod_get_seat(mod, i).fd;
			if(fd < 0) continue;
			int pos = rand() % freecelllist.size();
			Cell *c = at(freecelllist, pos);
			Player *p = new Player(generator(), c->x(), c->y(), NULL);
			p->numLives(rules->player_lives);
			p->numBombs(rules->player_bombs);
			p->power(rules->player_power);
			p->setFd(fd);
			p->setSeat(i);
			playerlist.push_back(p);

			cout << "player " << i << " with fd " << fd << " placed on map" << endl;
			*net << Net::begin << opcode_action << action_newplayer << i << c->x() << c->y() << Net::end << Net::flush;

			freecelllist.remove(c);
		}

		// create monsters
		for(int i = 0; i < selected_bots; i++)
		{
			int pos = rand() % freecelllist.size();
			Cell *c = at(freecelllist, pos);

			createMonster(generator(), c->x(), c->y());

			freecelllist.remove(c);
		}

		// create exit
		int pos = rand() % freecelllist.size();
		Cell *c = at(freecelllist, pos);
		exit = new Exit(-1, c->x(), c->y(), NULL);

		*net << Net::begin << opcode_action << action_newexit << c->x() << c->y() << Net::end << Net::flush;

		freecelllist.remove(c);

		*net << MNet::peer;

		m_state = state_playing;
	}
	cout << "checkStart() done" << endl;
}

void Handler::dataEvent(GGZdMod *mod, int fd)
{
	int opcode;
	int minor;
	char *map;
	int bots;
	int gen, direction;
	std::string file;
	bool success;
	Player *p;
	list<Player*>::iterator pit;

	// Find player
	p = NULL;
	cout << "playerlist size is " << playerlist.size() << endl;
	for(pit = playerlist.begin(); pit != playerlist.end(); pit++)
	{
		cout << "check player fd " << (*pit)->fd() << endl;
		if((*pit)->fd() == fd)
		{
			p = (*pit);
			cout << "found player " << p << endl;
			break;
		}
	}
	//if(!p)
	//{
	//	std::cerr << "No player with fd " << fd << " known!" << std::endl;
	//}

	*net << Net::channel << fd;
	*net << Net::input;

	while(1)
	{
		// Network buffering
		*net << Net::channel << fd;
		//if(net->empty()) *net << Net::input;

		if(net->empty()) break;

		*net >> &opcode;
		cout << "opcode: " << opcode << endl;

		switch(opcode)
		{
			case opcode_move:
				// Read move from player
				cout << "move!" << endl;
				*net >> &gen >> &direction;
				cout << "generator: " << gen << endl;
				cout << "direction: " << direction << endl;

				// evaluate move
				if(m_state != state_playing)
				{
					cout << "Cheater! Not playing yet."  << endl;
					*net << Net::channel << fd << opcode_cheat;
					close(fd);
					return;
				}

				// sanity check
				if(!p)
				{
					cout << "Cheater! No player with fd " << fd << " found."  << endl;
					*net << Net::channel << fd << opcode_cheat;
					close(fd);
					return;
				}

				// check boundaries
				int x, y;
				x = p->x();
				y = p->y();
				if(direction == move_up) y--;
				else if(direction == move_down) y++;
				else if(direction == move_left) x--;
				else if(direction == move_right) x++;
				if(walkableTile(x, y))
				{
					// execute move
					p->move(x, y);
					cout << "[current position] " << p->x() << "/" << p->y() << endl;

					// broadcast move
					*net << MNet::multicast;
					*net << Net::begin << opcode_move << gen << move_player << p->id() << direction << Net::end << Net::flush;
					*net << MNet::peer;

					// check for items
					checkItem(p, true);
				}
				break;
			case opcode_action:
				cout << "action!" << endl;

				// sanity check
				if(!p)
				{
					cout << "Cheater! No player assigned yet."  << endl;
					*net << Net::channel << fd << opcode_cheat;
					close(fd);
					return;
				}

				*net >> &minor;
				if(minor == action_newbomb)
				{
					// broadcast bomb creation
					Bomb *bomb = new Bomb(generator(), p->x(), p->y(), NULL, p);
					bomblist.push_back(bomb);

					*net << MNet::multicast;
					*net << MNet::begin << opcode_action << action_newbomb << p->id() << bomb->id() << p->x() << p->y() << Net::end << Net::flush;
					*net << MNet::peer;
				}
				break;
			case opcode_map:
				cout << "map!" << endl;
				*net >> &minor;
				switch(minor)
				{
					case map_selected:
						// read information about selected map
						*net >> &map >> &bots;
						cout << "selected map: " << map << endl;

						// check for validity
						if(m_state != state_map)
						{
							cout << "Cheater! No map selection allowed yet."  << endl;
							*net << Net::channel << fd << opcode_cheat;
							close(fd);
							return;
						}

						if(bots > ggzdmod_count_seats(mod, GGZ_SEAT_BOT))
						{
							cout << "Cheater! Bots (" << bots << ") > ggzmod_count_seats(bots) (" << ggzdmod_count_seats(mod, GGZ_SEAT_BOT) << ")."  << endl;
							*net << Net::channel << fd << opcode_cheat;
							close(fd);
							return;
						}

						// try to load
						level = new Level();
						file = DATADIR "/levels/";
						file += map;
						file += ".txt";
						success = level->loadLevel(file.c_str());
						if(!success)
						{
							cout << "Cheater! Map (" << file.c_str() << ") doesn't exist."  << endl;

							*net << Net::channel << fd << opcode_cheat;
							close(fd);
							return;
						}

						// broadcast map choice
						*net << MNet::multicast;
						*net << Net::begin << opcode_map << map_selected << map << bots << Net::end << Net::flush;
						*net << MNet::peer;

						/*ggz_free(map);*/
						selected_map = map;
						selected_bots = bots;

						// move to new state
						m_state = state_waiting;

						// are we there yet?
						if(rules) checkStart(mod);
						break;
				}
				break;
			case opcode_rules:
				cout << "rules!" << endl;
				if(!rules)
				{
					rules = new Rule();
					*net >> &rules;

					// broacast rules
					*net << MNet::multicast;
					*net << opcode_rules;
					*net << rules;
					*net << MNet::peer;

					checkStart(mod);
				}
				else
				{
					cout << "Cheater! Already got rules."  << endl;
					*net << Net::channel << fd << opcode_cheat;
					close(fd);
				}
				break;
			default:
				// detect cheating clients
				cout << "Cheater! Unknown opcode." << endl;
				*net << Net::channel << fd << opcode_cheat;
				close(fd);
				break;
		}
	}
}

void Handler::leaveEvent(GGZdMod *mod, int fd)
{
	*net << MNet::remove << fd;
}

void Handler::joinEvent(GGZdMod *mod, int fd)
{
	// First player must select the map
	/*if(ggzdmod_count_seats(mod, GGZ_SEAT_PLAYER) == 1)*/

	*net << MNet::add << fd;

	if(m_state == state_launched)
	{
		cout << "require map" << endl;
		m_state = state_map;
		*net << Net::channel << fd;
		*net << Net::begin << opcode_map << map_select << ggzdmod_count_seats(mod, GGZ_SEAT_BOT);
		*net << Net::end << Net::flush;
	}
	else if((m_state == state_waiting) || (m_state == state_playing))
	{
		*net << Net::channel << fd;
		*net << Net::begin << opcode_map << map_selected << selected_map << selected_bots;
		*net << Net::end << Net::flush;

		// Are we ready yet?
		checkStart(mod);
	}
}

void Handler::event(GGZdMod *ggzdmod, GGZdModEvent event, const void *data) // static !!! pass object?
{
	int player = -1;
	int spectator;
	int fd;
	GGZSeat *seat;

	cout << "================"<< endl;
	cout << "Got event: " << event << endl;

	fd = -1;

	if((event == GGZDMOD_EVENT_JOIN)
	|| (event == GGZDMOD_EVENT_PLAYER_DATA))
	{
		player = *(int*)data;
		fd = ggzdmod_get_seat(ggzdmod, player).fd;
		cout << "* Associated player: " << player << " (" <<
			ggzdmod_get_seat(ggzdmod, player).name << ")" << endl;
	}
	if(event == GGZDMOD_EVENT_LEAVE)
	{
		seat = (GGZSeat*)data;
		player = seat->num;
		fd = seat->fd;
		cout << "* Associated player: " << player << " (" <<
			seat->name << ")" << endl;
	}

	if((event == GGZDMOD_EVENT_SPECTATOR_JOIN)
	|| (event == GGZDMOD_EVENT_SPECTATOR_LEAVE))
	{
		spectator = *(int*)data;
		fd = ggzdmod_get_spectator(ggzdmod, spectator).fd;
		cout << "* Associated spectator: " << spectator << " (" <<
			ggzdmod_get_spectator(ggzdmod, spectator).name << ")" << endl;
	}

	if((event == GGZDMOD_EVENT_JOIN)
	|| (event == GGZDMOD_EVENT_SPECTATOR_JOIN))
	{
		//fcntl(fd, F_SETFL, O_NONBLOCK);
	}

	switch(event)
	{
		case GGZDMOD_EVENT_STATE:
			cout << "* Type: state change" << endl;
			break;
		case GGZDMOD_EVENT_JOIN:
			cout << "* Type: join" << endl;
			o->joinEvent(ggzdmod, fd);
			break;
		case GGZDMOD_EVENT_LEAVE:
			// TODO: remove player from map, send updates to all remaining
			cout << "* Type: leave" << endl;
			o->leaveEvent(ggzdmod, fd);
			break;
		case GGZDMOD_EVENT_PLAYER_DATA:
			cout << "* Type: player data" << endl;
			o->dataEvent(ggzdmod, fd);
			break;
		case GGZDMOD_EVENT_SPECTATOR_JOIN:
			break;
		case GGZDMOD_EVENT_SPECTATOR_LEAVE:
			break;
		case GGZDMOD_EVENT_SEAT:
		case GGZDMOD_EVENT_LOG:
		case GGZDMOD_EVENT_SPECTATOR_DATA:
#ifdef GGZDMOD_VERSION_MAJOR /* GGZ >= 0.0.7 */
		case GGZDMOD_EVENT_SPECTATOR_SEAT:
#if GGZDMOD_VERSION_MICRO <= 13
		case GGZDMOD_EVENT_GAMEREPORT:
		case GGZDMOD_EVENT_REQ_BOT:
		case GGZDMOD_EVENT_REQ_BOOT:
		case GGZDMOD_EVENT_REQ_OPEN:
		case GGZDMOD_EVENT_REQ_NUM_SEATS:
#if GGZDMOD_VERSION_MICRO >= 11
		case GGZDMOD_EVENT_SAVEGAMEREPORT:
#endif /* >= 0.0.11 */
#endif /* <= 0.0.13 */
#endif /* >= 0.0.7 */
		case GGZDMOD_EVENT_ERROR:
			/* not supported */
			break;
	}
}

void Handler::loop()
{
	GGZdMod *ggzdmod;
	int ret;
	long diff;
	struct timeval oldtimer, timer, oldtimer2, timer2;

	cout << "(handler: loop)" << endl;

	ggzdmod = ggzdmod_new(GGZDMOD_GAME);

	ggzdmod_set_handler(ggzdmod, GGZDMOD_EVENT_STATE, &event);
	ggzdmod_set_handler(ggzdmod, GGZDMOD_EVENT_JOIN, &event);
	ggzdmod_set_handler(ggzdmod, GGZDMOD_EVENT_LEAVE, &event);
	ggzdmod_set_handler(ggzdmod, GGZDMOD_EVENT_PLAYER_DATA, &event);
	ggzdmod_set_handler(ggzdmod, GGZDMOD_EVENT_SPECTATOR_JOIN, &event);
	ggzdmod_set_handler(ggzdmod, GGZDMOD_EVENT_SPECTATOR_LEAVE, &event);

	ret = ggzdmod_connect(ggzdmod);
	if(ret != -1)
	{
		cout << "(handler: connected)" << endl;

		globalmod = ggzdmod;

		gettimeofday(&oldtimer, NULL);
		gettimeofday(&oldtimer2, NULL);

#ifndef GGZDMOD_VERSION_MAJOR /* GGZ < 0.0.7 */
		while(ggzdmod_get_state(ggzdmod) != GGZDMOD_STATE_DONE)
		{
#endif

		//ret = ggzdmod_loop(ggzdmod);
		while((ret = ggzdmod_dispatch(ggzdmod)) != -1)
		{
			if(m_state == state_playing)
			{
				/* monster timer (500 msec) */
				gettimeofday(&timer, NULL);
				diff = timer.tv_sec - oldtimer.tv_sec;
				diff = diff * 1000 * 1000 + (timer.tv_usec - oldtimer.tv_usec);
				if(diff > 500 * 1000)
				{
					oldtimer.tv_sec = timer.tv_sec;
					oldtimer.tv_usec = timer.tv_usec;
					//cout << "## timer (500)" << endl;
					moveMonsters();
				}

				/* player timer (100 msec) */
				timer2.tv_sec = timer.tv_sec;
				timer2.tv_usec = timer.tv_usec;
				diff = timer2.tv_sec - oldtimer2.tv_sec;
				diff = diff * 1000 * 1000 + (timer2.tv_usec - oldtimer2.tv_usec);
				if(diff > 100 * 1000)
				{
					oldtimer2.tv_sec = timer2.tv_sec;
					oldtimer2.tv_usec = timer2.tv_usec;
					//cout << "## timer (100)" << endl;
					progress();
				}
			}
		}

#ifndef GGZDMOD_VERSION_MAJOR /* GGZ < 0.0.7 */
		}
#endif

		cerr << "Warning: game will end in 10 seconds" << endl;
		sleep(10);
		cerr << "Warning: game ends now" << endl;
		ggzdmod_disconnect(ggzdmod);
	}
	else
	{
		cerr << "Error: Couldn't connect to ggzd." << endl;
	}
	ggzdmod_free(ggzdmod);
}

int Handler::generator()
{
	static int gen = 0;
	return ++gen;
}

