// Author: Denis Kozadaev - (c) 2017-2020


#include <cstdlib>

#include <tdeapplication.h>
#include <tqpainter.h>
#include <tqcursor.h>
#include <tqmessagebox.h>
#include <tqfiledialog.h>
#include <tqdatetime.h>
#include <tqwmatrix.h>

#include "gameboard.h"

#define	DELAY	10
#define	STEP	5

BoardItem::BoardItem(int n, TQWidget *parent, const char *name)
	:TQLabel(parent, name)
{
	num = n;
}

BoardItem::~BoardItem()
{
}

void
BoardItem::paintEvent(TQPaintEvent *e)
{
	TQPainter	*p;
	int		w = width() - 1,
			h = height() - 1;

	TQLabel::paintEvent(e);
	p = new TQPainter(this);

	p->setPen(TQt::black);
	p->drawRect(0, 0, w, h);
	p->setPen(TQt::magenta);
	p->drawText(0, 0, w, h, TQt::AlignCenter,
		TQString::number(item() + 1));

	delete p;
}

//------------------------------------------------------------------------------

GameBoard::GameBoard(TQWidget *parent, const char *name)
	:TQWidget(parent, name)
{
#include "cat.xpm"
	TQPixmap	xpm(cat_xpm);

	memset(map, 0, sizeof(map));
	nt = n = xt = yt = -1;
	dx = dy = 0;

	tmr = new TQTimer(this);
	TQObject::connect(tmr, SIGNAL(timeout()), this, SLOT(moveItem()));
	setEnabled(TRUE);
	origin = xpm;
}

GameBoard::~GameBoard()
{
	int	i;

	for (i = 0; i < 16; ++i)
		if (map[i] != NULL)
			delete map[i];

	delete tmr;
}


void
GameBoard::resizeEvent(TQResizeEvent *e)
{
	TQWidget::resizeEvent(e);
	initMap();
}


void
GameBoard::initMap()
{
	const int	num = 15;
	int	i, x, y,
		w = width() / 4,
		h = height() / 4;
	TQFont	fnt("Antiqua", 18);
	TQPixmap	xpm;

	if (!origin.isNull()) {
		TQWMatrix	mtx;
		mtx.scale((float)width() / (float)origin.width(),
			(float)height() / (float)origin.height());
		xpm = origin.xForm(mtx);
    }

	for (i = 0; i < num; ++i) {
		if (map[i] != NULL)
			delete map[i];
		map[i] = new BoardItem(i, this);
		map[i]->setFont(fnt);
		map[i]->resize(w, h);
		map[i]->setAlignment(TQt::AlignCenter);
		if (!xpm.isNull()) {
			y = (i >> 2);
			x = i - (y << 2);
			map[i]->move(x * w, y * h);
			TQPixmap	xpm1(map[i]->size());
			copyBlt(&xpm1, 0, 0, &xpm,
				map[i]->x(), map[i]->y(), w, h);
			map[i]->setPixmap(xpm1);
		}
	}
	if (map[num] != NULL)
		delete map[num];
	map[num] = NULL;
}


void
GameBoard::mousePressEvent(TQMouseEvent *e)
{
	int	x = e->x(),
		y = e->y(),
		i, it;
	
	i = index(x, y);
	if (mayMove(i) && (n == -1)) {
		for (it = 0; (it < 16) && (map[it] != NULL); ++it);
		startMoving(i, it);
	}
}


int
GameBoard::mayMove(int n)
{
	int	res = 0;

	if ((n >= 0) && (n < 16)) {
		switch (n) {
			case 0:
				res =	(map[n + 1] == NULL) ||
					(map[n + 4] == NULL);
				break;

			case 1:
			case 2:
				res =	(map[n - 1] == NULL) ||
					(map[n + 1] == NULL) ||
					(map[n + 4] == NULL);
				break;

			case 3:
				res =	(map[n - 1] == NULL) ||
					(map[n + 4] == NULL);
				break;

			case 4:
			case 8:
				res =	(map[n + 1] == NULL) ||
					(map[n - 4] == NULL) ||
					(map[n + 4] == NULL);
				break;

			case 5:
			case 6:
			case 9:
			case 10:
				res =	(map[n - 1] == NULL) ||
					(map[n + 1] == NULL) ||
					(map[n - 4] == NULL) ||
					(map[n + 4] == NULL);
				break;

			case 7:
			case 11:
				res =	(map[n - 1] == NULL) ||
					(map[n - 4] == NULL) ||
					(map[n + 4] == NULL);
				break;

			case 12:
				res =	(map[n + 1] == NULL) ||
					(map[n - 4] == NULL);
				break;

			case 13:
			case 14:
				res =	(map[n - 1] == NULL) ||
					(map[n + 1] == NULL) ||
					(map[n - 4] == NULL);
				break;

			case 15:
				res =	(map[n - 1] == NULL) ||
					(map[n - 4] == NULL);
				break;
		}
	}

	return (res);
}


