#include "solitaire.h"
#include <QDebug>

short Solitaire::Size = 0;
quint64 Solitaire::Solution = 0;
quint64 Solitaire::Mask = 0;
const quint64 Solitaire::One = 1;
/*
Solitaire::Solitaire (const Solitaire& s) :  Hash(s.Hash)
{
//	int a = 12;
	Generation = s.Generation+1;
}
*/

Solitaire::Solitaire()
	: Solitaire("xx...xxxx...xx..........o..........xx...xxxx...xx")
{

}

Solitaire::Solitaire(QString config)
{
	if (config.isNull())
		config = "xx...xxxx...xx..........o..........xx...xxxx...xx";

	Size = config.size();

	if (Solution == 0)
	{
		Solution = fromString("xxoooxxxxoooxxoooooooooo.ooooooooooxxoooxxxxoooxx");
		//Solution = solution;
	}

	if (Mask == 0)
	{
		Mask = fromString("..ooo....ooo..ooooooooooooooooooooo..ooo....ooo..");
	}

	Hash = fromString(config);

	//Generation = 0;

}


quint64 Solitaire::fromString(QString s)
{
	quint64 hash = 0;

	Q_ASSERT(s.size() == Size);

	for (int i = 0; i < s.size(); ++i)
	{
		switch (s[i].toLatin1())
		{
			case '.': hash += (One << i); break;
			//case 'x': Mask += One << i; break;
			default: break;
		}
	}

	return hash;
}

void Solitaire::setField(int x,int y, FieldT f)
{
	short i = sqrt(Size)*y+x;

	switch (f)
	{
		case Pin: Hash = Hash | (One << i); break;
		default: Hash = Hash & ~(One << i); break;
	}
}

Solitaire::FieldT Solitaire::field(int i) const
{
	if (Mask & (One << i))
		return Forbidden;
	else if (Hash & (One << i))
		return Pin;
	else
		return Hole;
}
/*
bool Solitaire::solved(void) const
{
	return (Hash==Solution);
}
*/
Solitaire Solitaire::apply(int xsrc, int ysrc, int xdest, int ydest) const
{
	FieldT from = Pin;
	FieldT to = Hole;

	Solitaire copy = *this;

	if (copy.field(xdest, ydest)!=to)
		throw QString("dst != ")+to;

	if (copy.field(xsrc, ysrc)!=from)
		throw QString("src != ")+from;

	copy.setField(xdest, ydest, from);

	copy.setField(xsrc, ysrc, to);

	if (xsrc!=xdest) //waagerecht
		copy.setField((xdest+xsrc)/2,ysrc,to);

	if (ysrc!=ydest) //senkrecht
		copy.setField(xsrc,(ydest+ysrc)/2, to);

	return copy;
}

quint64 Backtrack::qHash(const Solitaire &key)
{
	return key.hash();
}

bool Solitaire::operator == (const Solitaire& other) const
{
	return Hash == other.Hash;
}

bool Solitaire::operator < (const Solitaire& other) const
{
	return Hash < other.Hash;
}
/*
void Solitaire::checkHashAndAdd(Solitaire* v, Trackable::ListT& l) const
{
	//l.push_back(v); return;


	//static QMap<Solitaire, short> AllVariants;
	static QSet<quint64> AllVariants;


	if (!AllVariants.contains(v->hash()))
	{
		//AllVariants[*v]=v->Generation;
		AllVariants.insert(v->hash());
		l.push_back(v);
	}
	else
	{
		delete v;
	}
}
*/
QList<Solitaire> Solitaire::variants(void) const
{
	//FieldT from = Hole;
	//FieldT to = Pin;

	const int l = sqrt(Size);
	QList<Solitaire> variants;

	int pins = 0;

	for (int i = 0; i < Size; ++i)
	{
		int x = i%l;
		int y = i/l;

		if (field(x, y) == Hole)
		{
			if ( (field(x-1,y) == Pin) && (field(x-2,y) == Pin) )
				variants << apply(x-2,y,x,y);

			if ( (field(x+1,y) == Pin) && (field(x+2,y) == Pin) )
				variants << apply(x+2,y,x,y);

			if ( (field(x,y-1) == Pin) && (field(x,y-2) == Pin) )
				variants << apply(x,y-2,x,y);

			if ( (field(x,y+1) == Pin) && (field(x,y+2) == Pin) )
				variants << apply(x,y+2,x,y);
		}
		else
			pins++;
	}

	//qDebug() << variants.count() << "variants" << pins << "pins";

	return variants;
}

QString Solitaire::diff(const Solitaire& other) const
{
	typedef QMap<int, char> fieldsT;
	static fieldsT map;
	if (map.isEmpty())
	{
		map[2]='a';
		map[3]='b';
		map[4]='c';

		map[9]='d';
		map[10]='e';
		map[11]='f';

		map[14]='g';
		map[15]='h';
		map[16]='i';
		map[17]='j';
		map[18]='k';
		map[19]='l';
		map[20]='m';

		map[21]='n';
		map[22]='o';
		map[23]='p';
		map[24]='x';
		map[25]='P';
		map[26]='O';
		map[27]='N';

		map[28]='M';
		map[29]='L';
		map[30]='K';
		map[31]='J';
		map[32]='I';
		map[33]='H';
		map[34]='G';

		map[37]='F';
		map[38]='E';
		map[39]='D';

		map[44]='C';
		map[45]='B';
		map[46]='A';
	}

	quint64 d = Hash^other.Hash;

	QString out;
	for (short i = 0; i < Size; ++i)
	{
		if ((d & (One << i)) > 0)
		{
			out+=map[i];
		}
	}

	return out;
}

QString Solitaire::toString(int mode) const
{
	const int l = sqrt(Size);

	QString out;
	for (int i = 0; i < Size; ++i)
	{
		out+=field(i);

		if (((i+1)%l==0) && (mode))
			out+="\n";
	}
	return out;
}
/*
Trackable* Solitaire::clone(void) const
{
	return new Solitaire(*this);
}
*/
Solitaire::FieldT Solitaire::field(int x,int y) const
{
	//static const FieldT forbidden = Fo
	static const int l = sqrt(Size);

	if (checkRange(x,y))
		return field(y*l+x);

	return Forbidden;
}

bool Solitaire::checkRange(int x, int y) const
{
	static const int l = sqrt(Size);

	if (x < 0)
		return false;

	if (y < 0)
		return false;

	if (x >= l)
		return false;

	if (y >= l)
		return false;

	return true;
}


