#include <qimage.h>
#include <qpixmap.h>
#include <qpainter.h>
#include <qtimer.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

#include "ai_player.h"
#include "blast.h"
#include "bomb.h"
#include "item.h"
#include "playcanvas.h"
#include "player.h"
#include "resource.h"
#include "exit.h"
#include "rule.h"

#include <kdebug.h>
#include <klocale.h>
#include <kapplication.h>

#include <qvaluelist.h>
#include <qpoint.h>

PlayCanvas::PlayCanvas(QObject *parent, const char *name)
	: QCanvas(parent, name)
{
	m_level = NULL;
	m_away = false;
	m_enabled = false;
	m_localenabled = false;
	m_players = 3;
	m_rules = NULL;

	m_score = 0;
	m_clock = 0;

	weapon = NULL;
	weaponcounter = 0;

	exit = NULL;
	currPlayer = NULL;
	overlayfont = NULL;

	srand(time(0) + getpid());

	resource = new Resource();

	bombList.setAutoDelete(true);
	blastList.setAutoDelete(true);
	playerList.setAutoDelete(true);
	itemList.setAutoDelete(true);

	startTimer(100);

	monsterTimer = new QTimer(this);
	connect(monsterTimer, SIGNAL(timeout()), this, SLOT(monsterTimeout()));
	monsterTimer->start(500);

	clockTimer = new QTimer(this);
	connect(clockTimer, SIGNAL(timeout()), this, SLOT(clockTimeout()));
	clockTimer->start(1000);
}

PlayCanvas::~PlayCanvas()
{
	if (monsterTimer)
		delete monsterTimer;

	playerList.remove(currPlayer);

	currPlayer = 0;
}

void PlayCanvas::setTheme(QString theme)
{
	if(theme.isNull()) return;

	resource->setTheme(theme);

	// reload everything
	if(m_level)
	{
		QPixmap background(resource->path("background"));
		QImage im = background.convertToImage();
		im = im.smoothScale(resource->tilesize() * m_level->width(), resource->tilesize() * m_level->height());
		background.convertFromImage(im);
		setBackgroundPixmap(background);

		resize(resource->tilesize() * m_level->width(), resource->tilesize() * m_level->height());
	}

	drawBackground(false, 0, 0);

	// greyed out
	if(m_enabled == false)
	{
		if(!backgroundPixmap().isNull())
		{
			QImage im = backgroundPixmap().convertToImage();

			for(int j = 0; j < im.height(); j += 2)
				for(int i = 0; i < im.width(); i += 2)
					im.setPixel(i, j, 0);

			QPixmap background;
			background.convertFromImage(im);

			/*QPainter p;
			QFont f(QString::null, 48, QFont::Black);
			p.begin(&background);
			p.setPen(QPen(QColor(255, 0, 0), 4));
			p.setFont(f);
			QFontMetrics m(f);
			p.drawText(im.width() / 2 - m.width(i18n("Loser!")) / 2, im.height() / 2 - 24, i18n("Loser!"));
			p.end();*/

			setBackgroundPixmap(background);
		}
	}

	// reload all items
	if(exit)
	{
		exit->setSprite(resource->pixmaparray("exit", 3));
		exit->move(exit->x(), exit->y());
	}
	if(weapon)
	{
		weapon->setSprite(resource->pixmaparray(weapontype(weaponcounter), 8));
		weapon->move(weapon->x(), weapon->y());
	}
	if(currPlayer)
	{
		currPlayer->setSprite(resource->pixmaparray("guy1", 1/*12*/));
		currPlayer->move(currPlayer->x(), currPlayer->y());
	}

	for (Player *player = playerList.first(); player != NULL; player = playerList.next())
	{
		player->setSprite(resource->pixmaparray("guy1", 1/*12*/));
		player->move(player->x(), player->y());
	}
	for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next())
	{
		monster->setSprite(resource->pixmaparray("monster1", 2/*13*/));
		monster->move(monster->x(), monster->y());
	}

	for (Bomb *bomb = bombList.first(); bomb != NULL; bomb = bombList.next())
	{
		bomb->setSprite(resource->pixmaparray("bomb", /*2*/8));
		bomb->move(bomb->x(), bomb->y());
	}

	for (Blast *blast = blastList.first(); blast != NULL; blast = blastList.next())
	{
		blast->setSprite(resource->pixmaparray("blast", /*10*/4));
		blast->move(blast->x(), blast->y());
	}

	for (Item *item = itemList.first(); item != NULL; item = itemList.next())
	{
		switch(item->type())
		{
			case Level::invalid:
				// nothing
				break;
			case Level::ground:
				break;
				// background
			case Level::wall:
				// background
				break;
			case Level::bridge:
				// background
				break;
			case Level::space:
				// nothing
				break;
			case Level::itemScree:
				item->setSprite(resource->pixmaparray("item_scree", 3));
				break;
			case Level::itemBomb:
				item->setSprite(resource->pixmaparray("item_bomb", 3));
				break;
			case Level::itemLive:
				item->setSprite(resource->pixmaparray("item_live", 3));
				break;
			case Level::itemPower:
				item->setSprite(resource->pixmaparray("item_power", 3));
				break;
			case Level::itemGrail:
				item->setSprite(resource->pixmaparray("item_grail", 3));
				break;
			case Level::itemSpeed:
				item->setSprite(resource->pixmaparray("item_speed", 3));
				break;
			case Level::itemInvisible:
				item->setSprite(resource->pixmaparray("item_invisible", 3));
				break;
			case Level::itemFlash:
				item->setSprite(resource->pixmaparray("item_flash", 3));
				break;
		}
		item->move(item->x(), item->y());
	}
}

