#include "q2048view.h"
#include "q2048tile.h"
#include "q2048ai.h"

#include <QKeyEvent>
#include <QDebug>
#include <QMessageBox>

Q2048View::Q2048View(QWidget *parent) :
	QGraphicsView(parent), AI(this), Score(0), AutoPlay(false), Size(4), Width(40), Height(40), Border(3)
{
	setScene(new QGraphicsScene(this));

	scene()->addRect(QRectF(0,0,Size*Width+(Size+1)*Border, Size*Height+(Size+1)*Border), QPen(), QBrush(QColor("#BBADA0")))->setZValue(-3);

	for (int i = 0; i < Size*Size; ++i)
		scene()->addRect(tileRect(i/Size, i%Size), QPen(Qt::NoPen), QBrush(QColor("#CDC0B4")))->setZValue(-2);


	addTile();

	//if (AutoPlay)
		//keyPressEvent(0);
}

void Q2048View::keyPressEvent(QKeyEvent * event)
{
	static int shifts = 0;
	static int k = 0;

	int automove = AI.move(k);

	if (event)
	{
		k = event->key();

		if (k != automove)
			qDebug() << "human: " << key2txt(k) << "ai: " << key2txt(automove);
	}
	else
		k = automove;

	//qDebug() << k;

	switch (k)
	{
		case Qt::Key_Left: shifts = moveTiles(-1,0); qDebug() << "left"; break;
		case Qt::Key_Up: shifts = moveTiles(0,-1); qDebug() << "up"; break;
		case Qt::Key_Right: shifts = moveTiles(1,0); qDebug() << "right"; break;
		case Qt::Key_Down: shifts = moveTiles(0,1); qDebug() << "down"; break;
		default:  break;
	}


	if (shifts)
	{
		Q2048Tile* t = addTile();

		if (t==0)
			throw;

		//Q2048Tile::animate();

		//qDebug() << t->x() << t->y() << t->value();
	}
	else
		qDebug() << "dead move";

	if (event)
		QGraphicsView::keyPressEvent(event);
}

void Q2048View::resizeEvent(QResizeEvent * /*event*/)
{
	fitInView(scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
}

int Q2048View::moveTiles(int dx, int dy)
{
	int x,y;
	int shifts = 0;

	for (int outer = 0; outer < Size; ++outer)
	{
		int nulls = 0;
		Q2048Tile* lasttile = 0;
		for (int inner = 0; inner < Size; ++inner)
		{

			y = outer;
			x = inner;

			if (dx==0)
				std::swap (x,y);

			y = (dy>0) ? Size-y-1 : y;
			x = (dx>0) ? Size-x-1 : x;

			Q2048Tile* tile = Q2048Tile::getTile(x,y);

			if ( (lasttile) && (tile) && (lasttile->value() == tile->value()) ) //merge?
			{
				shifts++;
				//delete tile;

				int sx = lasttile->getX() - tile->getX();
				int sy = lasttile->getY() - tile->getY();

				//qDebug() << sx << sy << dx << dy;

				tile->shift(sx, sy);
				tile->setZValue(-1);
				lasttile->grow();

				if (lasttile->value()==2048)
					QMessageBox::information(this, "You Win!", "You Win!");

				score(lasttile->value());

				tile = 0;
				lasttile = 0;
			}

			if (tile)
				lasttile = tile;

			if (tile==0)
				nulls++;
			else
			{
				if (nulls)
				{
					shifts++;
					tile->shift(nulls*dx, nulls*dy);
				}
			}
		}
	}

	return shifts;
}

QRectF Q2048View::tileRect(int x, int y) const
{
	return QRectF (Border+x*Width+x*Border,Border+y*Height+y*Border,Width,Height);

}

Q2048Tile* Q2048View::addTile()
{
	if (Q2048Tile::count() == Size*Size)
		return 0;

	int rnd, x, y;

	do
	{
		rnd = Size*Size*(qrand()/(float)RAND_MAX);
		x = rnd % Size;
		y = rnd / Size;

	} while (Q2048Tile::getTile(x, y));



	Q2048Tile* t = new Q2048Tile(x, y, this);

	if (gameOver())
	{
		QMessageBox::information(this, "Game Over!", "Game Over!");
	}

	return t;
}

void Q2048View::deleteMerged()
{
	QList<QGraphicsItem*> items = scene()->items();

	foreach (QGraphicsItem* i, items)
	{
		Q2048Tile* t = dynamic_cast <Q2048Tile*> (i);
		if ((t) && (t->zValue()==-1))
		{
			t->deleteLater();
			//delete i;
		}

	}

	if (AutoPlay)
		keyPressEvent(0);
}

int Q2048View::score(int s)
{
	if (s)
		Score+=s;
	else
		Score = 0;

	emit newScore(QString("Score: %1").arg(Score));

	return Score;
}

bool Q2048View::gameOver(void) const
{
	if (Q2048Tile::count() == Size*Size)
	{
		for (int outer = 0; outer < Size*Size; ++outer)
		{
			Q2048Tile* tile1 = Q2048Tile::getTile(outer%Size,outer/Size);

			for (int inner = 0; inner < Size*Size; ++inner)
			{
				Q2048Tile* tile2 = Q2048Tile::getTile(inner%Size,inner/Size);
				//if (tile2 && tile1)
				//{

				int diffx = tile1->getX()-tile2->getX();
				int diffy = tile1->getY()-tile2->getY();
				if ( ((qAbs(diffx) + qAbs(diffy))==1) && (tile1->value()==tile2->value()) )
				{
					return false;
				}
				//}
			}
		}

		return true;
	}

	return false;
}

void Q2048View::newGame()
{
	QList<QGraphicsItem*> items = scene()->items();

	foreach (QGraphicsItem* i, items)
	{
		Q2048Tile* t = dynamic_cast <Q2048Tile*> (i);
		if (t)
		{
			t->removeTile();
			t->deleteLater();
		}
	}

	score(0);
	Q2048Tile* init  = addTile();

	if (init==0)
		throw;
}


void Q2048View::setAutoPlay(bool b)
{
	AutoPlay = b;

	if (AutoPlay)
		keyPressEvent(0);
}

QString Q2048View::key2txt (int k)
{
	switch (k)
	{
		case Qt::Key_Left: return "left"; break;
		case Qt::Key_Up: return "up"; break;
		case Qt::Key_Right: return "right"; break;
		case Qt::Key_Down: return "down"; break;
		default: return "unknown"; break;
	}
}

