#include "simulationgraphicview.h"

//SimulationGraphicView::SimulationGraphicView(QWidget *parent)
//	: QWidget(parent)
//{
//	ui.setupUi(this);
//}
//
//SimulationGraphicView::~SimulationGraphicView()
//{
//
//}



#include "showpoints.h"

#define MIN_SCENE_X -20
#define MIN_SCENE_Y -0


typedef QList<QGraphicsItem*> GraphicsItemList;

GraphInfo::~GraphInfo()
{
	for(LineInfoVector::iterator it = _lines.begin(); it!=_lines.end(); it++) {
		LineInfo *info = *it;
		delete info;
	}
	_lines.clear();
}

/////////////////////////////////////////////////////////////////////////////////////////////////
SimulationGraphicView::SimulationGraphicView(QWidget *parent)
: QWidget(parent)
{
	ui.setupUi(this);

	_lastWidth = _lastHeight = 0;
	_graphInfo = NULL;
	_updatePending = false;

	QGraphicsScene* scene = new QGraphicsScene(ui.graphicsView);
	ui.graphicsView->setScene(scene);
	ui.graphicsView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
	connect(ui.viewResultsBtn, SIGNAL(clicked(bool)), this, SLOT(viewPoints()));
	connect(ui.showRefCheck, SIGNAL(stateChanged(int)), this, SLOT(showRefStateChanged(int)));
}

SimulationGraphicView::~SimulationGraphicView()
{
}

void SimulationGraphicView::clearData()
{
	if(_graphInfo)
		delete _graphInfo;
	_graphInfo = NULL;
}

void SimulationGraphicView::setGraphic(SimulationGraphic *graph)
{
	clearData();

	_matrix.reset();

	SimulationGraphicCurveList lines = graph->curves();
	bool init = false;

	_graphInfo = new GraphInfo;
	_graphInfo->_title = graph->title();

	for(SimulationGraphicCurveList::iterator itLine = lines.begin(); itLine != lines.end(); itLine++) {
		SimulationGraphicCurve *line = *itLine;
		QPointF prevPoint;
		PointList points = line->points();

		LineInfo *info = new LineInfo;
		info->_pen = line->pen();
		info->_text = line->text();

		for(PointList::iterator itPoint = points.begin(); itPoint != points.end(); itPoint++) {
			QPointF p = *itPoint;

			//info->_points.append(p);

			if(info->_points.elementCount() > 0) {
				info->_points.lineTo(p);

				if(p.x() > _maxX)
					_maxX = p.x();
				if(p.y() > _maxY)
					_maxY = p.y();
				if(p.x() < _minX)
					_minX = p.x();
				if(p.y() < _minY)
					_minY = p.y();
				//QGraphicsLineItem *graphLine = new QGraphicsLineItem(QLineF(prevPoint, p), NULL, view->scene());
				//graphLine->setPen(line->pen());
			}
			else {
				if(!init) {
					init = true;
					_minX = _maxX = p.x();
					_minY = _maxY = p.y();
				}

				info->_points = QPainterPath(p);
			}

			prevPoint = p;
		}

		_graphInfo->_lines.append(info);
	}

	_graphInfo->_xString = graph->xText();
	_graphInfo->_yString = graph->yText();

	if(graph->maxXSet())
		_graphInfo->_maxX = graph->maxX();
	else
		_graphInfo->_maxX = _maxX;
	if(graph->maxYSet())
		_graphInfo->_maxY = graph->maxY();
	else
		_graphInfo->_maxY = _maxY;

	if(graph->minXSet())
		_graphInfo->_minX = graph->minX();
	else
		_graphInfo->_minX = _minX;
	if(graph->minYSet())
		_graphInfo->_minY = graph->minY();
	else
		_graphInfo->_minY = _minY;
}

bool SimulationGraphicView::needToUpdate()
{
	bool ret = true;
	double newWidth = width() - 20;
	double newHeight = height() - 90;

	// nothing to do!
	if(_lastWidth == newWidth && _lastHeight == newHeight)
		ret = false;
	return ret;
}