void PlayCanvas::drawBackground(bool only, int only_x, int only_y)
{
	QPainter painter;
	QString pixmap;
	QPixmap pm;

	if(!m_level) return;
	
	pm = backgroundPixmap();

	if(pm.isNull()) return;

	QPixmap pmBridge = resource->pixmap("bridge", 0);
	QPixmap pmGround = resource->pixmap("ground", 0);
	QPixmap pmWall = resource->pixmap("wall", 0);

	painter.begin(&pm);
	for(int j = 0; j < m_level->height(); j++) {
		if((only) && (j != only_y)) continue;
		for(int i = 0; i < m_level->width(); i++) {
			if((only) && (i != only_x)) continue;
			switch (m_level->tile(i, j)) {
				case Level::wall:
					if((m_rules) && (m_rules->wall_damage))
						pmWall = resource->pixmapmulti("walldamage", m_level->condition(i, j));
					painter.drawPixmap(resource->tilesize() * i, resource->tilesize() * j, pmWall);
					break;
				case Level::bridge:
					painter.drawPixmap(resource->tilesize() * i, resource->tilesize() * j, pmBridge);
					break;
				case Level::space:
					break;
				case Level::ground:
				default:
					if((m_rules) && (m_rules->ground_damage))
						pmGround = resource->pixmapmulti("grounddamage", m_level->condition(i, j));
					painter.drawPixmap(resource->tilesize() * i, resource->tilesize() * j, pmGround);
					break;
			}

		}
	}

	painter.end();

	setBackgroundPixmap(pm);
}

void PlayCanvas::buildLevel(QString levelfile)
{
	int w, h;

	if(levelfile.isNull()) return;
	//kdDebug() << "build level with " << "'" << levelfile << "'" << endl;

	m_level = new Level();

	bool success = m_level->loadLevel(resource->load(levelfile, Resource::level));
	if(!success) return;
	//kdDebug() << "loaded file: '" << resource->load(levelfile, Resource::level) << "'" << endl;

	QPixmap background(resource->path("background"));
	QImage im = background.convertToImage();
	im = im.smoothScale(resource->tilesize() * m_level->width(), resource->tilesize() * m_level->height());
	background.convertFromImage(im);
	setBackgroundPixmap(background);

	/* paint the background */
	drawBackground(false, 0, 0);

	w = resource->tilesize() * m_level->width();
	h = resource->tilesize() * m_level->height();
	resize(w, h);

	setAdvancePeriod(1);
	update();
}

void PlayCanvas::createExit(int x, int y)
{
	QCanvasSprite *sprite = new QCanvasSprite(resource->pixmaparray("exit", 3), this);
	sprite->setZ(-0.1);
	exit = new Exit(-1, x, y, sprite);
}

void PlayCanvas::createItem(int type, int id, int x, int y)
{
	QCanvasSprite *sprite;

	sprite = NULL;
	switch(type)
	{
		case Level::itemScree:
			sprite = new QCanvasSprite(resource->pixmaparray("item_scree", 3), this);
			break;
		case Level::itemPower:
			sprite = new QCanvasSprite(resource->pixmaparray("item_power", 3), this);
			break;
		case Level::itemGrail:
			sprite = new QCanvasSprite(resource->pixmaparray("item_grail", 3), this);
			break;
		case Level::itemLive:
			sprite = new QCanvasSprite(resource->pixmaparray("item_live", 3), this);
			break;
		case Level::itemBomb:
			sprite = new QCanvasSprite(resource->pixmaparray("item_bomb", 3), this);
			break;
		case Level::itemSpeed:
			sprite = new QCanvasSprite(resource->pixmaparray("item_speed", 3), this);
			break;
		case Level::itemInvisible:
			sprite = new QCanvasSprite(resource->pixmaparray("item_invisible", 3), this);
			break;
		case Level::itemFlash:
			sprite = new QCanvasSprite(resource->pixmaparray("item_flash", 3), this);
			break;
	}

	Item *item = new Item(id, x, y, sprite, type);
	itemList.append(item);

	m_level->setTile(x, y, type);
}

void PlayCanvas::createPlayer(int id, int x, int y)
{
	QCanvasSprite *sprite = new QCanvasSprite(resource->pixmaparray("guy1", 1/*12*/), this);
	if(sprite->frameCount() > 2) sprite->setFrame(2);

	Player *p = new Player(id, x, y, sprite);
	p->power(m_rules->player_power);
	p->numBombs(m_rules->player_bombs);
	p->numLives(m_rules->player_lives);
	playerList.append(p);

	// HACK!
	if(!currPlayer) currPlayer = p;
}

void PlayCanvas::createMonster(int id, int x, int y)
{
	QCanvasSprite *sprite = new QCanvasSprite(resource->pixmaparray("monster1", 2/*13*/), this);
	if(sprite->frameCount() > 2) sprite->setFrame(2);
	AI_Player *monster = new AI_Player(id, x, y, sprite);
	monster->power(m_rules->monster_power);
	monster->numBombs(m_rules->monster_bombs);
	monster->numLives(m_rules->monster_lives);
	monsterList.append(monster);
}

