#include "cube36.h"
#include <QDebug>
#include <QCryptographicHash>

using namespace Backtrack;

Cube36::Cube36(bool solvable)
{
	bool initspecialpins;

	if (solvable)
	{
		TrackSpecialPins = false;
		initspecialpins = !TrackSpecialPins;
	}
	else
	{
		TrackSpecialPins = false;
		initspecialpins = false;
	}


	//-------------------------------------
	Pins.push_back(Pin(Pin::Undefined, 0));
	Pins.push_back(Pin(Pin::Undefined, 2));
	Pins.push_back(Pin(Pin::Undefined, 5));
	Pins.push_back(Pin(Pin::Undefined, 4));
	Pins.push_back(Pin(Pin::Undefined, 3));
	Pins.push_back(Pin(Pin::Undefined, 1));
	//-------------------------------------
	Pins.push_back(Pin(Pin::Undefined, 3));
	Pins.push_back(Pin(Pin::Undefined, 1));
	Pins.push_back(Pin(Pin::Undefined, 4));

	if (initspecialpins)
		Pins.push_back(Pin(Pin::Yellow, 1));//
	else
		Pins.push_back(Pin(Pin::Undefined, 0));

	Pins.push_back(Pin(Pin::Undefined, 5));
	Pins.push_back(Pin(Pin::Undefined, 2));
	//-------------------------------------
	Pins.push_back(Pin(Pin::Undefined, 4));
	Pins.push_back(Pin(Pin::Undefined, 5));
	Pins.push_back(Pin(Pin::Undefined, 2));
	Pins.push_back(Pin(Pin::Undefined, 3));
	Pins.push_back(Pin(Pin::Undefined, 1));
	Pins.push_back(Pin(Pin::Undefined, 0));
	//-------------------------------------
	Pins.push_back(Pin(Pin::Undefined, 2));
	Pins.push_back(Pin(Pin::Undefined, 0));
	Pins.push_back(Pin(Pin::Undefined, 3));

	if (initspecialpins)
		Pins.push_back(Pin(Pin::Orange, 0));//
	else
		Pins.push_back(Pin(Pin::Undefined, 1));

	Pins.push_back(Pin(Pin::Undefined, 4));
	Pins.push_back(Pin(Pin::Undefined, 5));
	//-------------------------------------
	Pins.push_back(Pin(Pin::Undefined, 1));
	Pins.push_back(Pin(Pin::Undefined, 3));
	Pins.push_back(Pin(Pin::Undefined, 0));
	Pins.push_back(Pin(Pin::Undefined, 5));
	Pins.push_back(Pin(Pin::Undefined, 2));
	Pins.push_back(Pin(Pin::Undefined, 4));
	//-------------------------------------
	Pins.push_back(Pin(Pin::Undefined, 5));
	Pins.push_back(Pin(Pin::Undefined, 4));
	Pins.push_back(Pin(Pin::Undefined, 1));
	Pins.push_back(Pin(Pin::Undefined, 2));
	Pins.push_back(Pin(Pin::Undefined, 0));
	Pins.push_back(Pin(Pin::Undefined, 3));
}

Cube36::Pin Cube36::pin(int x,int y) const
{
	return Pins[y*6+x];
}

bool Cube36::solved(void) const
{
	for (int p = 0; p < Pins.size(); ++p)
	{
		if (Pins[p].Col==Pin::Undefined)
			return false;
	}
	return true;
}

QList <Cube36> Cube36::variants(void) const
{
	//if (!Variantscached)
	//{
		QList <Cube36> output;

		PinVecT peacesleft = peacesLeft();

		if (peacesleft.count() == 0)
			qDebug() << this->toString();

		for (int p = 0; p < Pins.size(); ++p)
		{
			if (Pins[p].Col==Pin::Undefined)
			{
				for (int l = 0; l < peacesleft.size(); ++l)
				{
					if (
						(Pins[p].Len==peacesleft[l].Len) ||
						( (TrackSpecialPins) && (p==9) && (peacesleft[l].Len==1) && (peacesleft[l].Col==Pin::Yellow) ) || //gelber 5er kann auf pos. 10
						( (TrackSpecialPins) && (p==21) && (peacesleft[l].Len==0) && (peacesleft[l].Col==Pin::Orange) ) //oranger 6er kann auf pos. 22
						)
					{
						bool validcolor=true;

						for (int pp = 0; pp < Pins.size(); ++pp)
						{
							if (
								((pp/6 == p/6) || (pp%6 == p%6)) &&
								(Pins[pp].Col==peacesleft[l].Col)
							)
							{
								validcolor=false;
								break;
							}
						}

						if (validcolor)
						{
							Cube36 c (*this);
							//PinVecT copy(Pins);
							c.Pins[p] = peacesleft[l];
							output.push_back(c);
						}
					}
				}

				break;
			}
		}

		//Variantscached=true;
	//}
	//qDebug() << out.size() << " variants";
	return output;
}

QString Cube36::toString(void) const
{
	QString out;
	int u = 0;
	int i = 0;

	for (i = 0; i < Pins.size(); ++i)
	{
		if ((i%6==0) && (i!=0))
		{
			//out+="\n";
		}

		if (Pins[i].Col==Pin::Undefined)
			u++;

		out+=pinToString(Pins[i]);
	}

	//QString hash = QCryptographicHash::hash(out.toAscii(), QCryptographicHash::Md5).toHex();

	return out;// + QString().sprintf(" (%d/%d) #", i-u, i) + hash;
}

QString Cube36::pinToString(Pin p) const
{
	QString out;

	switch (p.Col)
	{
		case Pin::Undefined: out="*"; break;
		case Pin::Red: out="R"; break;
		case Pin::Orange: out="O"; break;
		case Pin::Yellow: out="Y"; break;
		case Pin::Green: out="G"; break;
		case Pin::Blue: out="B"; break;
		case Pin::Violet: out="V"; break;
	}

	out+=QString::number(p.Len);

	return out + " ";
}

Cube36::PinVecT Cube36::peacesLeft(void) const
{
	PinVecT left;

	for (unsigned int l = 0; l < 6; l++)
	{
		for (unsigned int c = 1; c < 7; c++)
		{
			left.push_back(Pin((Pin::ColorT)c, l));
		}
	}

	for (int i = 0; i < Pins.size(); ++i)
	{
		int pos = -1;

		if ((pos=left.indexOf(Pins[i])) > -1)
		{
			left.remove(pos);
		}
		else
		{
			//qDebug() << "not found";
		}
	}

	//qDebug() << "left: " << Cube36(left).toString();

	return left;
}

bool Cube36::Pin::operator== (const Cube36::Pin& b) const
{
	if ((Col==b.Col) && (Len==b.Len))
		return true;

	return false;
}

bool Cube36::operator == (const Cube36& other) const
{
	if (Pins.size()!=other.Pins.size())
		return false;

	for (int i = 0; i < Pins.size(); ++i)
	{
		if (Pins[i]!=other.Pins[i])
			return false;
	}

	return true;
}