void SimulationGraphicView::updateScene()
{
	QGraphicsView *view = ui.graphicsView;

	if(_graphInfo == NULL)
		return;

	//GraphicsItemList items = view->scene()->items();
	//for(GraphicsItemList::iterator it=items.begin(); it!=items.end(); it++) {
	//	QGraphicsItem* item = *it;
	//	delete item;
	//}

	if(_graphInfo->_refParent) {
		delete _graphInfo->_refParent;
		_graphInfo->_refParent = NULL;
	}
	if(_graphInfo->_axisParent) {
		delete _graphInfo->_axisParent;
		_graphInfo->_axisParent = NULL;
	}
	if(_graphInfo->_linesParent) {
		delete _graphInfo->_linesParent;
		_graphInfo->_linesParent = NULL;
	}

	_maxX = _graphInfo->_maxX * 1.1;
	_maxY = _graphInfo->_maxY * 1.1;

	if(!_graphInfo->_title.isEmpty())
		setWindowTitle(_graphInfo->_title);
	_matrix = QMatrix();

	double newWidth = width() - 25;
	double newHeight = height() - 105;

	// nothing to do!
	if(_lastWidth == newWidth && _lastHeight == newHeight)
		return;

	view->scene()->setSceneRect(QRectF(MIN_SCENE_X, MIN_SCENE_Y, newWidth, newHeight));

	// scale the values and invert Y so small values are low and big values are high
	_matrix.scale((view->scene()->width()+MIN_SCENE_X)/(_maxX-_minX), -1*(view->scene()->height()-(-MIN_SCENE_Y))/(_maxY-_minY));

	// add minimums and 20 pixels to the right.
	_matrix.translate(-_minX + (-MIN_SCENE_X)/((double) (view->scene()->width()-(-MIN_SCENE_X))/((double) _maxX-_minX)), -(_maxY-_minY)+_minY);
	//_matrix.translate(-minX + 20/((double) (view->scene()->width()-20)/((double) maxX-minX)), -(maxY-minY) + minY/((double) (view->scene()->height()-70)));

	QPen pen;
	pen.setColor(Qt::darkGray);	
	pen.setWidth(1);

	_minX = _graphInfo->_minX;
	_maxX = _graphInfo->_maxX;
	_minY = _graphInfo->_minY;
	_maxY = _graphInfo->_maxY;

	QRectF rect = view->scene()->sceneRect();
	int i;
	QPointF p1, p2, p3, p4;
	QGraphicsTextItem *textItem;
	double startYAxisX;

	// create markers in the axis
	_graphInfo->_axisParent = new QGraphicsItemGroup(NULL, view->scene());

	for(i=0; i<=20; i++) {
		p1 = QPointF(_minX, _minY + (_maxY - _minY) / 20 * i);

		p3 = _matrix.map(p1);
		p4 = p3;
		p3.setX(p3.x()-10);

		QGraphicsLineItem *l = new QGraphicsLineItem(QLineF(p3, p4), NULL, view->scene());
		_graphInfo->_axisParent->addToGroup(l);
		l->setPen(pen);

		if(i == 0) {
			textItem = new QGraphicsTextItem(QString("%1").arg(_graphInfo->_minY), NULL, view->scene());
			_graphInfo->_axisParent->addToGroup(textItem);
			p3.setX(MIN_SCENE_X);
			p3.setY(p3.y()-textItem->boundingRect().height()/2);
			textItem->setPos(p3);
		}
		else if(i == 20) {
			textItem = new QGraphicsTextItem(QString("%1").arg(_graphInfo->_maxY), NULL, view->scene());
			_graphInfo->_axisParent->addToGroup(textItem);
			p3.setX(MIN_SCENE_X);
			p3.setY(p3.y()-textItem->boundingRect().height()/2);
			textItem->setPos(p3);
		}

		p1 = QPointF(_minX + (_maxX - _minX) / 20 * i, _minY);

		p3 = _matrix.map(p1);
		p4 = p3;
		p3.setY(p3.y()+10);

		l = new QGraphicsLineItem(QLineF(p3, p4), NULL, view->scene());
		_graphInfo->_axisParent->addToGroup(l);
		l->setPen(pen);

		if(i == 0) {
			startYAxisX = p4.y();
			textItem = new QGraphicsTextItem(QString("%1").arg(_graphInfo->_minX), NULL, view->scene());
			_graphInfo->_axisParent->addToGroup(textItem);
			p3.setY(p3.y()+3);
			p3.setX(p3.x()-textItem->boundingRect().width()/2);
			textItem->setPos(p3);
		}
		else if(i == 20) {
			textItem = new QGraphicsTextItem(QString("%1").arg(_graphInfo->_maxX), NULL, view->scene());
			_graphInfo->_axisParent->addToGroup(textItem);
			p3.setY(p3.y()+3);
			p3.setX(p3.x()-textItem->boundingRect().width()/2);
			textItem->setPos(p3);
		}
	}

	textItem = new QGraphicsTextItem(_graphInfo->_yString, NULL, view->scene());
	_graphInfo->_axisParent->addToGroup(textItem);

	textItem->setDefaultTextColor(QColor(0x20, 0x20, 0x20));
	textItem->setPos(-10, MIN_SCENE_Y);

	p1 = QPointF(_minX, _minY);
	p2 = _matrix.map(p1);

	textItem = new QGraphicsTextItem(_graphInfo->_xString, NULL, view->scene());
	_graphInfo->_axisParent->addToGroup(textItem);
	textItem->setDefaultTextColor(QColor(0x20, 0x20, 0x20));

	p2.setX(width() - 20 - textItem->boundingRect().width() - 25);
	p2.setY(p2.y() + 28);
	textItem->setPos(p2);

	// use it as parent of all references so we can hide / show
	_graphInfo->_refParent = new QGraphicsItemGroup(NULL, view->scene());
	_graphInfo->_linesParent = new QGraphicsItemGroup(NULL, view->scene());

	for(i = 0; i < _graphInfo->_lines.count(); i++) {
		LineInfo *info = _graphInfo->_lines[i];
		QPainterPath mappedPoints;

		mappedPoints = _matrix.map(info->_points);

		info->_graphPol = new QGraphicsPathItem(mappedPoints, NULL, view->scene());
		_graphInfo->_linesParent->addToGroup(info->_graphPol);
		info->_graphPol->setPen(info->_pen);
		if(!info->_text.isEmpty()) {
			double posX = width()*4/5;
			double posY = height() / 2 + i * 15;

			// it's buggy in Qt!
			//info->_graphPol->setToolTip(info->_text);

			if(posY < startYAxisX - 15) {
				info->_refLine = new QGraphicsLineItem(posX, posY, posX+10, posY, NULL, view->scene());
				_graphInfo->_refParent->addToGroup(info->_refLine);
				info->_refLine->setPen(info->_pen);
				info->_refLine->setToolTip(info->_text);

				info->_refText = new QGraphicsTextItem(info->_text, NULL, view->scene());
				_graphInfo->_refParent->addToGroup(info->_refText);
				QFont f = info->_refText->font();
				f.setPointSize(8);
				info->_refText->setFont(f);
				info->_refText->setPos(posX + 15, posY - info->_refText->boundingRect().height()/2);
				info->_refText->setToolTip(info->_text);
			}
			// as last item we display '...' indicating that there are more items
			else if(posY < startYAxisX) {
				info->_refText = new QGraphicsTextItem(tr("..."), NULL, view->scene());
				_graphInfo->_refParent->addToGroup(info->_refText);
				QFont f = info->_refText->font();
				f.setPointSize(8);
				info->_refText->setFont(f);
				info->_refText->setPos(posX + 15, posY - info->_refText->boundingRect().height()/2-5);
				info->_refText->setToolTip(info->_text);
			}
		}
	}

	showRefStateChanged(ui.showRefCheck->checkState());

	_lastWidth = newWidth;
	_lastHeight = newHeight;

	_updatePending = false;
}

bool SimulationGraphicView::event(QEvent *e)
{
	switch(e->type()) {
		case QEvent::Resize:
		{
			if(needToUpdate() && !_updatePending) {
				_updatePending = true;
				// first update?
				if(_lastHeight == 0)
					//updateScene();
					QTimer::singleShot(0, this, SLOT(updateScene()));
				else
					// when the user is updating the size generates this event lots of times so to update only some of
					// the events this is done
					QTimer::singleShot(200, this, SLOT(updateScene()));
			}
			break;
		}
	}

	return QWidget::event(e);
}

void SimulationGraphicView::viewPoints()
{
	if(_graphInfo == NULL)
		return;

	ShowPoints ptsDlg(this, _graphInfo);
	ptsDlg.exec();
}

void SimulationGraphicView::showRefStateChanged(int state)
{
	_graphInfo->_refParent->setVisible(state == Qt::Checked);
}