void PlayCanvas::createMonster(int count)
{
	int orig = count;
	if(!count) count = m_players;
	for (int i = 0; i < count; i++) {

		if(orig)
		{
			// FIXME: duplicate with placeDynamic -> generate query function
			QValueList<QPoint> freeCells;
			for (int j = 0; j < m_level->height(); j++)
				for (int i = 0; i < m_level->width(); i++)
					if (m_level->tile(i, j) != Level::invalid && m_level->tile(i, j) != Level::wall && m_level->tile(i, j) != Level::space) {
						QPoint point;
						point.setX(i);
						point.setY(j);
						freeCells.append(point);
					}

			int pos = random(freeCells.count());

			createMonster(-1, freeCells[pos].x(), freeCells[pos].y());

			freeCells.remove(freeCells[pos]);
		}
		else
		{
			createMonster(-1, 0, 0);
		}
	}
}

void PlayCanvas::setPlayers(int players)
{
	m_players = players;
}

void PlayCanvas::placeDynamic()
{
	AI_Player *monster;
	Player *player;
	QValueList<QPoint> freeCells;
	int pos, i;

	if(!m_level) return;

	for (int j = 0; j < m_level->height(); j++)
		for (int i = 0; i < m_level->width(); i++)
			if (m_level->tile(i, j) != Level::invalid && m_level->tile(i, j) != Level::wall && m_level->tile(i, j) != Level::space) {
				QPoint point;
				point.setX(i);
				point.setY(j);
				freeCells.append(point);
			}

	int size = (int)(freeCells.count() * m_rules->scree_percentage / 100);
	
	// place the scree items
	for (i = 0; i < size; i++) {
		pos = random(freeCells.count());
		createItem(Level::itemScree, -1, freeCells[pos].x(), freeCells[pos].y());
		freeCells.remove(freeCells[pos]);
	}

	// place the other items
	if(!m_rules->custom())
	{
		for (i = 0; i < 10; i++) {
			pos = random(freeCells.count());
			int type = Level::itemScree;

			switch (random(3)) {
				case 0:
					type = Level::itemBomb;
					break;
				case 1:
					type = Level::itemPower;
					break;
				case 2:
					type = Level::itemLive;
					break;
			}

			createItem(type, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}

		// place special items
		if(random(2))
		{
			pos = random(freeCells.count());
			createItem(Level::itemGrail, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}
		if(random(2))
		{
			pos = random(freeCells.count());
			createItem(Level::itemSpeed, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}
		if(random(2))
		{
			pos = random(freeCells.count());
			createItem(Level::itemFlash, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}
		if(random(2))
		{
			pos = random(freeCells.count());
			createItem(Level::itemInvisible, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}
	}
	else
	{
		for(i = 0; i < m_rules->bomb_number; i++)
		{
			pos = random(freeCells.count());
			createItem(Level::itemBomb, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}

		for(i = 0; i < m_rules->life_number; i++)
		{
			pos = random(freeCells.count());
			createItem(Level::itemLive, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}

		for(i = 0; i < m_rules->power_number; i++)
		{
			pos = random(freeCells.count());
			createItem(Level::itemPower, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}

		for(i = 0; i < m_rules->grail_number; i++)
		{
			pos = random(freeCells.count());
			createItem(Level::itemGrail, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}

		for(i = 0; i < m_rules->speed_number; i++)
		{
			pos = random(freeCells.count());
			createItem(Level::itemSpeed, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}
		for(i = 0; i < m_rules->invisible_number; i++)
		{
			pos = random(freeCells.count());
			createItem(Level::itemInvisible, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}
		for(i = 0; i < m_rules->flash_number; i++)
		{
			pos = random(freeCells.count());
			createItem(Level::itemFlash, -1, freeCells[pos].x(), freeCells[pos].y());
			freeCells.remove(freeCells[pos]);
		}
	}

	// place the players
	for (player = playerList.first(); player != NULL; player = playerList.next()) {
		QPoint p = goodrandom(freeCells.count());
		player->move(p.x(), p.y());
		for(unsigned int i = 0; i < freeCells.count(); i++)
			if((freeCells[i].x() == p.x()) && (freeCells[i].y() == p.y())) freeCells.remove(freeCells[i]);
	}

	// place the monster
	for (monster = monsterList.first(); monster != NULL; monster = monsterList.next()) {
		QPoint p = goodrandom(freeCells.count());
		monster->move(p.x(), p.y());
		for(unsigned int i = 0; i < freeCells.count(); i++)
			if((freeCells[i].x() == p.x()) && (freeCells[i].y() == p.y())) freeCells.remove(freeCells[i]);
	}

	// place the exit
	pos = random(freeCells.count());
	createExit(freeCells[pos].x(), freeCells[pos].y());
	freeCells.remove(freeCells[pos]);
}

QString PlayCanvas::weapontype(int weaponcounter)
{
	switch(weaponcounter)
	{
		case 0:
			return "bomb";
			break;
		case 1:
			if(m_rules->timebomb_allowed) return "timebomb";
			else return "bomb";
			break;
		case 2:
			if(m_rules->moveablebomb_allowed) return "moveablebomb";
			else return "bomb";
			break;
	}
	return QString::null;
}

void PlayCanvas::selectWeapon()
{
	if(!m_enabled) return;

	if(weapon)
	{
		delete weapon;
		weaponcounter = (weaponcounter + 1) % 3;
	}

	QCanvasSprite *bombSprite = new QCanvasSprite(resource->pixmaparray(weapontype(weaponcounter), /*2*/8), this);
	bombSprite->setZ(0.1);
	weapon = new Bomb(-1, currPlayer->x(), currPlayer->y(), bombSprite, currPlayer);
}

bool PlayCanvas::walkableTile(int fromx, int fromy, int x, int y)
{
	Bomb *bomb;
	Item *item;
	bool valid = true;

	if(!m_level) return false;

	// do we stay in range
	if ((x >= 0) && (y >= 0) && (x < m_level->width()) && (y < m_level->height())) {
		// is there something that could block us
		if (m_level->tile(x, y) == Level::wall || m_level->tile(x, y) == Level::invalid || m_level->tile(x, y) == Level::space)
			return false;

		if ((m_level->tile(x, y) == Level::itemBomb)
		|| (m_level->tile(x, y) == Level::itemLive)
		|| (m_level->tile(x, y) == Level::itemPower)
		|| (m_level->tile(x, y) == Level::itemGrail)
		|| (m_level->tile(x, y) == Level::itemSpeed)
		|| (m_level->tile(x, y) == Level::itemFlash)
		|| (m_level->tile(x, y) == Level::itemInvisible))
		{
			if ((item = findItem(x, y)) != NULL) {
				if(!item->isActive())
					return false;
			}
		}

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

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

		if (!bombList.isEmpty()) {
			for (bomb = bombList.first(); bomb != NULL; bomb = bombList.next()) {
				if (bomb->x() == x && bomb->y() == y) {
					// check for moveable bombs
					if(bomb->moveable())
					{
						if(walkableTile(x, y, fromx + (x - fromx) * 2, fromy + (y - fromy) * 2))
						{
							bomb->move(fromx + (x - fromx) * 2, fromy + (y - fromy) * 2);
							return true;
						}
						else return false;
					}
					else return false;
				}
			}
		}
	} else {
		return false;
	}

	return valid;
}

bool PlayCanvas::action(int event, int option)
{
	if(event == moveEvent)
	{
		currPlayer->steps(0);
	}

	return action_internal(event, option);
}

bool PlayCanvas::action_internal(int event, int option)
{
	int x, y;
	Bomb *bomb;
	bool moved = true;

	//kdDebug() << "action " << m_away << endl;

	if (m_away == true)
		return false;
	if (m_enabled == false)
		return false;

	x = currPlayer->x();
	y = currPlayer->y();
	switch(event)
	{
		case moveEvent:
			switch(option)
			{
				case optLeft:
					x--;
					if(currPlayer->sprite()->frameCount() > 1) currPlayer->sprite()->setFrame(1);
					break;
				case optRight:
					x++;
					if(currPlayer->sprite()->frameCount() > 1) currPlayer->sprite()->setFrame(0);
					break;
				case optUp:
					y--;
					if(currPlayer->sprite()->frameCount() > 3) currPlayer->sprite()->setFrame(3);
					break;
				case optDown:
					if(currPlayer->sprite()->frameCount() > 3) currPlayer->sprite()->setFrame(2);
					y++;
					break;
			}

			if(m_localenabled)
			{
				if (walkableTile(currPlayer->x(), currPlayer->y(), x, y)) {
					if(weapon)
					{
						delete weapon;
						weapon = NULL;
					}
					currPlayer->move(x, y);
					checkItem(currPlayer, true);

					emit position(x, y);
				}
				else moved = false;

				// Double move?
				currPlayer->steps(currPlayer->steps() + 1);
				if(currPlayer->steps() < currPlayer->totalsteps()) action_internal(event, option);
			}
			else currPlayer->move(x, y);

			if(m_localenabled)
			{
				if (m_level->tile(x, y) == Level::space)
					currPlayer->numLives(0);
			}
			break;

		case fireEvent:
			placeBomb(currPlayer);
			break;

		case timebombEvent:
			for (bomb = bombList.first(); bomb != NULL; bomb = bombList.next())
				if(bomb->player() == currPlayer) bomb->delay(false);
			break;
	}

	emit information(currPlayer->numLives(), currPlayer->numBombs(), currPlayer->power());

	return moved;
}

void PlayCanvas::createBomb(int playerid, int id, int x, int y)
{
	Q_UNUSED(playerid);

	for(Player *p = playerList.first(); p != NULL; p = playerList.next())
		if(p->id() == id)
		{
			placeBomb(p, id, x, y);
			return;
		}
	placeBomb(NULL, -1, x, y);
}

void PlayCanvas::placeBomb(Player *p)
{
	if(p) placeBomb(p, -1, p->x(), p->y());
}

void PlayCanvas::placeBomb(Player *p, int id, int x, int y)
{
	Bomb *bomb;
	QString type;

	if(m_localenabled)
	{
		if ((p) && (p->numBombs() == 0)) return;

		if (!bombList.isEmpty()) {
			for (bomb = bombList.first(); bomb != NULL; bomb = bombList.next())
				if (bomb->x() == x && bomb->y() == y)
					return;
		}
	}

	if((m_localenabled) && (p) && (p == currPlayer))
	{
		type = weapontype(weaponcounter);
	}
	else type = "bomb";
	QCanvasSprite *bombSprite = new QCanvasSprite(resource->pixmaparray(type, /*2*/8), this);

	bomb = new Bomb(id, x, y, bombSprite, p);
	if(type == "timebomb") bomb->delay(true);
	if(type == "moveablebomb") bomb->setMoveable(true);
	bombList.append(bomb);

	if(p)
	{
		p->numBombs(p->numBombs() - 1);
		if(p->numFlash() > 0)
		{
			bomb->setFlash(true);
			p->numFlash(p->numFlash() - 1);
		}
	}
}

void PlayCanvas::checkItem(/*int x, int y*/ Player *p, bool isplayer)
{
	Item *item;
	int x, y;

	x = p->x();
	y = p->y();

	/* check if player found something */
	if ((item = findItem(x, y)) != NULL) {
		switch (item->type()) {
			case Level::itemBomb:
				p->numBombs(p->numBombs() + 1);
				break;
			case Level::itemLive:
				p->numLives(p->numLives() + 1);
				break;
			case Level::itemPower:
				p->power(p->power() + 1);
				break;
			case Level::itemGrail:
				for(Player *p2 = playerList.first(); p2 != NULL; p2 = playerList.next())
				{
					if(p2->numLives() > 1)
					{
						p2->numLives(p2->numLives() - 1);
						p->numLives(p->numLives() + 1);
					}
				}
				for(AI_Player *p2 = monsterList.first(); p2 != NULL; p2 = monsterList.next())
				{
					if(p2->numLives() > 1)
					{
						p2->numLives(p2->numLives() - 1);
						p->numLives(p->numLives() + 1);
					}
				}
				break;
			case Level::itemSpeed:
				p->totalsteps(2);
				p->stepcounter(50);
				break;
			case Level::itemInvisible:
				p->visibility(0);
				p->stepcounter(50);
				p->sprite()->hide();
				break;
			case Level::itemFlash:
				p->numFlash(p->numFlash() + 1);
				break;
		}
		itemList.remove(item);
		m_level->setTile(x, y, Level::ground);

		if(p == currPlayer) m_score++;
	}
	
	if(isplayer)
	{
		if (exit->x() == x && exit->y() == y)
			if (exit->isActive())
			{
				setOverlay(winner);
				emit levelFinished();
			}
	}

	emit information(currPlayer->numLives(), currPlayer->numBombs(), currPlayer->power());
}

bool PlayCanvas::createBlast(int id, int x, int y)
{
	QCanvasSprite *blastSprite;
	Blast *blast;

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

	blastSprite = new QCanvasSprite(resource->pixmaparray("blast", /*4*/10), this);

	blast = new Blast(id, x, y, blastSprite);
	blastList.append(blast);

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

void PlayCanvas::timerEvent(QTimerEvent *e)
{
	Q_UNUSED(e);

	Item *item;

	if(m_enabled == false) return;

	if(exit) exit->proceed();

	for (Blast *blast = blastList.first(); blast != NULL; blast = blastList.next()) {
		blast->proceed();

		if(m_localenabled == true)
		{
			/* check for player, standing in a blast area */
			for (Player *player = playerList.first(); player != NULL; player = playerList.next()) {
				if (player->x() == blast->x() && player->y() == blast->y() && blast->step() == 1)
					player->numLives(player->numLives() - 1);
			}

			for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) {
				if (monster->x() == blast->x() && monster->y() == blast->y() && blast->step() == 1)
					monster->numLives(monster->numLives() - 1);
			}

			/* check for other bombs, lieing in a blast area */
			for (Bomb *bomb = bombList.first(); bomb != NULL; bomb = bombList.next()) {
				if (bomb->x() == blast->x() && bomb->y() == blast->y())
					bomb->setStep(0);
			}

			/* when exit is hit, generate a new monster */
			if (exit->x() == blast->x() && exit->y() == blast->y() && blast->step() == 1) {
				if (!exit->isActive())
					exit->activate(true);
				if (!exit->isHighlighted())
					createMonster(/*1*/-1, exit->x(), exit->y());
			}

			/* check for other items, lieing in a blast area */
			if ((item = findItem(blast->x(), blast->y())) != NULL) {
				if (blast->step() == 1) {
					if (!item->isActive())
						if (item->type() == Level::itemScree) {
							m_level->setTile(item->x(), item->y(), Level::ground);
							itemList.remove(item);
						} else
							if(!item->isActive()) item->activate(true);
					else {
						itemList.remove(item);
					}
				}
			}
			if (blast->step() == 1) {
				int i = blast->x();
				int j = blast->y();
				if((m_level->tile(i, j) == Level::ground) && (m_rules->ground_damage))
				{
					if(m_level->condition(i, j) < 5)
						m_level->setCondition(i, j, m_level->condition(i, j) + 1);
					else m_level->setTile(i, j, Level::space);
					drawBackground(true, i, j);
				}
				if((m_level->tile(i + 1, j) == Level::wall) && (m_rules->wall_damage))
				{
					if(m_level->condition(i + 1, j) < 5)
						m_level->setCondition(i + 1, j, m_level->condition(i + 1, j) + 1);
					else
					{
						m_level->setTile(i + 1, j, Level::ground);
						m_level->setCondition(i + 1, j, 2);
					}
					drawBackground(true, i + 1, j);
				}
				if((m_level->tile(i, j + 1) == Level::wall) && (m_rules->wall_damage))
				{
					if(m_level->condition(i, j + 1) < 5)
						m_level->setCondition(i, j + 1, m_level->condition(i, j + 1) + 1);
					else
					{
						m_level->setTile(i, j + 1, Level::ground);
						m_level->setCondition(i, j + 1, 2);
					}
					drawBackground(true, i, j + 1);
				}
				if((m_level->tile(i - 1, j) == Level::wall) && (m_rules->wall_damage))
				{
					if(m_level->condition(i - 1, j) < 5)
						m_level->setCondition(i - 1, j, m_level->condition(i - 1, j) + 1);
					else
					{
						m_level->setTile(i - 1, j, Level::ground);
						m_level->setCondition(i - 1, j, 2);
					}
					drawBackground(true, i - 1, j);
				}
				if((m_level->tile(i, j - 1) == Level::wall) && (m_rules->wall_damage))
				{
					if(m_level->condition(i, j - 1) < 5)
						m_level->setCondition(i, j - 1, m_level->condition(i, j - 1) + 1);
					else
					{
						m_level->setTile(i, j - 1, Level::ground);
						m_level->setCondition(i, j - 1, 2);
					}
					drawBackground(true, i, j - 1);
				}
			}
			if (blast->finished())
				blastList.remove(blast);
		}
	}

	for (Bomb *bomb = bombList.first(); bomb != NULL; bomb = bombList.next())
	{
		bomb->proceed();

		if(m_localenabled == true)
		{
			if (bomb->finished()) {
				bomb->player()->numBombs(bomb->player()->numBombs() + 1);

				/* create explosion */
				bool left, right, top, bottom, tl, tr, bl, br;

				createBlast(-1, bomb->x(), bomb->y());

				left = right = top = bottom = true;
				tl = tr = bl = br = true;

				for (int i = 1; i < bomb->player()->power() + 1; i++) {
					if (right)
						right = createBlast(-1, bomb->x() + i, bomb->y());
					if (left)
						left = createBlast(-1, bomb->x() - i, bomb->y());
					if (top)
						top = createBlast(-1, bomb->x(), bomb->y() + i);
					if (bottom)
						bottom = createBlast(-1, bomb->x(), bomb->y() - i);

					if(bomb->flash()) {
						if(tl) tl = createBlast(-1, bomb->x() - i, bomb->y() - i);
						if(tr) tl = createBlast(-1, bomb->x() + i, bomb->y() - i);
						if(bl) tl = createBlast(-1, bomb->x() - i, bomb->y() + i);
						if(br) tl = createBlast(-1, bomb->x() + i, bomb->y() + i);
					}
				}

				bombList.remove(bomb);
			}
		}
	}

	if(m_localenabled == true)
	{
		for (Player *player = playerList.first(); player != NULL; player = playerList.next()) {
			for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) {
				if (monster->x()  == player->x() && monster->y() == player->y())
					if(player->visibility())
						player->numLives(player->numLives() - 1);
			}

			if (player->numLives() <= 0) { /* player died */
				player->sprite()->setFrame(/*12*/0);
				setOverlay(loser);
				emit playerKilled();
			}

			if(player->stepcounter())
			{
				player->stepcounter(player->stepcounter() - 1);
				if(!player->stepcounter())
				{
					player->totalsteps(1);
					player->visibility(1);
					currPlayer->sprite()->show();
				}
			}
		}

		for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) {
			if (monster->numLives() == 0) { /* monster died */
				monster->sprite()->hide();
				monsterList.remove(monster);
			}
		}

		if (monsterList.count() == 0) {
			if(exit) exit->setHighlighted(true);
			for (Item *item = itemList.first(); item != NULL; item = itemList.next()) {
				if(item->type() >= Level::itemBomb)
					item->setHighlighted(true);
			}
		}
	}

	for (Item *item = itemList.first(); item != NULL; item = itemList.next())
		item->proceed();

	if(currPlayer)
		emit information(currPlayer->numLives(), currPlayer->numBombs(), currPlayer->power());
}

void PlayCanvas::moveMonster(int id, int direction)
{
	for(AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next())
		if(monster->id() == id)
		{
			int x = monster->x();
			int y = monster->y();
			switch(direction)
			{
				case AI_Player::Up:
					monster->move(x, y - 1);
					break;
				case AI_Player::Down:
					monster->move(x, y + 1);
					break;
				case AI_Player::Left:
					monster->move(x - 1, y);
					break;
				case AI_Player::Right:
					monster->move(x + 1, y);
					break;
			}
			monster->direction((AI_Player::Direction)direction);
			break;
		}
}

int PlayCanvas::monsterWaypoints(AI_Player *monster, int x, int y)
{
	int ret;

	//kdDebug() << "- " << monster->x() << ":" << monster->y() << " - " << x << ":" << y << endl;

	if((monster->x() == x) && (monster->y() == y))
		return 1;

	for(int j = y - 1; j <= y + 1; j++)
		for(int i = x - 1; i <= x + 1; i++)
		{
			if(((i == x) && (j == y)) || ((i != x) && (j != y))) continue;
			if(m_level->value(i, j)) continue;
			if(walkableTile(x, y, i, j))
			{
				//kdDebug() << "walkabletile: " << i << ":" << j << endl;
				m_level->addValue(i, j, 1);
				ret = monsterWaypoints(monster, i, j);
				if(ret == 1)
				{
					//kdDebug() << "* " << x << ":" << y << endl;
					monster->pushWaypoint(x, y);
					return 1;
				}
			}
		}
	return 0;
}

void PlayCanvas::clockTimeout()
{
	if((currPlayer) && (m_localenabled))
	{
		m_clock++;
		emit informationScore(m_clock, m_score);
	}
}

void PlayCanvas::monsterTimeout()
{
	const int sight = 100;
	bool foundtarget;
	Item *item;

	if(m_enabled == false) return;
	if(m_localenabled == false) return;

	for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) {
		int x = monster->x();
		int y = monster->y();

		// not too often yet because the dumb monsters kill themselves
		if(monster->numBombs() > 0)
		{
			if(random(200 / monster->numBombs()) == 0)
			{
				placeBomb(monster);
			}
		}

		foundtarget = false;

		// Intelligent search for new path
		if((!foundtarget) && (monster->numWaypoints() == 0))
		{
			for(int j = y - sight; j < y + sight; j++)
				for(int i = x - sight; i < x + sight; i++)
				{
					if((i < 0) || (j < 0) || (i >= m_level->width()) || (j >= m_level->height())) continue;
					if ((m_level->tile(i, j) == Level::itemBomb)
					|| (m_level->tile(i, j) == Level::itemLive)
					|| (m_level->tile(i, j) == Level::itemPower)
					|| (m_level->tile(i, j) == Level::itemGrail)
					|| (m_level->tile(i, j) == Level::itemSpeed)
					|| (m_level->tile(i, j) == Level::itemFlash)
					|| (m_level->tile(i, j) == Level::itemInvisible))
					{
						if ((item = findItem(i, j)) != NULL) {
							if(item->isActive())
							{
								// Found target
								kdDebug() << "new target! " << i << ":" << j << endl;
								foundtarget = true;
								m_level->clearValues();
								monster->clearWaypoints();
								monsterWaypoints(monster, i, j);
								kdDebug() << "added " << monster->numWaypoints() << " waypoints." << endl;
								break;
							}
						}
					}
				}
		}

		// Stored path
		if(monster->numWaypoints() > 0)
		{
			foundtarget = true;
			int i = monster->nextX();
			int j = monster->nextY();
			monster->popWaypoint();
			//kdDebug() << "goto: " << x << ":" << y << " - " << i << ":" << j << endl;
			if(!walkableTile(x, y, i, j))
			{
				kdDebug() << "cache dirty, invalidated!" << endl;
				foundtarget = false;
				monster->clearWaypoints();
			}
			else
			{
				if(i < x) monster->direction(AI_Player::Left);
				else if(i > x) monster->direction(AI_Player::Right);
				if(j < y) monster->direction(AI_Player::Up);
				else if(j > y) monster->direction(AI_Player::Down);
			}
		}
		else foundtarget = false;

		if(!foundtarget)
		{
			// Randomized switch of direction
			if (monster->counter() == 3) {
				if (random(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;
					}
				}
			}

			// Randomized switch of direction
			if (monster->counter() == 4) {
				if (random(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);
			}
		}

		// Based on the direction, try to make a step.
		// Try adjacent direction if this fails.
		// Try opposite direction if even this fails.
		if (monster->direction() == AI_Player::Up) {
			if (walkableTile(x, y, x, y - 1)) {
				monster->move(x, y - 1);
				monster->direction(AI_Player::Up);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x + 1, y)) {
				monster->move(x + 1, y);
				monster->direction(AI_Player::Right);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x - 1, y)) {
				monster->move(x - 1, y);
				monster->direction(AI_Player::Left);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x, y + 1)) {
				monster->move(x, y + 1);
				monster->direction(AI_Player::Down);
				checkItem(monster, false);
			}
		} else if (monster->direction() == AI_Player::Down) {
			if (walkableTile(x, y, x, y + 1)) {
				monster->move(x, y + 1);
				monster->direction(AI_Player::Down);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x - 1, y)) {
				monster->move(x - 1, y);
				monster->direction(AI_Player::Left);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x + 1, y)) {
				monster->move(x + 1, y);
				monster->direction(AI_Player::Right);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x, y - 1)) {
				monster->move(x, y - 1);
				monster->direction(AI_Player::Up);
				checkItem(monster, false);
			}
		} else if (monster->direction() == AI_Player::Left) {
			if (walkableTile(x, y, x - 1, y)) {
				monster->move(x - 1, y);
				monster->direction(AI_Player::Left);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x, y - 1)) {
				monster->move(x, y - 1);
				monster->direction(AI_Player::Up);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x, y + 1)) {
				monster->move(x, y + 1);
				monster->direction(AI_Player::Down);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x + 1, y)) {
				monster->move(x + 1, y);
				monster->direction(AI_Player::Right);
				checkItem(monster, false);
			}
		} else if (monster->direction() == AI_Player::Right) {
			if (walkableTile(x, y, x + 1, y)) {
				monster->move(x + 1, y);
				monster->direction(AI_Player::Right);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x, y + 1)) {
				monster->move(x, y + 1);
				monster->direction(AI_Player::Down);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x, y - 1)) {
				monster->move(x, y - 1);
				monster->direction(AI_Player::Up);
				checkItem(monster, false);
			} else if (walkableTile(x, y, x - 1, y)) {
				monster->move(x - 1, y);
				monster->direction(AI_Player::Left);
				checkItem(monster, false);
			}
		}
	}
}

