/* GNUPLOT - QtGnuplotItems.cpp */

/*[
 * Copyright 2009   Jérôme Lodewyck
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the complete modified source code.  Modifications are to
 * be distributed as patches to the released version.  Permission to
 * distribute binaries produced by compiling modified sources is granted,
 * provided you
 *   1. distribute the corresponding source modifications from the
 *    released version in the form of a patch file along with the binaries,
 *   2. add special version identification to distinguish your version
 *    in addition to the base release version number,
 *   3. provide your name and address as the primary contact for the
 *    support of your modified version, and
 *   4. retain our contact information in regard to use of the base
 *    software.
 * Permission to distribute the released version of the source code along
 * with corresponding source modifications in the form of a patch file is
 * granted with same provisions 2 through 4 for binary distributions.
 *
 * This software is provided "as is" without express or implied warranty
 * to the extent permitted by applicable law.
 *
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * GNU General Public License Version 2 or later (the "GPL"), in which case the
 * provisions of GPL are applicable instead of those above. If you wish to allow
 * use of your version of this file only under the terms of the GPL and not
 * to allow others to use your version of this file under the above gnuplot
 * license, indicate your decision by deleting the provisions above and replace
 * them with the notice and other provisions required by the GPL. If you do not
 * delete the provisions above, a recipient may use your version of this file
 * under either the GPL or the gnuplot license.
]*/

#include "QtGnuplotItems.h"

#include <QtGui>

#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
  #define horizontalAdvance width
#endif

/////////////////////////////
// QtGnuplotEnhanced

void QtGnuplotEnhanced::addText(const QString& fontName, double fontSize, 
				QFont::Style fontStyle, QFont::Weight fontWeight,
				double base, bool widthFlag,
                                bool showFlag, int overprint, const QString& text, QColor color)
{
	if ((overprint == 1) && !(m_overprintMark)) // Underprint
	{
		m_overprintPos = m_currentPos.x();
		m_overprintMark = true;
	}
	if (overprint == 3)                         // Save position
		m_savedPos = m_currentPos;
	else if (overprint == 4)                    // Recall saved position
		m_currentPos = m_savedPos;

	QFont font(fontName, fontSize);
	if (fontName.isEmpty()) {
		// qDebug() << "Empty font name";
		font.setFamily("Sans");		// FIXME: use default? use previous?
	}
	font.setStyle(fontStyle);
	font.setWeight(fontWeight);
	font.setStyleStrategy(QFont::ForceOutline);	// pcf fonts die if rotated
	QtGnuplotEnhancedFragment* item = new QtGnuplotEnhancedFragment(font, text, this);
	item->setPos(m_currentPos + QPointF(0., -base));
	if (showFlag)
		item->setPen(color);
	else
		item->setPen(Qt::NoPen);

	if (overprint == 2)                         // Overprint
	{
		item->setPos(QPointF((m_overprintPos + m_currentPos.x())/2. - item->width()/2., -base));
		m_overprintMark = false;
	}

	if (widthFlag && (overprint != 2))
		m_currentPos += QPointF(item->width(), 0.);
}

void QtGnuplotEnhanced::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	Q_UNUSED(painter);
	Q_UNUSED(option);
	Q_UNUSED(widget);
}

QtGnuplotEnhancedFragment::QtGnuplotEnhancedFragment(const QFont& font, const QString& text, QGraphicsItem * parent)
	: QAbstractGraphicsShapeItem(parent)
{
	m_font = font;
	m_text = text;
}

QRectF QtGnuplotEnhancedFragment::boundingRect() const
{
	QFontMetricsF metrics(m_font);
	return metrics.boundingRect(m_text);
}

qreal QtGnuplotEnhancedFragment::width() const
{
	QFontMetricsF metrics(m_font);
	return metrics.horizontalAdvance(m_text);
}