int
GameBoard::index(int x, int y)
{
	int	ndx = -1,
		i, x1, y1;

	if ((map[0] != NULL) || (map[1] != NULL)) {
		for (i = 0; i < 16; ++i) {
			if (map[i] != NULL) {
				x1 = map[i]->x();
				y1 = map[i]->y();
				if ((x >= x1) && (y >= y1) &&
					(x < (x1 + map[i]->width())) &&
					(y < (y1 + map[i]->height()))) {
					ndx = i;
					break;
				}
			}
		}
	}

	return (ndx);
}


void
GameBoard::startMoving(int i, int t)
{
	n = i; nt = t;
	xt = (t - (t & ~3)) * map[n]->width();
	yt = (t >> 2) * map[n]->height();
	dx = step(xt, map[n]->x());
	dy = step(yt, map[n]->y());
	tmr->start(DELAY);
	TDEApplication::setOverrideCursor(TQCursor(TQt::WaitCursor));
}


int
GameBoard::step(int t, int f)
{
	int	res;

	if (t > f)
		res = STEP;
	else if (t < f)
		res = -STEP;
	else
		res = 0;

	return (res);
}


void
GameBoard::moveItem()
{
	int	x, y, stp, a;

	if ((n != -1) && (nt != -1) && (map[n] != NULL)) {
		x = map[n]->x();
		y = map[n]->y();
		stp = 0;
		if (dx != 0) {
			stp = (x == xt);
			if (!stp) {
				a = abs(x - xt);
				if (a < abs(dx))
					dx = sign(dx) * a;
				x += dx;
			}
		} else if (dy != 0) {
			stp = (y == yt);
			if (!stp) {
				a = abs(y - yt);
				if (a < abs(dy))
					dy = sign(dy) * a;
				y += dy;
			}
		}
		map[n]->move(x, y);
		if (stp) {
			map[nt] = map[n];
			map[n] = NULL;
			nt = n = -1;
			xt = yt = -1;
			dx = dy = 0;
			tmr->stop();
			TDEApplication::restoreOverrideCursor();
			checkEndGame();
		}
	}
}

int
GameBoard::sign(int x)
{
	int	res;

	if (x > 0)
		res = 1;
	else if (x < 0)
		res = -1;
	else
		res = 0;

	return (res);
}


void
GameBoard::checkEndGame()
{
	int	i, n;
	TQString	txt;

	for (n = i = 0; i < 15; ++i)
		if ((map[i] != NULL) && (map[i]->item() == i))
			n++;
	if (n == 15) {
		txt = tr("Game Over");
		TQMessageBox::information(this, txt, txt);
		setEnabled(FALSE);
	}
}


void
GameBoard::newGame()
{
	setEnabled(TRUE);
	initMap();
	newTask();
}


void
GameBoard::newTask()
{
	int		i, n, stp, x, y;
	BoardItem	*tmp;

	srandom(TQDateTime::currentDateTime().toTime_t());
	do {
		stp = (random() % 13) & 0xF;
		if ((stp & 1) == 0)
			stp++;
	} while (stp % 5 == 0);

	n = random() & 0xF;

	for (i = 0; i < 16; ++i) {
		n += stp;
		if (n > 15)
			n -= 16;
		tmp = map[i];
		map[i] = map[n];
		map[n] = tmp;
	}

	for (i = 0; i < 16; ++i)
		if (map[i] != NULL) {
			y = (i >> 2);
			x = i - (y << 2);
			map[i]->move(x * map[i]->width(), y * map[i]->height());
			map[i]->show();
		}
}


void
GameBoard::loadImage()
{
	TQString	str;
	TQPixmap	img;

	str = TQFileDialog::getOpenFileName(TQString::null, "*", this, NULL,
		tr("Open an image..."));

	if (!str.isEmpty()) {
		if (img.load(str)) {
			origin = img;
			initMap();
		} else
			TQMessageBox::critical(this, tr("Error loading..."),
				tr("Cannot load the image ") + str);
	}
}

#include "gameboard.moc"