int PlayCanvas::random(int max)
{
	if(!max) return 0;
	return (rand() % max);
}

int PlayCanvas::isgoodrandom(int x, int y)
{
	QPtrList<Player> plist = playerList;
	QPtrList<AI_Player> mlist = monsterList;

	if((x < 0) || (y < 0) || (x >= m_level->width()) || (y >= m_level->height())) return -1;
	for (Player *player = plist.first(); player != NULL; player = plist.next())
		if((player->x() == x) && (player->y() == y)) return -3;
	for (AI_Player *monster = mlist.first(); monster != NULL; monster = mlist.next())
		if((monster->x() == x) && (monster->y() == y)) return -5;
	if(m_level->tile(x, y) == Level::ground) return 1;
	if(m_level->tile(x, y) == Level::bridge) return 1;
	return 0;
}

QPoint PlayCanvas::goodrandom(int max)
{
	Q_UNUSED(max);

	m_level->clearValues();

	for (int j = 0; j < m_level->height(); j++)
		for (int i = 0; i < m_level->width(); i++)
		{
			m_level->addValue(i, j, isgoodrandom(i, j) * 10);
			m_level->addValue(i, j, isgoodrandom(i + 1, j));
			m_level->addValue(i, j, isgoodrandom(i - 1, j));
			m_level->addValue(i, j, isgoodrandom(i, j + 1));
			m_level->addValue(i, j, isgoodrandom(i, j - 1));
			if((isgoodrandom(i - 1, j) > 0) || (isgoodrandom(i, j - 1) > 0)) m_level->addValue(i, j, isgoodrandom(i - 1, j - 1));
			if((isgoodrandom(i - 1, j) > 0) || (isgoodrandom(i, j + 1) > 0)) m_level->addValue(i, j, isgoodrandom(i - 1, j + 1));
			if((isgoodrandom(i + 1, j) > 0) || (isgoodrandom(i, j + 1) > 0)) m_level->addValue(i, j, isgoodrandom(i + 1, j + 1));
			if((isgoodrandom(i + 1, j) > 0) || (isgoodrandom(i, j - 1) > 0)) m_level->addValue(i, j, isgoodrandom(i + 1, j - 1));
		}

	QValueList<QPoint> freeCells;
	int limit = 18;
	while((limit > 0) && (freeCells.count() < 5))
	{
		for (int j = 0; j < m_level->height(); j++)
			for (int i = 0; i < m_level->width(); i++)
			{
				if(m_level->value(i, j) >= limit)
				{
					QPoint p;
					p.setX(i);
					p.setY(j);
					freeCells.append(p);
				}
			}
		limit--;
	}

	int pos = random(freeCells.count());
	//kdDebug() << "freecells has " << freeCells.count() << " elements, selected " << freeCells[pos].x() << "/" << freeCells[pos].y() << endl;
	return QPoint(freeCells[pos].x(), freeCells[pos].y());
}