void QtGnuplotEnhancedFragment::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	Q_UNUSED(option);
	Q_UNUSED(widget);

	painter->setPen(pen());
	painter->setFont(m_font);
	painter->drawText(QPointF(0.,0.), m_text);
}

/////////////////////////////
// QtGnuplotPoint

QtGnuplotPoint::QtGnuplotPoint(int style, double size, QPen pen, QGraphicsItem * parent)
	: QGraphicsItem(parent)
{
	m_pen   = pen;
	m_color = pen.color();
	m_style = style;
	m_size = 3.*size;
}

QRectF QtGnuplotPoint::boundingRect() const
{
	return QRectF(QPointF(-m_size, -m_size), QPointF(m_size, m_size));
}

void QtGnuplotPoint::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	Q_UNUSED(option);
	Q_UNUSED(widget);

	const int style = m_style % 15;

	if ((style % 2 == 0) && (style > 3)) // Filled points
	{
		painter->setPen(m_color);
		painter->setBrush(m_color);
	}
	else
		painter->setPen(m_pen);

	drawPoint(painter, QPointF(0., 0.), m_size, style);
}

void QtGnuplotPoint::drawPoint(QPainter* painter, const QPointF& origin, double size, int style)
{
	if (style == -1) // dot
	{
		painter->drawPoint(origin);
		return;
	}

	if ((style == 0) || (style == 2)) // plus or star
	{
		painter->drawLine(origin + QPointF(0., -size), origin + QPointF(0., size));
		painter->drawLine(origin + QPointF(-size, 0.), origin + QPointF(size, 0.));
	}
	if ((style == 1) || (style == 2)) // cross or star
	{
		painter->drawLine(origin + QPointF(-size, -size), origin + QPointF(size, size));
		painter->drawLine(origin + QPointF(-size, size), origin + QPointF(size, -size));
	}
	else if ((style == 3) || (style == 4)) // box
		painter->drawRect(QRectF(origin + QPointF(-size, -size), origin + QPointF(size, size)));
	else if ((style == 5) || (style == 6)) // circle
		painter->drawEllipse(QRectF(origin + QPointF(-size, -size), origin + QPointF(size, size)));
	else if ((style == 7) || (style == 8)) // triangle
	{
		const QPointF p[3] = { origin + QPointF(0., -size),
							origin + QPointF(.866*size, .5*size),
							origin + QPointF(-.866*size, .5*size)};
		painter->drawPolygon(p, 3);
	}
	else if ((style == 9) || (style == 10)) // upside down triangle
	{
		const QPointF p[3] = { origin + QPointF(0., size),
							origin + QPointF(.866*size, -.5*size),
							origin + QPointF(-.866*size, -.5*size)};
		painter->drawPolygon(p, 3);
	}
	else if ((style == 11) || (style == 12)) // diamond
	{
		const QPointF p[4] = { origin + QPointF(0., size),
							origin + QPointF(size, 0.),
							origin + QPointF(0., -size),
							origin + QPointF(-size, 0.)};
		painter->drawPolygon(p, 4);
	}
	else if ((style == 13) || (style == 14)) // pentagon
	{
		const QPointF p[5] = { origin + QPointF(0., size),
						origin + QPointF( size*0.9511,  size*0.3090),
						origin + QPointF( size*0.5878, -size*0.8090),
						origin + QPointF(-size*0.5878, -size*0.8090),
						origin + QPointF(-size*0.9511,  size*0.3090)};
		painter->drawPolygon(p, 5);
	}
}

/////////////////////////////
// QtGnuplotPoints

QtGnuplotPoints::QtGnuplotPoints(QGraphicsItem * parent)
	: QGraphicsItem(parent)
{
	m_currentZ = 0;
}

QRectF QtGnuplotPoints::boundingRect() const
{
	return m_boundingRect;
}

