/***************************************************************************
 *	BGraph.cpp	Graph plotter
 *				T.Barnaby,	BEAM Ltd,	2007-05-31
 ***************************************************************************
 */
#include <BGraph.h>
#include <BString.h>
#include <qpainter.h>
#include <math.h>

#define BDEBUG	0

#if BDEBUG
#define	dprintf(fmt, a...)	printf(fmt, ##a);
#else
#define	dprintf(fmt, a...)
#endif

class AxisLabel :  public QWidget {
public:
			AxisLabel(QWidget* parent, BGraph* cp);
protected:
	void		resizeEvent(QResizeEvent* event);
	void		paintEvent(QPaintEvent* event);
private:
	BGraph*	ocp;
};

AxisLabel::AxisLabel(QWidget* parent, BGraph* cp) : QWidget(parent), ocp(cp){
}

void AxisLabel::resizeEvent(QResizeEvent* event){
	dprintf("AxisLabel::ResizeEvent: %d,%d\n", width(), height());
	ocp->drawLabels();
}

void AxisLabel::paintEvent(QPaintEvent* event){
	dprintf("AxisLabel::paintEvent:\n");
	ocp->drawLabels();
}

BGraphPlot::BGraphPlot(QWidget* parent, BGraph* cp) : QWidget(parent), ocp(cp){
}

void BGraphPlot::resizeEvent(QResizeEvent* event){
	dprintf("BGraphPlot::ResizeEvent: %d,%d\n", width(), height());
	ocp->redraw();
}

void BGraphPlot::paintEvent(QPaintEvent* event){
	dprintf("BGraphPlot::paintEvent:\n");
	ocp->redraw();
}

BGraph::BGraph(QWidget* parent) : QGrid(2, parent){
	oplotType = LINES;
	oxRangeMin = 0.0;
	oxRangeMax = 0.0;
	oyRangeMin = 0.0;
	oyRangeMax = 0.0;
	oyMinValue = 0;
	oyMaxValue = 0;
	oxMinValue = 0;
	oxMaxValue = 0;
	ocolor = blue;
	oxGridStep = 0;
	oyGridStep = 0;

	setFrameShape(QFrame::StyledPanel);
	setFrameShadow(QFrame::Raised);

	// Display labels etc
	oyLabelBox = new AxisLabel(this, this);
	oyLabelBox->setFixedWidth(100);

	oplotFrame = new QHBox(this);
	oplotFrame->setFrameShape(QFrame::StyledPanel);
	oplotFrame->setFrameShadow(QFrame::Raised);

	ocorner = new QWidget(this, "corner");
	ocorner->setFixedHeight(0);

	oxLabelBox = new AxisLabel(this, this);
	oxLabelBox->setFixedHeight(0);

	// Create main plot widget
	oplot = new BGraphPlot(oplotFrame, this);
	oplot->setBackgroundColor(white);
	oplot->show();
	
	drawLabels();
}

BGraph::~BGraph(){
}

void BGraph::setPlotType(PlotType plotType){
	oplotType = plotType;
}

void BGraph::setGrid(int xStep, int yStep){
	oxGridStep = xStep;
	oyGridStep = yStep;
	drawGrid();
}

void BGraph::setXLabel(BString label){
	oxLabel = label;
	drawLabels();
}

void BGraph::setYLabel(BString label){
	oyLabel = label;
	drawLabels();
}

void BGraph::setXRange(double min, double max){
	oxRangeMin = min;
	oxRangeMax = max;

	drawPlot();
	drawGrid();
	drawLabels();
}

void BGraph::setYRange(double min, double max){
	oyMinValue = oyRangeMin = min;
	oyMaxValue = oyRangeMax = max;

	drawPlot();
	drawGrid();
	drawLabels();
}

void BGraph::getXRange(double& min, double& max){
	min = oxMinValue;
	max = oxMaxValue;
}

void BGraph::setYLabelWidth(int width){
	oyLabelBox->setFixedWidth(width);
}

void BGraph::setData(BArray<float> data){
	odata = data;
	
	drawPlot();
	drawGrid();
	drawLabels();
}

void BGraph::getData(BArray<float>& data){
	data = odata;
}

void BGraph::redraw(){
	dprintf("BGraph::redraw:\n");
	drawPlot();
	drawGrid();
	drawLabels();
}

