/*
 * ImageHolder.cpp
 *
 *  Created on: Oct 5, 2011
 *      Author: gapchich
 */

#include "ImageHolder.h"
#include "functions.h"

#include <QKeyEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QListWidgetItem>
#include <qmath.h>
#include <QDebug>

ImageHolder::ImageHolder(QWidget *aParent)
	: QLabel(aParent)
{
	repaint_needed_ = 0;
	tool_ = NoTool;
	state_ = StandBy;
	keyboard_modifier_ = Qt::NoModifier;

	focused_selection_ = -1;
	focused_selection_type_ = NoFigure;

	hovered_point_.figure = NoFigure;
	hovered_point_.figureID = -1;
	hovered_point_.pointID = -1;

	list_bounding_box_ = 0;
	main_label_ = 0;
	//list_bounding_box_ = new QList< QRect >;

	scale_ = 1;

	point_radius_ = 6;

	setScaledContents(true);
	setMouseTracking(true);
}

ImageHolder::~ImageHolder()
{
	//list_bounding_box_->clear();
	//delete list_bounding_box_;
}

void
ImageHolder::paintEvent(QPaintEvent *anEvent)
{
	QLabel::paintEvent(anEvent);

	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing);
	//painter.setRenderHint(QPainter::SmoothPixmapTransform);
	QPen pen;

	if (NoTool != tool_) {
		pen.setWidth(1);
		pen.setColor(QColor(Qt::black));
		pen.setStyle(Qt::DashLine);
		painter.setPen(pen);

		if (BoundingBoxTool == tool_) {
			/* scaling */
			QRect bbox = bounding_box_.rect;
			QPoint bboxTopLeft = bbox.topLeft() * scale_;
			QPoint bboxBottomRight = bbox.bottomRight() * scale_;

			bbox.setTopLeft(bboxTopLeft);
			bbox.setBottomRight(bboxBottomRight);

			painter.drawRect(bbox);
		}
		else if (PolygonTool == tool_) {
			/* scaling */
			QPoint point;
			QPolygon poly = polygon_.poly;
			for (int i = 0; i < poly.size(); i++) {
				point.setX(poly.at(i).x());
				point.setY(poly.at(i).y());
				point *= scale_;
				poly.remove(i);
				poly.insert(i, point);
			}
			painter.drawPolygon(poly);
		}
//		switch(tool_) {
//		case BoundingBoxTool:
//			painter.drawRect();
//			break;
//		case PolygonTool:
//			painter.drawPolygon(polygon_.poly);
//			break;
//		default:
//			break;
//		}

	}

	/* drawing bounding boxes */
	drawBoundingBoxes(&painter, &pen);
	drawPolygons(&painter, &pen);
}

void
ImageHolder::drawBoundingBoxes(
	QPainter *aPainter,
	QPen *aPen
)
{
	/* FIXME: hardcoded colors */
	if (0 == list_bounding_box_)
	{
		return;
		/* NOTREACHED */
	}

	Qt::PenStyle penStyle = Qt::SolidLine;
	int width = 1;
	/* confirmed boxes */
	for (int i = 0; i < list_bounding_box_->size(); i++) {
		int labelID = list_bounding_box_->at(i)->label_ID_;

		if (labelID < list_label_color_->count())
			aPen->setColor(QColor(list_label_color_->at(labelID)));
		else
			aPen->setColor(QColor(Qt::white));

		/* checking whether labeled area is of main object or not */
		if (labelID == *main_label_)
			width = 3;
		else
			width = 1;

		if (RectFigure == focused_selection_type_ &&
			focused_selection_ == i) {
			penStyle = Qt::DotLine;
			width = 2;
		}

		/* scaling */
		QRect rect = list_bounding_box_->at(i)->rect.normalized();
		QPoint topLeft = rect.topLeft() * scale_;
		QPoint bottomRight = rect.bottomRight() * scale_;

		rect.setTopLeft(topLeft);
		rect.setBottomRight(bottomRight);

		if (focused_selection_ == i &&
			focused_selection_type_ == RectFigure) {
			QPen circPen;
			circPen.setWidth(2);
			circPen.setStyle(Qt::SolidLine);
			circPen.setColor(aPen->color());
			aPainter->setPen(circPen);
			for (int j = 0; j < 4; j++) {
				QPoint point;
				if (!j) {
					point = rect.topLeft();
				}
				else if (1 == j)
				{
					point = rect.topRight();
				}
				else if (2 == j)
				{
					point = rect.bottomRight();
				}
				else if (3 == j)
				{
					point = rect.bottomLeft();
				}
				if (i == hovered_point_.figureID &&
					j == hovered_point_.pointID &&
					RectFigure == hovered_point_.figure) {
					QBrush brush;
					brush.setColor(aPen->color());
					brush.setStyle(Qt::SolidPattern);
					aPainter->setBrush(brush);
				}
				aPainter->drawEllipse(point, point_radius_, point_radius_);
				aPainter->setBrush(Qt::NoBrush);
			}
		}

		aPen->setWidth(width);
		aPen->setStyle(penStyle);
		aPainter->setPen(*aPen);

		aPainter->drawRect(rect);
		/* drawing label ids of these boxes */
		QString labelIDText =
			QString("%1").arg(labelID);


		aPainter->drawText(
			rect.left() + 5,
			rect.top() + 5,
			20,
			20,
			Qt::AlignLeft,
			labelIDText
			);
	}

}