Item *PlayCanvas::findItem(int x, int y)
{
	Item *item;

	for (item = itemList.first(); item != NULL; item = itemList.next())
		if (item->x() == x && item->y() == y)
			return item;
	
	return NULL;
}

Level *PlayCanvas::level()
{
	return m_level;
}

void PlayCanvas::enable(bool enabled)
{
	m_enabled = enabled;
	setTheme(resource->theme());
}

void PlayCanvas::enableLocal(bool enabled)
{
	m_localenabled = enabled;

	if(enabled)
	{
		createPlayer(0, 0, 0);
		createMonster();
		placeDynamic();
	}
}

void PlayCanvas::actionItem(int id, int type, int count)
{
	for(Player *player = playerList.first(); player; player = playerList.next())
		if(player->id() == id)
		{
			if(type == Level::itemLive) player->numLives(player->numLives() + count);
			else if(type == Level::itemBomb) player->numBombs(player->numBombs() + count);
			else if(type == Level::itemPower) player->power(player->power() + count);
			return;
		}
	for(AI_Player *monster = monsterList.first(); monster; monster = monsterList.next())
		if(monster->id() == id)
		{
			if(type == Level::itemLive) monster->numLives(monster->numLives() + count);
			else if(type == Level::itemBomb) monster->numBombs(monster->numBombs() + count);
			else if(type == Level::itemPower) monster->power(monster->power() + count);
			return;
		}
}

