#include "MathPlotPanel.h"


MathPlotPanel::MathPlotPanel(QWidget * parent) : QWidget(parent){
	bgColor=Qt::lightGray;
	sigColor=Qt::darkBlue;
	gridColor=Qt::black;
	createMenus();
	createUI();
}

MathPlotPanel::~MathPlotPanel(){}

// Public Methods
Qt::GlobalColor MathPlotPanel::getColorFor(int index){
	Qt::GlobalColor SignalColors[] = {
		Qt::darkBlue,
		Qt::darkRed,
		Qt::darkGreen,
		Qt::blue,
		Qt::red,
		Qt::darkCyan,
		Qt::magenta,
		Qt::darkMagenta,
		Qt::darkYellow,
		Qt::black
	};

	return SignalColors[index%10];
}

int MathPlotPanel::getSignalsCount(){return sigList.size();}

bool MathPlotPanel::isEmpty(){return sigList.isEmpty();}

Signal MathPlotPanel::getSignal(int index){
	if (index >= sigList.size()) return Signal();
	return sigList[index].top();
}

bool MathPlotPanel::isAnalog(int i) {return analog[i];}

void MathPlotPanel::setSignal(int index,Signal &sig){
	if (index >= sigList.size()) return;
	sigList[index].clear();
	sigList[index].push(sig);
}
void MathPlotPanel::setAnalog(int i,bool a){
	if (analog[i]==a) return;	
	analog[i]=a;
}

void MathPlotPanel::addSignal(Signal & sig){
	QStack<Signal> tmp;
	tmp.push(sig);
	sigList.append(tmp);
	analog.append(false);
	redrawAll();
	emit signalAdded();
}

void MathPlotPanel::removeSignal(int index){
	sigList.removeAt(index);
	analog.removeAt(index);
	redrawAll();
}


// EVENTS Override
void MathPlotPanel::contextMenuEvent(QContextMenuEvent  * event){
	
	PlotPopUp->exec(event->globalPos());
}
// Move is needed else it is not working..
void MathPlotPanel::dragLeaveEvent(QDragLeaveEvent *){}
void MathPlotPanel::dragMoveEvent(QDragMoveEvent *){}

void MathPlotPanel::dragEnterEvent( QDragEnterEvent * event){
	if (event->mimeData()->hasFormat("text/signal")){
		event->accept();
	}
	else
		event->ignore();
}

void MathPlotPanel::dropEvent(QDropEvent *event){
	event->accept();
	addSignal(((Plot *)event->source())->getSignal());
}

void MathPlotPanel::paintEvent(QPaintEvent * /*event*/){
	QPainter painter(this);
	painter.drawImage(QPoint(0,0),drawArea);
}

void MathPlotPanel::mouseMoveEvent ( QMouseEvent * event ){
	if (isEmpty()) return;
	if (plotViewport.contains(event->pos())){

		Point tmpPoint = ImageToSignal(event->pos());
		QPainter painter(&drawArea);
		painter.fillRect(QRect(drawArea.width()-5-COORD_W,5,COORD_W,20),bgColor);
		painter.drawText(drawArea.width()-5-COORD_W,5+15,"x:"+QString::number(tmpPoint.x)+" y:"+QString::number(tmpPoint.y));
		repaint();
	}
}

// Private Methods
void MathPlotPanel::createUI(){
	// Init image first
	initDrawArea();
	setAcceptDrops(true);
	setMouseTracking (true);


	setFixedSize(drawArea.size());
}

void MathPlotPanel::createMenus(){
	PlotPopUp=new QMenu("Menu");
	// Actions    
	actionLoad = new QAction(this);
	actionLoad->setText(QApplication::translate("MainWin", "&Load Signal", 0, QApplication::UnicodeUTF8));
	connect(actionLoad,SIGNAL(triggered()),this,SLOT(load()));
	
	actionSaveAsImage = new QAction(this);
	actionSaveAsImage->setText(QApplication::translate("MainWin", "&Save As Image", 0, QApplication::UnicodeUTF8));
	actionSaveAsImage->setEnabled(false);
	connect(actionSaveAsImage,SIGNAL(triggered()),this,SLOT(saveAsImage()));

	PlotPopUp->addAction(actionLoad);
	PlotPopUp->addAction(actionSaveAsImage);
	PlotPopUp->addSeparator();
}

void MathPlotPanel::initDrawArea(){
	// Create Plot Area
	drawArea=QImage(
		DEF_PLOT_W + H_NUM_BORDER + DEF_BORDER,
		DEF_PLOT_H + 2*DEF_BORDER,
		QImage::Format_RGB32
	);

	plotViewport=QRect(H_NUM_BORDER,DEF_BORDER,DEF_PLOT_W+1,DEF_PLOT_H+1);
	
	QPainter painter(&drawArea);
	painter.fillRect(painter.viewport(),bgColor);

	painter.setPen(QPen(Qt::black,1));
	painter.drawRect(H_NUM_BORDER-1,DEF_BORDER-1,DEF_PLOT_W+2,DEF_PLOT_H+2);

	painter.fillRect(plotViewport,Qt::white);


}


void MathPlotPanel::redrawAll(){
	// Alway Clear Area... If is last sig removed
	// we MUST clear it from screen
	initDrawArea();
	if (isEmpty()) return;

	calcMinMaxValues();
	calcSteps();
	drawAxes();
	for (int i=0; i<sigList.size(); i++) 
		if (isAnalog(i)) drawAnalogSignal(i);
		else drawDigitalSignal(i);
	addNumbers();

	actionSaveAsImage->setEnabled(true);

	update();
}

void MathPlotPanel::calcSteps(){
	YStep=(DEF_PLOT_H)/(totalMaxY-totalMinY);
	XStep=(DEF_PLOT_W)/(totalMaxX-totalMinX);

}

