#include "map.h"

Map::Map(QByteArray data, QSize size, QString name, int maxPlayers, int image, QObject *parent) :
		QObject(parent)
{
	QByteArray ba = QByteArray::fromBase64(data);
	ba = qUncompress(ba);
	QDataStream s(ba);
	mapName = name;
	s >> Cells;
	int chei;
	if (Cells.size() <= 0)
		qFatal("Map has invalid width");
	if (Cells.size() != size.width())
		qCritical("Map has wrong width");
	mapSize.setWidth(Cells.size());
	chei = Cells.at(0).size();
	for (int i=0; i<Cells.size(); i++)
	{
		if (Cells[i].size() <= 0)
			qFatal("Map has invalid height");
		if (Cells[i].size() != size.height())
		{
			qCritical("Map has wrong height");
			if (Cells[i].size() != chei)
				qFatal("Corrupted map data");
		}
		for (int j=0; j<Cells[i].size(); j++)
		{
			if (Cells[i][j]==Start) starts.push_back(QPoint(i,j));
			if (Cells[i][j]==Finish) finishs.push_back(QPoint(i,j));
		}
	}
	if (starts.isEmpty() || finishs.isEmpty()) qFatal("invalid map, not found start/finish");
	// TODO: check for maxPlayers
	players = maxPlayers;
	imageType = image;
}


//void Map::ReadSettings()
//{
//	QString str;
//	SettingsReader * sreader = new SettingsReader("map.conf");
//	str = sreader->getValue("cells path");
//	maxPlayers = sreader->getValue("max players","1").toInt();
//	QFile  * f = new QFile(str);
//	f->open(QIODevice::ReadOnly);
//	QDataStream s(f);
//	s >> Cells;
//	if (Cells.size()>0)
//	{
//		mapsize.setWidth(Cells.size());
//		mapsize.setHeight(Cells[0].size());
//		//qDebug() << "Map size" << mapsize;
//		//printMap();
//	} else qDebug("invalid size");
//	delete f;
//	delete sreader;
//}


void Map::printMap()
{
	for (int i=0; i<Cells.size(); i++) qDebug()<<Cells.at(i);
}


void Map::removeAliensPath()
{
	for (int i=0; i<Cells.size(); i++)
	{
		for (int j=0; j<Cells[i].size(); j++)
		{
			if (Cells[i][j] > 1000 && Cells[i][j] < 2000) Cells[i][j]=Cells[i][j]-1000;
		}
	}
}

bool Map::addTowerOnMap(int playerId, QPoint pos)
{
	if (playerId < 0 || playerId > players) return false;
	QRect r(QPoint(),mapSize);
	if (!r.contains(pos)) return false;
	if (Cells[pos.x()][pos.y()]==Player+playerId)
	{
		Cells[pos.x()][pos.y()]=PlayerTower-playerId;
		return true;
	}
	if (Cells[pos.x()][pos.y()]==PlayerAlien+playerId)
	{
		Cells[pos.x()][pos.y()]=PlayerTower-playerId;
		bool ok=true;
		for (int i=0; i<starts.size(); i++)
			for (int j=0; j<finishs.size(); j++)
				if (WaveTrace(starts.at(i),finishs.at(j))<0) ok = false;
		qDebug()<<"ok="<<ok;
		if (!ok)
		{
			Cells[pos.x()][pos.y()]=PlayerAlien+playerId;
			return false;
		}
		//Cells[pos.x()][pos.y()]=PlayerTower-playerId;
		bool * pathOK;
		pathOK = new bool();
		*pathOK = true;
		qDebug("emit");
		emit RecreateAlienPath(pathOK);
		qDebug() << *pathOK;
		if (!pathOK)
		{
			Cells[pos.x()][pos.y()]=PlayerAlien+playerId;
			emit RecreateAlienPath(pathOK);
			return false;
		}
		return true;
	}
	qDebug()<<"Dont buid here" << Cells[pos.x()][pos.y()];
	return false;
}