void PlayCanvas::removeItem(int id)
{
	for(Item *item = itemList.first(); item; item = itemList.next())
		if(item->id() == id)
		{
			if(!item->isActive())
			{
				m_level->setTile(item->x(), item->y(), Level::ground);
			}
			itemList.remove(item);
			return;
		}
}

void PlayCanvas::removeMonster(int id)
{
	for(AI_Player *monster = monsterList.first(); monster; monster = monsterList.next())
		if(monster->id() == id)
		{
			monsterList.remove(monster);
			return;
		}
}

void PlayCanvas::removePlayer(int id)
{
	for(Player *player = playerList.first(); player; player = playerList.next())
		if(player->id() == id)
		{
			playerList.remove(player);
			return;
		}
}

void PlayCanvas::removeBomb(int id)
{
	for(Bomb *bomb = bombList.first(); bomb; bomb = bombList.next())
		if(bomb->id() == id)
		{
			bombList.remove(bomb);
			return;
		}
}

void PlayCanvas::removeBlast(int id)
{
	for(Blast *blast = blastList.first(); blast; blast = blastList.next())
		if(blast->id() == id)
		{
			blastList.remove(blast);
			return;
		}
}

void PlayCanvas::activateItem(int id)
{
	for(Item *item = itemList.first(); item; item = itemList.next())
		if(item->id() == id)
		{
			item->activate(true);
			return;
		}
}