void
ImageHolder::drawPolygons(
	QPainter *aPainter,
	QPen *aPen
)
{
	/* FIXME: hardcoded colors */
	if (0 == list_polygon_)
	{
		return;
		/* NOTREACHED */
	}

	Qt::PenStyle penStyle = Qt::SolidLine;
	int width = 1;
	/* confirmed polygons */
	for (int i = 0; i < list_polygon_->size(); i++) {
		penStyle = Qt::SolidLine;
		int labelID = list_polygon_->at(i)->label_ID_;

		if (labelID < list_label_color_->count())
			aPen->setColor(QColor(list_label_color_->at(labelID)));
		else
			aPen->setColor(QColor(Qt::white));

		/* checking whether labeled area is of main object or not */
		if (labelID == *main_label_)
			width = 3;
		else
			width = 1;

		if (PolyFigure == focused_selection_type_ &&
			focused_selection_ == i) {
			penStyle = Qt::DotLine;
			width = 2;
		}

		QPoint point;
		QPolygon poly = list_polygon_->at(i)->poly;
		for (int j = 0; j < poly.size(); j++) {
			point.setX(poly.at(j).x());
			point.setY(poly.at(j).y());
			point *= scale_;
			poly.remove(j);
			poly.insert(j, point);

			if (focused_selection_ == i &&
				focused_selection_type_ == PolyFigure) {
				QPen circPen;
				circPen.setWidth(2);
				circPen.setStyle(Qt::SolidLine);
				circPen.setColor(aPen->color());
				aPainter->setPen(circPen);
				if (j == hovered_point_.pointID &&
					i == hovered_point_.figureID &&
					PolyFigure == hovered_point_.figure) {
					QBrush brush;
					brush.setColor(aPen->color());
					brush.setStyle(Qt::SolidPattern);
					aPainter->setBrush(brush);
				}
				aPainter->drawEllipse(point, point_radius_, point_radius_);
				aPainter->setBrush(Qt::NoBrush);
			}
		}

		aPen->setWidth(width);
		aPen->setStyle(penStyle);
		aPainter->setPen(*aPen);

		aPainter->drawPolygon(poly);
		/* drawing label ids of these polys */
		QString labelIDText =
			QString("%1").arg(labelID);
		QRect rect = poly.boundingRect();
		int x = rect.center().x();
		int y = rect.center().y();

		aPainter->drawText(
			x,
			y,
			20,
			20,
			Qt::AlignHCenter,
			labelIDText
			);
	}

}

void
ImageHolder::triggerBoundBox(
	const QPoint &aNewPos,
	const QPoint &anOldPos,
	QRect *aNewRect
	)
{
	aNewRect->setCoords(
			 anOldPos.x(),
			 anOldPos.y(),
			 aNewPos.x(),
			 aNewPos.y()
	);

	state_ = NewSelection;
	repaint_needed_ = 1;
}