void BGraph::drawPlot(){
	unsigned int	x;
	unsigned int	i;
	QPainter	p;
	double		xScale = 1.0;
	double		xOffset = 1.0;
	double		yScale = 1.0;
	double		yOffset = 0;
	unsigned int	xMin;
	unsigned int	xMax;
	
	dprintf("DrawPlot\n");

	p.begin(oplot);
        p.setPen(ocolor);

	oplot->erase();
	if(odata.size() == 0)
		return;

	if((oyRangeMin == 0.0) && (oyRangeMax == 0.0)){
		oyMinValue = oyMaxValue = odata[0];
		for(x = 0; x < odata.size(); x++){
			if(odata[x] > oyMaxValue)
				oyMaxValue = odata[x];
			if(odata[x] < oyMinValue)
				oyMinValue = odata[x];
		}
		
		yScale = (oplot->height() - 4) / (oyMaxValue - oyMinValue);
		yOffset = (oplot->height() - 2) + oyMinValue * yScale;
		dprintf("YMin: %f YMax: %f\n", oyMinValue, oyMaxValue);
		dprintf("Height: %d Offset: %f Scale: %f\n", oplot->height(), yOffset, yScale);
	}
	else {
		yScale =  (oplot->height() - 4) / (oyRangeMax - oyRangeMin);
		yOffset = (oplot->height() - 2) + oyRangeMin * yScale;
		dprintf("YMin: %f YMax: %f\n", oyRangeMin, oyRangeMax);
		dprintf("Height: %d Offset: %f Scale: %f\n", oplot->height(), yOffset, yScale);
	}
	
	if((oxRangeMin == 0.0) && (oxRangeMax == 0.0)){
		xOffset = 0;
		xMin = 0;
		xMax = odata.size() - 1;
		xScale = double(oplot->width()) / (xMax - xMin);
	}
	else {
		xOffset = int(oxRangeMin);
		xMin = int(oxRangeMin);
		xMax = int(oxRangeMax);
		xScale = double(oplot->width()) / (xMax - xMin);
	}
	oxMinValue = xMin;
	oxMaxValue = xMax;
	dprintf("XMin: %d XMax: %d\n", xMin, xMax);
	dprintf("Width: %d Offset: %f Scale: %f\n", oplot->width(), xOffset, xScale);
	
	opoints.resize(xMax - xMin);

	for(i = 0; i < (xMax - xMin); i++){
		x = int(i * xScale);
		if(((xOffset + i) >= 0) && ((xOffset + i) < odata.size()))
			opoints[i] = QPoint(x, int(yOffset + yScale * -odata[int(xOffset) + i]));
		else
			opoints[i] = QPoint(0, 0);
	}
#ifdef ZAP
	for(i = 0; i < opoints.size(); i++){
		printf("%d,%d\n", opoints[i].x(), opoints[i].y());
	}
#endif
	if(oplotType == POINTS){
		p.setBrush(QBrush(red));
		p.setPen(QPen(red, 8));
		p.setPen(NoPen);
		
		for(i = 0; i < opoints.size(); i++){
			p.drawRect(opoints[i].x(), opoints[i].y(), 5, 5);
		}
//		p.drawPoints(opoints, 0, opoints.size());
	}
	else {
		p.drawPolyline(opoints, 0, opoints.size());
	}
	p.flush();
}

void BGraph::drawGrid(){
	QPainter	p;
	double		x;
	double		y;
	double		xs;
	double		ys;
	
	dprintf("DrawGrid: XStep: %d\n", oxGridStep);

	p.begin(oplot);
        p.setPen("black");
	p.setPen(Qt::DotLine);

	if(oxGridStep){
		xs = double(oplot->width()) / oxGridStep;
		for(x = xs; x < double(oplot->width()); x += xs){
			p.drawLine(int(x), 0, int(x), oplot->height());
		}
	}
	if(oyGridStep){
		ys = double(oplot->height()) / oyGridStep;
		for(y = ys; y < double(oplot->height()); y += ys){
			p.drawLine(0, int(y), oplot->width(), int(y));
		}
	}
}