void QtGnuplotPoints::addPoint(const QPointF& point, int style, double pointSize, const QPen& pen)
{
	QtGnuplotPoints_PointData pointData;
	pointData.point = point;
	pointData.style = style;
	pointData.pointSize = 3*pointSize;
	pointData.pen = pen;
	pointData.z = m_currentZ++;
	m_points << pointData;

	const double size = pointData.pointSize;
	const QPointF& origin = pointData.point;
	m_boundingRect = m_boundingRect.united(QRectF(origin + QPointF(-size, -size), origin + QPointF(size, size)));
}

void QtGnuplotPoints::addPolygon(const QPolygonF& polygon, const QPen& pen)
{
	QtGnuplotPoints_PolygonData polygonData;
	polygonData.polygon = polygon;
	polygonData.pen = pen;
	polygonData.z = m_currentZ++;
	m_polygons << polygonData;
	m_boundingRect = m_boundingRect.united(polygon.boundingRect());
}

void QtGnuplotPoints::addFilledPolygon(const QPolygonF& polygon, const QBrush& brush)
{
	QtGnuplotPoints_FilledPolygonData filledPolygonData;
	filledPolygonData.polygon = polygon;
	filledPolygonData.brush = brush;
	filledPolygonData.z = m_currentZ++;
	m_filledPolygons << filledPolygonData;
	m_boundingRect = m_boundingRect.united(polygon.boundingRect());
}

bool QtGnuplotPoints::isEmpty() const
{
	return m_points.isEmpty() && m_polygons.isEmpty() && m_filledPolygons.isEmpty();
}

void QtGnuplotPoints::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	Q_UNUSED(option);
	Q_UNUSED(widget);

	painter->setBrush(Qt::NoBrush);

	int i = 0;
	int j = 0;
	int k = 0;
	unsigned int z = 0;

	while ((i < m_points.size()) || (j < m_polygons.size()) || (k < m_filledPolygons.size()))
	{
		for (; i < m_points.size() && m_points[i].z == z; i++, z++)
		{
			const int style = m_points[i].style % 15;

			painter->setPen(m_points[i].pen.color());
			if ((style % 2 == 0) && (style > 3)) // Filled points
				painter->setBrush(m_points[i].pen.color());
			else
			{
				painter->setPen(m_points[i].pen);
				painter->setBrush(Qt::NoBrush);
			}

			QtGnuplotPoint::drawPoint(painter, m_points[i].point, m_points[i].pointSize, style);
		}

		painter->setBrush(Qt::NoBrush);
		for (; j < m_polygons.size() && m_polygons[j].z == z; j++, z++)
		{
			painter->setPen(m_polygons[j].pen);
			painter->drawPolyline(m_polygons[j].polygon);
		}

		for (; k < m_filledPolygons.size() && m_filledPolygons[k].z == z; k++, z++)
		{
			QPen pen = Qt::NoPen;
			QBrush& brush = m_filledPolygons[k].brush;
			if (brush.isOpaque())
				pen = brush.color();
			painter->setPen(pen);
			painter->setBrush(brush);
			painter->drawPolygon(m_filledPolygons[k].polygon);
		}

	}
}

/////////////////////////////
// QtGnuplotKeyBox

/*
 * EAM - support for toggling plots by clicking on a key sample
 */

QtGnuplotKeybox::QtGnuplotKeybox(const QRectF& rect) : QRectF(rect)
{
	m_hidden = false;
	m_statusBox = NULL;
}

bool QtGnuplotKeybox::ishidden() const
{
	return m_hidden;
}

void QtGnuplotKeybox::setHidden(bool state)
{
	m_hidden = state;
	if (m_statusBox)
		m_statusBox->setVisible(m_hidden);
}

void QtGnuplotKeybox::showStatus(QGraphicsRectItem* me)
{
	m_statusBox = me;
	m_statusBox->setVisible(m_hidden);
}

void QtGnuplotKeybox::resetStatus()
{
	m_statusBox = NULL;
}