void
ImageHolder::triggerPolygon(
	const QPoint &aPoint,
	QPolygon *aNewPoly
	)
{
	*aNewPoly << aPoint;

	repaint_needed_ = 1;
}

void
ImageHolder::focusOnArea(QListWidgetItem *anItem)
{
	QString text = anItem->text();

	Tool tool = NoTool;
	if (-1 != text.indexOf("BBox"))
		tool = BoundingBoxTool;
	else if (-1 != text.indexOf("Poly"))
		tool = PolygonTool;

	/* looking for a number of selected area */
	if (NoTool != tool) {
		bool ok = 0;
		focused_selection_ = getNumFromString(&text, "#", ";", &ok);

		if (!ok) {
			focused_selection_ = -1;
			focused_selection_type_ = NoFigure;
			return;
			/* NOTREACHED */
		}

		switch (tool) {
		case BoundingBoxTool:
			focused_selection_type_ = RectFigure;
			break;
		case PolygonTool:
			focused_selection_type_ = PolyFigure;
			break;
		default:
			focused_selection_type_ = NoFigure;
			break;
		}

	}
	update();
}

void
ImageHolder::scaleImage(
	ZoomDirection aDirection,
	double scaleFactor
)
{
	//QSize size = pixmap()->size();
	QSize size = this->size();

	/* zoomin */
	if (ZoomIn == aDirection) {
		size *= scaleFactor;
		scale_ *= scaleFactor;
	}
	/* zoomout */
	else if (ZoomOut == aDirection) {
		size /= scaleFactor;
		scale_ /= scaleFactor;
	}

//	setScaledContents(true);
	this->resize(size);
//	setPixmap(
//		image_->scaled(
//			size,
//			Qt::IgnoreAspectRatio,
//			Qt::SmoothTransformation
//			)
//		);
//	setScaledContents(false);



	//resize(scaleFactor * pixmap()->size());
	//emit imageScaled();
}

void
ImageHolder::clearFocusOnArea()
{
	focused_selection_ = -1;
	focused_selection_type_ = NoFigure;
	update();
}

void
ImageHolder::setTool(Tool aTool)
{
	switch(aTool) {
	case BoundingBoxTool:
		tool_ = BoundingBoxTool;
		break;
	case PolygonTool:
		tool_ = PolygonTool;
		break;
	case TaggingTool:
		tool_ = TaggingTool;
		break;
	default:
		tool_ = NoTool;
		break;
	}
}

void
ImageHolder::setBoundingBoxList(QList< BoundingBox * > *aBBoxList)
{
	if (0 == aBBoxList) {
		return;
		/* NOTREACHED */
	}

	list_bounding_box_ = aBBoxList;
}

void
ImageHolder::setPolygonList(QList< Polygon * > *aPolygonList)
{
	if (0 == aPolygonList) {
		return;
		/* NOTREACHED */
	}

	list_polygon_ = aPolygonList;
}

void
ImageHolder::setLabelColorList(QList< uint > *aLabelColorList)
{
	if (0 == aLabelColorList) {
		return;
		/* NOTREACHED */
	}

	list_label_color_ = aLabelColorList;
}

void
ImageHolder::setMainLabelNum(int *aNum)
{
	if (0 == aNum) {
		return;
		/* NOTREACHED */
	}

	main_label_ = aNum;
}

void
ImageHolder::setImage(QPixmap *anImage)
{
	if (0 == anImage) {
		return;
		/* NOTREACHED */
	}

	image_ = anImage;
}

void
ImageHolder::clearAll()
{
	//list_bounding_box_->clear();
	bounding_box_.rect.setRect(-1, -1, 0, 0);
	//list_polygon_->clear();
	polygon_.poly.clear();
	clearFocusOnArea();
	state_ = StandBy;

	update();
}

void
ImageHolder::clearLast()
{
	switch (tool_) {
	case BoundingBoxTool:
		bounding_box_.rect.setRect(-1, -1, 0, 0);
		break;
	case PolygonTool:
		polygon_.poly.clear();
		break;
	case TaggingTool:
		break;
	default:
		break;
	}
	bounding_box_.rect.setRect(-1, -1, 0, 0);

	state_ = StandBy;
	update();
}