QPointF MathPlotPanel::SignalToImage(const Point &p){
	double ny=(totalMaxY-p.y)*YStep+DEF_BORDER;
	double nx=(p.x-totalMinX)*XStep+H_NUM_BORDER;
	return QPointF(nx,ny);
}

Point MathPlotPanel::ImageToSignal(const QPointF &p){
	double x=(p.x()-H_NUM_BORDER)/XStep + totalMinX;
	double y=totalMaxY - ( (p.y()-DEF_BORDER ) / YStep );
	return Point(x,y);
}

void MathPlotPanel::drawDigitalSignal(int index){

	QPainter painter(&drawArea);
	painter.setPen(QPen(getColorFor(index),2));

	for (ul i=0; i<sigList[index].top().size(); i++){
		painter.drawPoint( SignalToImage(sigList[index].top()[i]) );
	}	
}

void MathPlotPanel::drawAnalogSignal(int index){

	QPainter painter(&drawArea);
	painter.setPen(QPen(getColorFor(index),2));

	for (ul i=1; i<sigList[index].top().size(); i++){
		painter.drawLine( SignalToImage(sigList[index].top()[i]) , SignalToImage(sigList[index].top()[i-1]) );
	}	
}

void MathPlotPanel::addNumbers(){
	// round numbers
	long long int round_num=(long long int)pow(10.0,DISPLAY_DIG);

	QPainter painter(&drawArea);

	for (int i=H_NUM_BORDER; i<=DEF_PLOT_W+H_NUM_BORDER; i+=50){
		int y = DEF_PLOT_H+DEF_BORDER+1;
		painter.drawLine(i,y,i,y+4);
		double pos=((int)(ImageToSignal(QPointF(i,y)).x*round_num))/(double)round_num;
		painter.drawText(i-2,y+15,QString::number(pos));
	}

	for (int i=DEF_BORDER; i<=DEF_PLOT_H+DEF_BORDER; i+=50){
		int x = H_NUM_BORDER-1;
		painter.drawLine(x-4,i,x,i);
		double pos=ImageToSignal(QPointF(x,i)).y;
		painter.drawText(x-55,i-5,50,10,Qt::AlignRight|Qt::TextDontClip,QString::number(pos));
	}
}

void MathPlotPanel::drawAxes(){

	QPainter painter(&drawArea);
	painter.setPen(QPen(Qt::lightGray,2));

	//y'y (+1 for pen size 2)
	if (totalMaxX*totalMinX<=0) {
		QPointF zero=SignalToImage(Point(0,0));
		painter.drawLine((int)zero.x()+1,DEF_BORDER+1,(int)zero.x()+1,DEF_BORDER+DEF_PLOT_H);
	}

	// x'x
	if (totalMaxY*totalMinY<=0) {
		QPointF zero=SignalToImage(Point(0,0));
		painter.drawLine(H_NUM_BORDER+1,(int)zero.y(),H_NUM_BORDER+DEF_PLOT_W,(int)zero.y());
	}
}

void MathPlotPanel::calcMinMaxValues(){
	if (isEmpty()) return;

	totalMaxX = sigList[0].top().getMaxX();
	totalMinX = sigList[0].top().getMinX();
	totalMaxY = sigList[0].top().getMaxY();
	totalMinY = sigList[0].top().getMinY();	

	for (int i=1; i<sigList.size(); i++ ){
		if (sigList[i].top().getMaxX()>totalMaxX)
			totalMaxX = sigList[i].top().getMaxX();

		if (sigList[i].top().getMinX()<totalMinX)
			totalMinX = sigList[i].top().getMinX();
		
		if (sigList[i].top().getMaxY()>totalMaxY)
			totalMaxY = sigList[i].top().getMaxY();

		if (sigList[i].top().getMinY()<totalMinY)
			totalMinY = sigList[i].top().getMinY();
	}
}

// SLOTS
void MathPlotPanel::load(){
	//File Dialog
	QFileDialog dialog(this,tr("Load Signal From File"));
	
	// Initial Directory
	QString dir=QDir::homePath();
	
	dialog.setDirectory(dir);
	
	// File Extension Filters
	 QStringList filters;
		filters << "DAT Files (*.dat *.data)"
			<< "Sig Files (*.sig )"
			<< "Any files (*.*)";
	
	dialog.setFilters(filters);
	dialog.setFileMode(QFileDialog::ExistingFile);
	dialog.setViewMode(QFileDialog::Detail);
	dialog.setAcceptMode(QFileDialog::AcceptOpen);
	
	// Execute
	QString fileName;
	 if (dialog.exec()) 
	 	fileName = dialog.selectedFiles()[0];
	 else 
	 	return;
	 
	 Signal toAdd(fileName.toStdString().c_str());
	 if (toAdd.isEmpty()) return;
	 addSignal(toAdd);
	 repaint();
}

void MathPlotPanel::saveAsImage(){
		//File Dialog
	QFileDialog dialog(this,tr("Save As Image"));
	
	// Initial Directory
	QString dir=QDir::homePath();
	
	dialog.setDirectory(dir);

	dialog.setViewMode(QFileDialog::Detail);
	dialog.setAcceptMode(QFileDialog::AcceptSave);
	
	// Supported Image Extension Filters
	QStringList filters;
		filters << "PNG Image (*.png)";
	dialog.setFilters(filters);
	
	// Execute
	QString fileName;
	if (dialog.exec()){
		fileName = dialog.selectedFiles()[0];
		QFileInfo imginfo(fileName);
		if (imginfo.suffix()!="png")
			fileName+=".png";
 	}
	else
		return;
		
	QFile imgfile(fileName);
	if (imgfile.exists()) imgfile.remove();

	drawArea.save(fileName,"PNG");
}



