#include "../headers/HistogramItem.h"

#include <qstring.h>
#include <qpainter.h>
#include <qwt_plot.h>
#include <qwt_interval_data.h>
#include <qwt_painter.h>
#include <qwt_scale_map.h>


class HistogramItem::PrivateData {
	public:
		int attributes;
		QwtIntervalData data;
		QColor color;
		double reference;
};


HistogramItem::HistogramItem(const QwtText &title): QwtPlotItem(title) {
	init();
}


HistogramItem::HistogramItem(const QString &title): QwtPlotItem(QwtText(title)) {
	init();
}


HistogramItem::~HistogramItem() {
	delete d_data;
}


void HistogramItem::init() {

	d_data = new PrivateData();
	d_data->reference = 0.0;
	d_data->attributes = HistogramItem::Auto;

	setItemAttribute(QwtPlotItem::AutoScale, true);
	setItemAttribute(QwtPlotItem::Legend, true);

	setZ(20.0);
}


void HistogramItem::setBaseline(double reference) {

	if ( d_data->reference != reference ) {
		d_data->reference = reference;
		itemChanged();
	}
}


double HistogramItem::baseline() const {
	return d_data->reference;
}


void HistogramItem::setData(const QwtIntervalData &data) {
	d_data->data = data;
	itemChanged();
}


const QwtIntervalData &HistogramItem::data() const {
	return d_data->data;
}


void HistogramItem::setColor(const QColor &color) {
	if ( d_data->color != color ){
		d_data->color = color;
		itemChanged();
	}
}


QColor HistogramItem::color() const {
	return d_data->color;
}


QwtDoubleRect HistogramItem::boundingRect() const {

	QwtDoubleRect rect = d_data->data.boundingRect();

	if ( !rect.isValid() )
		return rect;

	if ( d_data->attributes & Xfy ) {
		rect = QwtDoubleRect( rect.y(), rect.x(),
		rect.height(), rect.width() );

		if ( rect.left() > d_data->reference )
			rect.setLeft( d_data->reference );
		else if ( rect.right() < d_data->reference )
			rect.setRight( d_data->reference );
	}
	else {
		if ( rect.bottom() < d_data->reference )
			rect.setBottom( d_data->reference );
		else if ( rect.top() > d_data->reference )
			rect.setTop( d_data->reference );
	}
	return rect;
}


int HistogramItem::rtti() const {
	return QwtPlotItem::Rtti_PlotHistogram;
}


void HistogramItem::setHistogramAttribute(HistogramAttribute attribute, bool on) {

	if ( bool(d_data->attributes & attribute) == on )
		return;

	if ( on )
		d_data->attributes |= attribute;
	else
		d_data->attributes &= ~attribute;

	itemChanged();
}


bool HistogramItem::testHistogramAttribute(HistogramAttribute attribute) const {
	return d_data->attributes & attribute;
}


void HistogramItem::draw(QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRect &) const {

	const QwtIntervalData &iData = d_data->data;

	painter->setPen(QPen(d_data->color));

	const int x0 = xMap.transform(baseline());
	const int y0 = yMap.transform(baseline());

	for ( int i = 0; i < (int)iData.size(); i++ ) {
		if ( d_data->attributes & HistogramItem::Xfy ){
			const int x2 = xMap.transform(iData.value(i));
			if ( x2 == x0 )
				continue;

			int y1 = yMap.transform( iData.interval(i).minValue());
			int y2 = yMap.transform( iData.interval(i).maxValue());
			if ( y1 > y2 )
				qSwap(y1, y2);

			if ( i < (int)iData.size() - 2 ){
				const int yy1 = yMap.transform(iData.interval(i+1).minValue());
				const int yy2 = yMap.transform(iData.interval(i+1).maxValue());

				if ( y2 == qwtMin(yy1, yy2) ){
					const int xx2 = xMap.transform(iData.interval(i+1).minValue());

					if ( xx2 != x0 && ( (xx2 < x0 && x2 < x0) || (xx2 > x0 && x2 > x0) ) ){
						// One pixel distance between neighboured bars
						y2++;
					}
				}
			}

			drawBar(painter, Qt::Horizontal, QRect(x0, y1, x2 - x0, y2 - y1));
		}
		else {
			const int y2 = yMap.transform(iData.value(i));

			if ( y2 == y0 )
				continue;

			int x1 = xMap.transform(iData.interval(i).minValue());
			int x2 = xMap.transform(iData.interval(i).maxValue());

			if ( x1 > x2 )
				qSwap(x1, x2);

			if ( i < (int)iData.size() - 2 ) {
				const int xx1 = xMap.transform(iData.interval(i+1).minValue());
				const int xx2 = xMap.transform(iData.interval(i+1).maxValue());

				if ( x2 == qwtMin(xx1, xx2) ) {
					const int yy2 = yMap.transform(iData.value(i+1));

					if ( yy2 != y0 && ( (yy2 < y0 && y2 < y0) || (yy2 > y0 && y2 > y0) ) ) {
                        // One pixel distance between neighboured bars
						x2--;
					}
				}
			}
			drawBar(painter, Qt::Vertical, QRect(x1, y0, x2 - x1, y2 - y0) );
		}
	}
}


void HistogramItem::drawBar(QPainter *painter, Qt::Orientation, const QRect& rect) const {
	painter->save();

	const QColor color(painter->pen().color());

#if QT_VERSION >= 0x040000
   const QRect r = rect.normalized();
#else
   const QRect r = rect.normalize();
#endif

   const int factor = 125;
   const QColor light(color.light(factor));
   const QColor dark(color.dark(factor));

   painter->setBrush(color);
   painter->setPen(Qt::NoPen);
   QwtPainter::drawRect(painter, r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
   painter->setBrush(Qt::NoBrush);
   painter->setPen(QPen(light, 2));

#if QT_VERSION >= 0x040000
   QwtPainter::drawLine(painter, r.left() + 1, r.top() + 2, r.right() + 1, r.top() + 2);
#else
   QwtPainter::drawLine(painter, r.left(), r.top() + 2, r.right() + 1, r.top() + 2);
#endif

   painter->setPen(QPen(dark, 2));

#if QT_VERSION >= 0x040000
   QwtPainter::drawLine(painter, r.left() + 1, r.bottom(), r.right() + 1, r.bottom());
#else
   QwtPainter::drawLine(painter, r.left(), r.bottom(), r.right() + 1, r.bottom());
#endif

   painter->setPen(QPen(light, 1));

#if QT_VERSION >= 0x040000
   QwtPainter::drawLine(painter, r.left(), r.top() + 1, r.left(), r.bottom());
   QwtPainter::drawLine(painter, r.left() + 1, r.top() + 2, r.left() + 1, r.bottom() - 1);
#else
   QwtPainter::drawLine(painter, r.left(), r.top() + 1, r.left(), r.bottom() + 1);
   QwtPainter::drawLine(painter, r.left() + 1, r.top() + 2, r.left() + 1, r.bottom());
#endif

   painter->setPen(QPen(dark, 1));

#if QT_VERSION >= 0x040000
   QwtPainter::drawLine(painter, r.right() + 1, r.top() + 1, r.right() + 1, r.bottom());
   QwtPainter::drawLine(painter, r.right(), r.top() + 2, r.right(), r.bottom() - 1);
#else
   QwtPainter::drawLine(painter, r.right() + 1, r.top() + 1, r.right() + 1, r.bottom() + 1);
   QwtPainter::drawLine(painter, r.right(), r.top() + 2, r.right(), r.bottom());
#endif

   painter->restore();
}