void
ImageHolder::confirmSelection()
{
	if (NewSelection != state_ || NoTool == tool_) {
		return;
		/* NOTREACHED */
	}

	if (BoundingBoxTool == tool_) {
		BoundingBox *bbox = new BoundingBox;
		*bbox = bounding_box_;
		list_bounding_box_->append(bbox);
		bounding_box_.rect.setRect(-1, -1, 0, 0);
	}
	else if (PolygonTool == tool_) {
		Polygon *poly = new Polygon;
		*poly = polygon_;
		list_polygon_->append(poly);
		polygon_.poly.clear();
	}

	list_poly_history_.clear();

	state_ = StandBy;
	update();
}

ImageHolder::State
ImageHolder::state()
{
	return state_;
}

ImageHolder::Tool
ImageHolder::tool()
{
	return tool_;
}

int
ImageHolder::focusedSelection()
{
	return focused_selection_;
}
Figure
ImageHolder::focusedSelectionType()
{
	return focused_selection_type_;
}

void
ImageHolder::undo()
{
	if (PolygonTool == tool_ &&
		NewSelection == state_ &&
		!polygon_.poly.isEmpty())
	{
		list_poly_history_.append(polygon_.poly.last());
		polygon_.poly.pop_back();
		repaint_needed_ = 1;
	}

	if (repaint_needed_) {
		update();
		repaint_needed_ = 0;
	}
}

void
ImageHolder::redo()
{
	if (PolygonTool == tool_ &&
		NewSelection == state_ &&
		!list_poly_history_.isEmpty())
	{
		polygon_.poly.append(list_poly_history_.last());
		list_poly_history_.pop_back();
		repaint_needed_ = 1;
	}

	if (repaint_needed_) {
		update();
		repaint_needed_ = 0;
	}
}

void
ImageHolder::checkForPoints(QPoint *aPos)
{
	if ((!list_polygon_->count() &&
		!list_bounding_box_->count()) ||
		!aPos) {
		return;
		/* NOTREACHED */
	}

	int newRadius = 0;
	int x = aPos->x();
	int y = aPos->y();
	/* center coordinates */
	int xc = 0;
	int yc = 0;
	for (int i = 0; i < list_polygon_->count(); i++) {
		QPolygon poly = list_polygon_->at(i)->poly;
		for (int j = 0; j < poly.count(); j++) {
			xc = poly.at(j).x();
			yc = poly.at(j).y();
			newRadius = qSqrt(qPow(x - xc, 2) + qPow(y - yc, 2));
			if (newRadius <= point_radius_) {
				hovered_point_.figure = PolyFigure;
				hovered_point_.figureID = i;
				hovered_point_.pointID = j;
				repaint_needed_ = 1;
				return;
				/* NOTREACHED */
			}
		}
	}

	for (int i = 0; i < list_bounding_box_->count(); i++) {
		QRect rect = list_bounding_box_->at(i)->rect;

		for (int j = 0; j < 4; j++) {
			if (!j) {
				xc = rect.left();
				yc = rect.top();
			}
			else if (1 == j)
			{
				xc = rect.right();
				yc = rect.top();
			}
			else if (2 == j)
			{
				xc = rect.right();
				yc = rect.bottom();
			}
			else if (3 == j)
			{
				xc = rect.left();
				yc = rect.bottom();
			}


			newRadius = qSqrt(qPow(x - xc, 2) + qPow(y - yc, 2));
			if (newRadius <= point_radius_) {
				hovered_point_.figure = RectFigure;
				hovered_point_.figureID = i;
				hovered_point_.pointID = j;
				repaint_needed_ = 1;
				return;
				/* NOTREACHED */
			}
		}
	}

	hovered_point_.figure = NoFigure;
	hovered_point_.figureID = -1;
	hovered_point_.pointID = -1;
	repaint_needed_ = 1;
}

void
ImageHolder::keyPressEvent(QKeyEvent *anEvent)
{
	QLabel::keyPressEvent(anEvent);

	//keyboard_modifier_ = anEvent->modifiers();

	if (repaint_needed_) {
		update();
		repaint_needed_ = 0;
	}
}