//void Map::CreateMapExample()
//{
//	Cells.resize(16);
//	for (int i=0; i<Cells.size(); i++)
//	{
//		Cells[i].resize(16);
//		Cells[i][0]=Wall;
//		Cells[i][Cells[i].size()-1]=Wall;
//	}
//	for (int i=0; i<Cells.size(); i++)
//	{
//		for (int j=0; j<Cells[i].size(); j++)
//		{
//			Cells[0][j]=Wall;
//			Cells[Cells.size()-1][j]=Wall;
//		}
//	}
//	for (int i=1; i<Cells.size()/2; i++)
//		for (int j=1; j<Cells[i].size()-1; j++)
//			Cells[i][j]=Player;
//	for (int i=Cells.size()/2; i<Cells.size()-1; i++)
//		for (int j=1; j<Cells[i].size()-1; j++)
//			Cells[i][j]=Player+1;
//	for (int i=2; i<Cells.size()-2; i++)
//		Cells[i][Cells[i].size()/2] = Wall;
//	Cells[Cells.size()/2][0]=Start;
//	Cells[Cells.size()/2-1][0]=Start;
//	Cells[Cells.size()/2][Cells[Cells.size()/2].size()-1]=Finish;
//	Cells[Cells.size()/2-1][Cells[Cells.size()/2].size()-1]=Finish;
//	QFile  * f = new QFile("example16x16_64.map");
//	f->open(QIODevice::ReadWrite);
//	f->resize(0);
//	QByteArray ba;
//	QDataStream tmps(&ba,QIODevice::ReadWrite);
//	tmps << Cells;
//	ba = qCompress(ba);
//	ba = ba.toBase64();
//	QString str(ba);
////	QDataStream s(f);
////	s << ba;
//	f->write(str.toUtf8());
//	f->close();
//	delete f;
//}


QVector<QPointF> Map::CreatePath(QPoint start, QPoint finish)
{
	QPointF tp;
	QVector<QPoint> srcPath;
	QVector<QPointF> tmpPath;
	QVector<QPointF> path;
	//PathIndex = 1;
	if (Cells[start.x()][start.y()] < 0) qDebug("ERROR invalid start"),exit(-1);
	if (Cells[finish.x()][finish.y()] < 0) qDebug("ERROR invalid finish"),exit(-1);
	srcPath = InvWaveTrace(finish,WaveTrace(start,finish));
	for (int i=0; i<srcPath.size();i++)
	{
		int x = Cells.at(srcPath.at(i).x()).at(srcPath.at(i).y());
		if (x > 0 && x < 1000) x = x+1000;
		Cells[srcPath.at(i).x()][srcPath.at(i).y()] = x;
		tmpPath.push_back(QPointF(srcPath.at(i)));
	}
	srcPath.clear();
	//qDebug() << tmpPath.size();
	if (!tmpPath.isEmpty())
	{
		for (int j=0; j<4; j++)
		{
			path.clear();
			tp = tmpPath.at(0);
			path.push_back(tp);
			for (int i = 0; i < tmpPath.size() - 1; i++)
			{
				//if (j > 1) path.push_back(tmpPath[i]);
				tp.setX((tmpPath.at(i).x() + tmpPath.at(i + 1).x()) / 2.0);
				tp.setY((tmpPath.at(i).y() + tmpPath.at(i + 1).y()) / 2.0);
				path.push_back(tp);
			}
			tp = tmpPath.at(tmpPath.size() - 1);
			path.push_back(tp);
			tmpPath = path;
		}
		tmpPath.clear();
		if (path.size() >= 6)
		{
			path.remove(1);
			path.remove(1);
			path.remove(path.size()-2);
			path.remove(path.size()-2);
		}
		return path;
	}
	path.clear();
	return path;
}


int Map::WaveTrace(QPoint start, QPoint finish)
{
	bool stop = false;
	int step = 2;
	QPoint cp, tp;
	QRect fr(QPoint(), mapSize);
	QVector<QPoint> tmpp, curp;
	TmpCells << Cells;
	for (int i = 0; i < Cells.size(); i++)
	{
		for (int j = 0; j < Cells.at(i).size(); j++)
		{
			if (Cells[i][j] < 0 ) TmpCells[i][j] = -1;
			else TmpCells[i][j] = 0;
		}
	}
	cp = start;
	curp.push_back(cp);
	TmpCells[cp.x()][cp.y()] = 1;
	//qDebug() << "trace";
	while (!stop) {
		tmpp = curp;
		curp.clear();
		stop = true;
		for (int i = 0; i < tmpp.size(); i++) {
			cp = tmpp.at(i);
			if (cp == finish) {
				TmpCells[cp.x()][cp.y()] = step;
				//qDebug() << "trace done!";
				return step;
			}
			tp.setX(cp.x() - 1);
			tp.setY(cp.y());
			if (fr.contains(tp) && TmpCells[tp.x()][tp.y()] == 0) {
				TmpCells[tp.x()][tp.y()] = step;
				curp.push_back(tp);
				stop = false;
			}
			tp.setX(cp.x() + 1);
			if (fr.contains(tp) && TmpCells[tp.x()][tp.y()] == 0) {
				TmpCells[tp.x()][tp.y()] = step;
				curp.push_back(tp);
				stop = false;
			}
			tp.setX(cp.x());
			tp.setY(cp.y() - 1);
			if (fr.contains(tp) && TmpCells[tp.x()][tp.y()] == 0) {
				TmpCells[tp.x()][tp.y()] = step;
				curp.push_back(tp);
				stop = false;
			}
			tp.setY(cp.y() + 1);
			if (fr.contains(tp) && TmpCells[tp.x()][tp.y()] == 0) {
				TmpCells[tp.x()][tp.y()] = step;
				curp.push_back(tp);
				stop = false;
			}
		}
		step++;
	}
	qDebug() << "trace false";
	//qDebug() << TmpCells;
	return -1;
}