void PlayCanvas::activateExit()
{
	if(exit) exit->activate(true);
}

void PlayCanvas::highlightItem(int id)
{
	for(Item *item = itemList.first(); item; item = itemList.next())
		if(item->id() == id)
		{
			item->setHighlighted(true);
			return;
		}
}

void PlayCanvas::highlightExit()
{
	if(exit) exit->setHighlighted(true);
}

void PlayCanvas::setRules(Rule *rules)
{
	m_rules = rules;
}

bool PlayCanvas::enabled()
{
	return m_enabled;
}

void PlayCanvas::setOverlay(int overlay)
{
	if(overlay != none)
	{
		if(!overlayfont)
		{
			QFont f(QString::null, 48, QFont::Black);
			overlayfont = new QCanvasText("", f, this);
			overlayfont->move(width() / 2 - 100, height() / 2 - 24);
			overlayfont->setZ(0.2);
		}
	}
	if(!overlayfont) return;

	switch(overlay)
	{
		case loser:
			overlayfont->setColor(QColor(255, 0, 0));
			overlayfont->setText(i18n("Loser!"));
			break;
		case winner:
			overlayfont->setColor(QColor(0, 255, 0));
			overlayfont->setText(i18n("Winner!"));
			break;
		case pause:
			overlayfont->setColor(QColor(255, 230, 0));
			overlayfont->setText(i18n("Pause"));
			break;
		default:
			break;
	}

	if(overlay == none) overlayfont->hide();
	else overlayfont->show();
}