void
ImageHolder::mouseMoveEvent(QMouseEvent *anEvent)
{
	QPoint pos = anEvent->pos() / scale_;
	if (anEvent->pos().x() < 0)
		pos.setX(0);

	if (width() < anEvent->pos().x())
		pos.setX(width() - 1);

	if (anEvent->pos().y() < 0)
		pos.setY(0);


	if (height() < anEvent->pos().y())
		pos.setY(height() - 1);


	if ((anEvent->buttons() & Qt::LeftButton) &&
		BoundingBoxTool == tool_ &&
		NewSelection == state_ &&
		Qt::NoModifier == keyboard_modifier_)
	{
		triggerBoundBox(pos, prev_cursor_pos_, &(bounding_box_.rect));
	}

	if (PolygonTool == tool_ &&
		NewSelection == state_ &&
		(anEvent->buttons() & Qt::LeftButton))
	{
		polygon_.poly.setPoint(polygon_.poly.count() - 1, pos);
		//triggerPolygon(pos, &(polygon_.poly));
		repaint_needed_ = 1;
	}

	if (-1 != focused_selection_ &&
		!(anEvent->buttons() & Qt::LeftButton)) {
		checkForPoints(&pos);
	}

	/* editing polygons */
	if (-1 != hovered_point_.figureID &&
		!list_polygon_->isEmpty() &&
		PolyFigure == hovered_point_.figure &&
		(anEvent->buttons() & Qt::LeftButton))
	{
		Polygon *poly = list_polygon_->at(hovered_point_.figureID);
		poly->poly.setPoint(hovered_point_.pointID, pos);
		repaint_needed_ = 1;
	}

	/* editing bounding boxes */
	if (-1 != hovered_point_.figureID &&
		!list_bounding_box_->isEmpty() &&
		RectFigure == hovered_point_.figure &&
		(anEvent->buttons() & Qt::LeftButton))
	{
		BoundingBox *rect = list_bounding_box_->at(hovered_point_.figureID);
		if (0 == hovered_point_.pointID)
			rect->rect.setTopLeft(pos);
		else if (1 == hovered_point_.pointID)
			rect->rect.setTopRight(pos);
		else if (2 == hovered_point_.pointID)
				rect->rect.setBottomRight(pos);
		else if (3 == hovered_point_.pointID)
				rect->rect.setBottomLeft(pos);

		repaint_needed_ = 1;
	}

	if (repaint_needed_) {
		update();
		repaint_needed_ = 0;
	}
}

void
ImageHolder::mousePressEvent(QMouseEvent *anEvent)
{
	/* remembering coordinates of the click */
	if ((anEvent->buttons() & Qt::LeftButton) ||
		(anEvent->buttons() & Qt::RightButton))
	{
		prev_cursor_pos_ = anEvent->pos() / scale_;
	}

	QPoint pos = anEvent->pos() / scale_;

	if (anEvent->buttons() & Qt::LeftButton) {
		/* clearing the selected area if it is not confirmed */
		if (NewSelection == state_ && BoundingBoxTool == tool_) {
			bounding_box_.rect.setRect(-1, -1, 0, 0);
			state_ = StandBy;
		}

		/* making new points for poly */
		if (PolygonTool == tool_ &&
			NewSelection == state_ &&
			Qt::NoModifier == keyboard_modifier_)
		{
			triggerPolygon(pos, &(polygon_.poly));
		}

		/* starting new selection by click */
		if (StandBy == state_ && NoTool != tool_ &&
			-1 == focused_selection_) {
			state_ = NewSelection;
			emit selectionStarted();

			polygon_.poly.clear();
			if (PolygonTool == tool_) {
				polygon_.poly << prev_cursor_pos_;
			}
		}
	}

	if (repaint_needed_) {
		update();
		repaint_needed_ = 0;
	}
}

void
ImageHolder::mouseReleaseEvent(QMouseEvent *anEvent)
{
	Q_UNUSED(anEvent)

	if (-1 != hovered_point_.figureID)
		emit areaEdited();

	if (RectFigure == hovered_point_.figure &&
		-1 != hovered_point_.figureID &&
		!list_bounding_box_->
			at(hovered_point_.figureID)->
			rect.isValid()
		)
	{
		BoundingBox *rect = list_bounding_box_->at(hovered_point_.figureID);
		rect->rect = rect->rect.normalized();
	}
}

/*
 *
 */