#include "blockviewwavetrace.h"
#include <QTime>


BlockViewWavetrace::BlockViewWavetrace(int width, int height) {
	max_steps = 512;
	resize(width, height);
	setPreferredDirection(BlockViewWavetrace::Horizontal);
}


void BlockViewWavetrace::resize(int width, int height) {
	wid = width;
	hei = height;
	if (field.size() != wid)
		field.resize(wid);
	for (int i = 0; i < wid; ++i) {
		if (field[i].size() != hei) {
			field[i].resize(hei);
			field[i].fill(-1);
		}
	}
}


void BlockViewWavetrace::fill(short val) {
	QTime tm; tm.restart();
	for (int i = 0; i < wid; ++i) {
		if (i == 0)
			field[i].fill(val);
		else
			memcpy(field[i].data(), field[0].constData(), hei * sizeof(field[0][0]));
	}
}


void BlockViewWavetrace::fill(const QRect & rect, short val) {
	for (int i = rect.left(); i <= rect.right(); ++i)
		for (int j = rect.top(); j <= rect.bottom(); ++j)
			field[i][j] = val;
}


bool BlockViewWavetrace::trace(const QPoint & start, const QPoint & finish) {
	st = start;
	fn = finish;
	if (dir_ == NoTrace) return true;
	//qDebug() << "trace" << start << finish;
	//return true;
	int cx, cy;
	short cl = 0, empty = (short)BlockViewWavetrace::Empty;
	QRect frect(0, 0, wid - 1, hei - 1);
	QVector<QPoint> cpnts, npnts;
	fill(st, cl);
	cpnts.push_back(st);
	if (field[fn.x()][fn.y()] == (short)BlockViewWavetrace::Blocked)
		return false;
	while (cpnts.size() > 0) {
		npnts.clear();
		cl++;
		if (cl >= max_steps) return false;
		for (int i = 0; i < cpnts.size(); ++i) {
			if (cpnts[i] == fn) {
				return true;
			}
			cx = cpnts[i].x() - 1;
			cy = cpnts[i].y();
			if (frect.contains(cx, cy)) {
				if (field[cx][cy] == empty) {
					npnts.push_back(QPoint(cx, cy));
					fill(cx, cy, cl);
				}
			}
			cx = cpnts[i].x() + 1;
			if (frect.contains(cx, cy)) {
				if (field[cx][cy] == empty) {
					npnts.push_back(QPoint(cx, cy));
					fill(cx, cy, cl);
				}
			}
			cx = cpnts[i].x();
			cy = cpnts[i].y() - 1;
			if (frect.contains(cx, cy)) {
				if (field[cx][cy] == empty) {
					npnts.push_back(QPoint(cx, cy));
					fill(cx, cy, cl);
				}
			}
			cy = cpnts[i].y() + 1;
			if (frect.contains(cx, cy)) {
				if (field[cx][cy] == empty) {
					npnts.push_back(QPoint(cx, cy));
					fill(cx, cy, cl);
				}
			}
		}
		cpnts = npnts;
		//qDebug() << cl << ": " << cpnts.size();
	}
	return false;
}


void BlockViewWavetrace::gatherPath() {
	path_.clear();
	path_.push_back(fn);
	if (dir_ == NoTrace) {
		path_.push_front(st);
		return;
	}
	int cx, cy;
	int pa = -1, ca = -1;
	bool first = true;
	short cl = field[fn.x()][fn.y()];
	QRect frect(0, 0, wid, hei);
	QPoint cpnt = fn;
	//cout << "start from " << cl << endl;
	while (cl > 0) {
		cl--;
		pa = ca;
		int dir = 0;
		cx = cpnt.x() + dps[dir].x();
		cy = cpnt.y() + dps[dir].y();
		dir++;
		if (frect.contains(cx, cy)) {
			if (field[cx][cy] == cl) {
				ca = QLineF(QPointF(cx, cy), cpnt).angle();
				if (ca != pa && !first)
					path_.push_front(cpnt);
				cpnt = QPoint(cx, cy);
				first = false;
				continue;
			}
		}
		cx = cpnt.x() + dps[dir].x();
		cy = cpnt.y() + dps[dir].y();
		dir++;
		if (frect.contains(cx, cy)) {
			if (field[cx][cy] == cl) {
				ca = QLineF(QPointF(cx, cy), cpnt).angle();
				if (ca != pa && !first)
					path_.push_front(cpnt);
				cpnt = QPoint(cx, cy);
				first = false;
				continue;
			}
		}
		cx = cpnt.x() + dps[dir].x();
		cy = cpnt.y() + dps[dir].y();
		dir++;
		if (frect.contains(cx, cy)) {
			if (field[cx][cy] == cl) {
				ca = QLineF(QPointF(cx, cy), cpnt).angle();
				if (ca != pa && !first)
					path_.push_front(cpnt);
				cpnt = QPoint(cx, cy);
				first = false;
				continue;
			}
		}
		cx = cpnt.x() + dps[dir].x();
		cy = cpnt.y() + dps[dir].y();
		if (frect.contains(cx, cy)) {
			if (field[cx][cy] == cl) {
				ca = QLineF(QPointF(cx, cy), cpnt).angle();
				if (ca != pa && !first)
					path_.push_front(cpnt);
				cpnt = QPoint(cx, cy);
				first = false;
				continue;
			}
		}
	}
	path_.push_front(st);
	//cout << path_.size() << endl;
}


void BlockViewWavetrace::setPreferredDirection(BlockViewWavetrace::Direction dir) {
	dir_ = dir;
	if (dir == BlockViewWavetrace::Horizontal) {
		dps[0] = QPoint(0, -1);
		dps[1] = QPoint(0, 1);
		dps[2] = QPoint(-1, 0);
		dps[3] = QPoint(1, 0);
	}
	if (dir == BlockViewWavetrace::Vertical) {
		dps[2] = QPoint(0, -1);
		dps[3] = QPoint(0, 1);
		dps[0] = QPoint(-1, 0);
		dps[1] = QPoint(1, 0);
	}
}


const QVector<QPoint> & BlockViewWavetrace::path() const {
	/*path_.resize(3);
	path_[0] = st;
	path_[1] = (st + fn) / 2;
	path_[2] = fn;*/
	return path_;
}