void BGraph::drawLabels(){
	QPainter	p;
	int		w, h;
	BString		str;

	dprintf("DrawLabels\n");

	if(oxLabel.len()){	
		// X Label
		oxLabelBox->setFixedHeight(16);
		oxLabelBox->erase();
		w = oxLabelBox->width();
		h = oxLabelBox->height();

		p.begin(oxLabelBox);
       		p.setPen(blue);

		str.printf("%-12.6g", oxMinValue);
		p.drawLine(2, 0, 2, 6);
		p.drawText(QRect(8, 0, w - 16, h), AlignLeft, str.retStr());

		str.printf("%12.6g", oxMaxValue);
		p.drawLine(w - 3, 0, w - 3, 6);
		p.drawText(QRect(8, 0, w - 16, h), AlignRight, str.retStr());

		p.drawText(QRect(0, 0, w, h), AlignCenter, oxLabel.retStr());
		p.flush();	
		p.end();
	}
	else {
		ocorner->setFixedHeight(0);
		oxLabelBox->setFixedHeight(0);
	}
	
	if(oyLabel.len()){
		oyLabelBox->erase();
		h = oyLabelBox->height();
		w = oyLabelBox->width();

		p.begin(oyLabelBox);
       		p.setPen(blue);

		str.printf("%12.6g", oyMaxValue);
		p.drawLine(w - 5, 2, w, 2);
		p.drawText(QRect(0, 3, w, 16), AlignLeft, str.retStr());

		str.printf("%12.6g", oyMinValue);
		p.drawLine(w - 5, h - 3, w, h - 3);
		p.drawText(QRect(0, h - 16, w, h), AlignLeft, str.retStr());

#ifdef ZAP
		p.rotate(90);
		p.translate(0, -w);

		p.drawText(QRect(0, int(w * 0.25), h, int(w * 0.75)), AlignCenter, oyLabel.retStr());
#else
		p.drawText(QRect(0, int(h * 0.5 - 8), w, int(h * 0.5 + 8)), AlignLeft, oyLabel.retStr());
#endif
		p.flush();	
		p.end();
	}
	else {
	}
}

void BGraph::clear(){
	dprintf("Clear\n");
	oplot->erase();
	odata.clear();
}

#if DEBUG_MAIN

#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <qapp.h>
#include <qvbox.h>
#include <qtextedit.h>
#include <qpushbutton.h>
#include <qmainwindow.h>
#include <qframe.h>
#include <qdatetimeedit.h>
#include <math.h>

void usage(){
	fprintf(stderr, "Usage: testChart\n");
}

int main(int argc, char** argv){
	int		a;
	QApplication*	app;
	QMainWindow*	mw;
	QVBox*		v;
	BGraph*		p1;
	BGraph*		p2;
	BGraph*		p3;
	BGraph*		p4;
	BArray<float>	data;
	unsigned int	x;
	
	for(a = 1; a < argc; a++){
		if(argv[a][0] == '-'){
			switch(argv[a][1]){
			case 'd':
				if(argc <= (a + 1)){
					usage();
					return 1;
				}
//				setDebug(strtol(argv[++a], 0, 0));
				break;
			// QT Options
			case 'g':
				break;
			default:	usage();	return 1;
			}
		}
	}

	// Create Gui control class
	app = new QApplication(argc, argv);
	mw = new QMainWindow();
	app->setMainWidget(mw);
	mw->show();

	v = new QVBox(mw);
	mw->setCentralWidget(v);
	v->show();
	
	data.resize(100);
	for(x = 0; x < data.size(); x++){
		data[x] = sin((4.0 * 2.0 * 3.14 * x) / data.size());
	}

#ifdef ZAP
	p1 = new BGraph(v);
	p1->show();

	p1->setYLabel("FRef");	
//	p1->setXLabel("Samples");	
	p1->setData(data);

	p2 = new BGraph(v);
	p2->show();

	p2->setYRange(-0.555555, 2);	
//	p2->setYRange(-1.0, 1.0);	
	p2->setYLabel("FRef");	
	p2->setXLabel("Samples");	
	p2->setData(data);
#endif

	p3 = new BGraph(v);
	p3->show();

	p3->setYLabelWidth(100);
	p3->setGrid(8, 4);
	p3->setXRange(25, 75);	
	p3->setYLabel("FRef");	
	p3->setXLabel("Samples");	
	p3->setData(data);

#ifdef ZAP
	p4 = new BGraph(v);
	p4->show();

	for(x = 0; x < data.size(); x++){
		data[x] = 30040000.0 + 10000.0 * sin((4.0 * 2.0 * 3.14 * x) / data.size());
	}

	p4->setYLabelWidth(100);
	p4->setGrid(4, 4);
	p4->setYLabel("FRef");	
	p4->setXLabel("Samples");	
	p4->setData(data);
#endif
	app->exec();
	
	return 0;
}
#endif