QVector<QPoint> Map::InvWaveTrace(QPoint finish, int cnt)
{
	QPoint wp, Ppnt;
	QVector<QPoint> alpath;
	if (cnt < 2) return alpath;
	int Ind, c, xpp, ypp, xnn, ynn;
	unsigned char chk;
	Ppnt = wp = finish;
	xnn=0;
	xpp=0;
	ynn=0;
	ypp=0;
	cnt--;
	alpath.push_front(Ppnt);
	while (cnt > 1)
	{
		cnt--;
		chk = 0;
		Ind = 0;
		c = 0;
		if (wp.x() - 1 >= 0 && TmpCells[wp.x()-1][wp.y()] == cnt)
		{
			chk = chk | 0x01;
			c++;
		}
		if (wp.x() + 1 < mapSize.width() && TmpCells[wp.x()+1][wp.y()] == cnt)
		{
			chk = chk | 0x02;
			c++;
		}
		if (wp.y() - 1 >= 0 && TmpCells[wp.x()][wp.y()-1] == cnt)
		{
			chk = chk | 0x04;
			c++;
		}
		if (wp.y() + 1 < mapSize.height() && TmpCells[wp.x()][wp.y()+1] == cnt)
		{
			chk = chk | 0x08;
			c++;
		}
		if (c == 0 || chk == 0) qDebug("ERROR in InvWaveTrace"),exit(-1);
		if (c > 1)
		{
			if ((chk & 0x01)==0x01 && (chk & 0x04)==0x04)
			{
				if (xnn <= ynn && Ind == 0){
					wp.rx()--;
					xnn++;
					if (xnn == ynn) xnn++;
					Ind = 1;
				} else if (Ind == 0) {
					wp.ry()--;
					ynn++;
					ynn++;
					Ind = 1;
				}
			}
			if ((chk & 0x02)==0x02 && (chk & 0x04)==0x04)
			{
				if (xpp <= ynn && Ind == 0){
					wp.rx()++;
					xpp++;
					if (xpp == ynn) xpp++;
					Ind = 1;
				} else if (Ind == 0) {
					wp.ry()--;
					ynn++;
					ynn++;
					Ind = 1;
				}
			}
			if ((chk & 0x01)==0x01 && (chk & 0x08)==0x08)
			{
				if (xnn <= ypp && Ind == 0){
					wp.rx()--;
					xnn++;
					if (xnn == ypp) xnn++;
					Ind = 1;
				} else if (Ind == 0) {
					wp.ry()++;
					ypp++;
					ypp++;
					Ind = 1;
				}
			}
			if ((chk & 0x02)==0x02 && (chk & 0x08)==0x08)
			{
				if (xpp <= ypp && Ind == 0){
					wp.rx()++;
					xpp++;
					if (xpp == ypp) xpp++;
					Ind = 1;
				} else if (Ind == 0) {
					wp.ry()++;
					ypp++;
					ypp++;
					Ind = 1;
				}
			}
		}
		if (c == 1 || Ind == 0)
		{
			xnn=0;
			xpp=0;
			ynn=0;
			ypp=0;
			if ((chk & 0x01)==0x01) {
				wp.rx()--;
				xnn++;
			}
			else if ((chk & 0x02)==0x02) {
				wp.rx()++;
				xpp++;
			}
			else if ((chk & 0x04)==0x04) {
				wp.ry()--;
				ynn++;
			}
			else if ((chk & 0x08)==0x08) {
				wp.ry()++;
				ypp++;
			}
		}
		Ppnt = wp;
		alpath.push_front(Ppnt);
	}
	return alpath;
}

