/*
* Title:	PupeDiagnosticsWin.cpp 
* Author:	M.Thomas BEAM Ltd
* Date:		2007-02-13
*
* Contents:	Status Display
*
* Mod Rec:
*
*/


#include <PupeDiagnosticsWin.h>
#include <qlayout.h>
#include <qvbox.h>
#include <qlabel.h>
#include <qtable.h>
#include <BError.h>
#include <TmsD.h>
#include <qvalidator.h>
#include <qmessagebox.h>
#include <qgroupbox.h>
#include <qvgroupbox.h>
#include <BQComboBox.h>
#include <math.h>
#include <Globals.h>
#include <qfiledialog.h>
#include <fftw3.h>

const int	BUTTON_WIDTH = 200;
const int	LINE_EDIT_MAX_WIDTH = 200;
const int	GRAPHS_TOTAL_HEIGHT = 620;
const int	GRAPHS_X_LEDGEND_HEIGHT = 50;

using namespace std;
using namespace Tms;

#define	BIT(v,b)	((v >> b) & 1)


PupeDiagBitDefinition	defSource0[] = {
//	name		startBit    nBits  isSigned   gain

	{ "fref",		63, 	1, 	0, 	1 },
	{ "PllMsb",		62, 	1, 	0,	-1 },	
	{ "PhaseTableMsb",	61, 	1, 	0,	-1 },
	{ "lo1",		60, 	1, 	0,	1 },
	{ "sigma",		32, 	12,	1,	4 },
	{ "lo2",		56, 	1, 	0,	1 },
	{ "gate",		49, 	1, 	0,	1 },
	{ "blr",		48, 	1, 	0,	1 },
	{ "mean1",		50, 	1, 	0,	1 },
	{ "mean2",		51, 	1, 	0,	1 },
	{  "rfSelect1",		52, 	1, 	0,	1 },
	{  "rfSelect2",		53, 	1, 	0,	1 },
	{  "selFilter",		54, 	1, 	0,	1 },
	{  "SwitchState",	44,	4, 	0,	1 },
	{  "dds_freq",		0, 	26, 	1,	64 },
	{ 0,0,0,0,0 }
};

PupeDiagBitDefinition	defSource1[] = {
	{ "FREF",		63,	1, 	0,	1 },
	{ "PllMsb", 		62, 	1, 	0,	-1 },
	{ "PhaseTableMsb", 	61, 	1, 	0,	-1 },
	{ "LO_pulse1", 		60, 	1, 	0,	1 },
	{ "sigma", 		57, 	3, 	1,	2048 },
	{ "LO_pulse2", 		56, 	1, 	0,	1 },
	{ "mult_out1", 		38,	14, 	1,	1 },
	{ "mult_out2", 		24, 	14, 	1,	1 },
	{ "f_error", 		0, 	24, 	1,	1 },
	{ 0,0,0,0,0 }
};					
		// Source = 2

PupeDiagBitDefinition	defSource2[] = {
	{ "FREF",		63, 	1, 	0,	1 },
	{ "PllMsb",		62, 	1, 	0,	-1 },
	{ "PhaseTableMsb",	61, 	1, 	0,	-1 },
	{ "LO1",		60,	1,	0,	1 },
	{ "sigma",		57,	3,	1,	2048 },
	{ "LO2",		56,	1,	0,	1 },
	{ "Gate",		55,	1,	0,	1 },
	{ "B0",			32,	23,	1,	1 },
	{ "Result0",		0,	32,	1,	1 },
	{ 0,0,0,0,0 }
};
	
PupeDiagBitDefinition	defSource3[] = {
	{ "fref",		63, 	1, 	0,	1 },
	{ "PllMsb",		62, 	1, 	0,	-1 },
	{ "PhaseTableMsb",	61, 	1, 	0,	-1 },
	{ "lo1",		60, 	1, 	0,	1 },
#ifdef ZAP
	{ "hchange_temp",	59, 	1, 	0,	1 },
	{ "hchange_switch",	58, 	1, 	0,	1 },
	{ "ms_pulse_reg",	57, 	1, 	0,	1 },
#else
	{ "SigmaDelayed",	57,	3, 	1,	2048 },
#endif
	{ "lo2",		56, 	1, 	0,	1 },
	{ "BLR", 		55, 	1, 	0,	1 },
	{ "Gate", 		54, 	1, 	0,	1 },
	{ "Sigma", 		40,	14, 	1,	1 },
	{ "DeltaX", 		26,	14, 	1,	1 },
	{ "DeltaY", 		12,	14, 	1,	1 },
	{ 0,0,0,0,0 }
};

PupeDiagBitDefinition	defSigma[] = {
	{ "Sigma", 		40,	14, 	1,	1 },
	{ 0,0,0,0,0 }
};

PupeDiagBitDefinition	defDeltaX[] = {
	{ "DeltaX", 		26,	14, 	1,	1 },
	{ 0,0,0,0,0 }
};

PupeDiagBitDefinition	defDeltaY[] = {
	{ "DeltaY", 		12,	14, 	1,	1 },
	{ 0,0,0,0,0 }
};

PupeDiagBitDefinition	defSource0trigger[] = {
	{ "FREF", 	 	7, 	1,	0,	1 },
	{ "HCHANGE",	 	6, 	1,	0,	1 },
	{ "INJECTION",	 	5, 	1,	0,	1 },
	{ "CAL_STOP",	 	4, 	1, 	0,	1 },
	{ "CAL_START",	 	3, 	1,	0,	1 },
	{ "CYCLE_STOP",	 	2, 	1,	0,	1 },
	{ "CYCLE_START", 	1, 	1, 	0,	1 },
	{ "10MHz",  		0, 	1, 	0,	1 },
};

typedef BArray<float>	BFArray;

static ComboDefinition sourceDef[] = {
	{ 0,	"Source 0",	0,	1 },
	{ 1,	"Source 1",	1,	0 },
 	{ 2,	"Source 2",	2,	0 },
 	{ 3,	"Source 3",	3,	0 },
 	{ 4,	"Sigma",	4,	0 },
 	{ 5,	"DeltaX",	5,	0 },
 	{ 6,	"DeltaY",	6,	0 },
	{ 0,0,0,0 }
};

static ComboDefinition clockDef[] = {
 	{ 0,	"ADC Clock",		ClkAdcDiv_1,		1 },
 	{ 1,	"ADC Clock/2",		ClkAdcDiv_2,		0 },
	{ 2,  	"ADC Clock/5",		ClkAdcDiv_5,		0 },
	{ 3,  	"ADC Clock/10",		ClkAdcDiv_10,		0 },
	{ 4,  	"ADC Clock/20",		ClkAdcDiv_20,		0 },
	{ 5,  	"ADC Clock/50",		ClkAdcDiv_50,		0 },
	{ 6,  	"ADC Clock/100",	ClkAdcDiv_100,		0 },
	{ 7,  	"ADC Clock/200",	ClkAdcDiv_200,		0 },
	{ 8, 	"ADC Clock/500",	ClkAdcDiv_500,		0 },
	{ 9, 	"ADC Clock/1000",	ClkAdcDiv_1000,		0 },
	{ 10, 	"ADC Clock/2000",	ClkAdcDiv_2000,		0 },
	{ 11, 	"ADC Clock/5000",	ClkAdcDiv_5000,		0 },
	{ 12, 	"ADC Clock/10000",	ClkAdcDiv_10000,	0 },
	{ 13, 	"ADC Clock/20000",	ClkAdcDiv_20000,	0 },
	{ 14, 	"ADC Clock/50000",	ClkAdcDiv_50000,	0 },
	{ 15, 	"ADC Clock/100000",	ClkAdcDiv_100000,	0 },
	{ 16,	"Ms",			ClkMs,			0 },
	{ 17,	"FREF",			ClkFref,		0 },
	{ 0,0,0,0 }
};

Int32	getBitValue(UInt64 value, int startBit, int nBits, int s){
	Int64	v;
	UInt64	bm = (1ULL << nBits) - 1;
	UInt64	sm = (1ULL << (nBits - 1));
	
	v = (value >> startBit) & bm;
	if(s){
		v = -(v & sm) | v;
	}
	return v;
}

PupeDiagnosticsWin::PupeDiagnosticsWin(QWidget* w,Control& c) : ocontrol(c)  {
	QVBox* 		mv = new QVBox(this);
			
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			oplots.append(new BGraph(mv));
			ographControls = new BGraphControlBasic(mv);

	QValidator* 		validator = new QIntValidator(0, (int)pow(2.0,31.0), this );
	QRegExpValidator* 	validatorHex = new QRegExpValidator(this);
	QRegExp		rx;
	
	QWidget*	t = new QWidget(mv,"dataEntry");

	QVGroupBox*	masksG = new QVGroupBox(t);
	QWidget*	masks = new QWidget(masksG);
	QGridLayout* 	v1 = new QGridLayout(masks);

	QHBox*		h = new QHBox(mv,"controls");
	QGridLayout* 	v = new QGridLayout(t);

	int		row = 0;
	int		col = 0;
	
	
	rx.setPattern("0x[0-9,a-f][0-9,a-f]");
	validatorHex->setRegExp(rx);
	

	orefreshButton 	= new QPushButton("Refresh Data",h,"refreshButton");
	osaveButton 	= new QPushButton("Save To File",h,"saveButton");

	okst 		= new QCheckBox(t,"");
	ochannel 	= new QSpinBox(1,60,1,t,"");
	osource 	= new BQComboBox(sourceDef,t,"");
	oclock 		= new BQComboBox(clockDef,t,"");

	ostartTime 	= new QLineEdit(t,"");ostartTime->setValidator(validator);
	opostTriggerDelay = new QLineEdit(t,"");opostTriggerDelay->setValidator(validator);
	v1->addWidget(oClock	  = new QCheckBox("Clock",masks,""),1,3);
	v1->addWidget(oCycleStart = new QCheckBox("Cycle Start",masks,""),1,2);
	v1->addWidget(oCycleStop  = new QCheckBox("Cycle Stop",masks,""),1,1);
	v1->addWidget(oCalStart   = new QCheckBox("Cal Start",masks,""),1,0);
	v1->addWidget(oCalStop	  = new QCheckBox("Cal Stop",masks,""),0,3);
	v1->addWidget(oInjection  = new QCheckBox("Injection",masks,""),0,2);
	v1->addWidget(oHChange	  = new QCheckBox("H Change",masks,""),0,1);
	v1->addWidget(oFref	  = new QCheckBox("Fref",masks,""),0,0);
	v1->addWidget(oState      = new QCheckBox("In State:",masks,""),2,0);    // JMB
	v1->addWidget(oStateV     = new QSpinBox(0,15,1,masks,""),2,1);             // JMB
	oInjection->setChecked(true);
	oStateV->setEnabled(FALSE);

	otriggerSourceData 	= new QCheckBox(t,"");
	otriggerAnd 	= new QCheckBox(t,"");
	otriggerStore 	= new QCheckBox(t,"");

	
	ostartTime->setText("0");
	opostTriggerDelay->setText("0");
	
	masksG->setTitle("Trigger Mask");
	
	
	h->setMargin(4);
	ochannel->setWrapping(true);

	orefreshButton->setMaximumWidth(BUTTON_WIDTH);
	osaveButton->setMaximumWidth(BUTTON_WIDTH);

	ochannel->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	osource->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	oclock->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	ostartTime->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	opostTriggerDelay->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	otriggerAnd->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	otriggerStore->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	otriggerSourceData->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	oStateV->setMaximumWidth(LINE_EDIT_MAX_WIDTH);
	
	col = 0;row = 0;
	v->setSpacing(4);	// JMB
  	v->addMultiCellWidget(masksG      ,row,3,col+4,col+4);	// JMB

	v->addWidget(new QLabel("Channel",t,""),row,col);		v->addWidget(ochannel,row,col+1);	row++;
	v->addWidget(new QLabel("Source",t,""),row,col);		v->addWidget(osource,row,col+1);	row++;
	v->addWidget(new QLabel("Clock",t,""),row,col); 	    	v->addWidget(oclock,row,col+1);	row++;
	v->addWidget(new QLabel("StartTime (ms)",t,""),row,col);     	v->addWidget(ostartTime,row,col+1);	row++;
	v->addWidget(new QLabel("Post Trigger Delay (Clk)",t,""),row,col);   	v->addWidget(opostTriggerDelay,row,col+1);	row++;
	
	col = 2;row = 0;
	v->addWidget(new QLabel("Trigger Src Data",t,""),row,col);   	v->addWidget(otriggerSourceData,row,col+1);	row++;
	v->addWidget(new QLabel("Trigger And",t,""),row,col);     	v->addWidget(otriggerAnd,row,col+1);	row++;
	v->addWidget(new QLabel("Trigger Store",t,""),row,col);     	v->addWidget(otriggerStore,row,col+1);	row++;

	v->addWidget(new QLabel("Save Format Kst",t,""),row,col);  	v->addWidget(okst      ,row,col+1);	row++;
	
	BIter	i;
	for (oplots.start(i);! oplots.isEnd(i);oplots.next(i)) {
		ographControls->addGraph(oplots[i]);
		oplots[i]->setGrid(16,0);
		oplots[i]->installEventFilter(this);
	}
	connect(orefreshButton,SIGNAL(clicked()),this,SLOT(refresh()));
	connect(osaveButton,SIGNAL(clicked()),this,SLOT(saveFile()));
	connect(oState,SIGNAL(clicked()),this,SLOT(statebuttonupdate()));
}
	
PupeDiagnosticsWin::~PupeDiagnosticsWin() {}

	
void PupeDiagnosticsWin::show() {
	QWidget::show();
}	


/*
 * Dim or brighten the oStateV spin button according to the state of the oState
 * check button.
 * JMB
 */
void PupeDiagnosticsWin::statebuttonupdate() {
	if (oState->isChecked())
		oStateV->setEnabled(TRUE);
	else
		oStateV->setEnabled(FALSE);
}

void PupeDiagnosticsWin::refresh() {
	BError	err;
	int	source;
	
	Tms::TestCaptureInfo 	captureInfo;
	Tms::PuChannel 		puPhysChannel;
	
	// Build request
	source = (int)osource->getValue();
	if((source == 4) || (source == 5) || (source == 6))
		source = 3;
	
	captureInfo.source = source;;
	captureInfo.clock = (int)oclock->getValue();
	captureInfo.startTime = ostartTime->text().toUInt();
	captureInfo.postTriggerDelay = opostTriggerDelay->text().toUInt();
	captureInfo.triggerSourceData = otriggerSourceData->isChecked();
	// Slip in the state-qualified trigger enable. - JMB
	captureInfo.triggerAnd = otriggerAnd->isChecked() | oState->isChecked() << 1;   // JMB 
	captureInfo.triggerStore = otriggerStore->isChecked();
	
	captureInfo.triggerMask =
		oClock->isChecked() << 0 |       
		oCycleStart->isChecked() << 1 |       
		oCycleStop->isChecked() << 2 |       
		oCalStart->isChecked() << 3 |       
		oCalStop->isChecked() << 4 |       
		oInjection->isChecked() << 5 |       
		oHChange->isChecked() << 6 |       
		oFref->isChecked() << 7 |
		oStateV->value() << 8;        // JMB

	if (err = ocontrol.getPuChannel (ochannel->value(),puPhysChannel)) {
		warningDialog("Reading Test Data",err);
		return;
	}

	if (err = ocontrol.getTestData(puPhysChannel,captureInfo,odata)) {
		warningDialog("Reading Test Data",err);
		return;
	}
	setPlotData();
}	

void	PupeDiagnosticsWin::doFft(){
	BArray<float>	dataIn;
	BArray<float>	dataOut;
	int		n;
	fftw_complex*	in;
	fftw_complex*	out;
	fftw_plan	p;
	int		i;
	
	oplots[0]->getData(dataIn);
	n = dataIn.size();

	in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * n);
	out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * n);
	p = fftw_plan_dft_1d(n, in, out, FFTW_FORWARD, FFTW_ESTIMATE);

	for(i = 0; i < n; i++){
		in[i][0] = dataIn[i] / 8192.0;
//		in[i][0] = 1.0 * sin(100 * 3.14 * i / n) + 0.5 * sin(n / 4 * 2 * 3.14 * i / n);
		in[i][1] = 0;
	}
	
	fftw_execute(p);
	
	dataOut.resize(n);
	for(i = 0; i < n; i++){
		out[i][0] /= (n / 2.0);
		out[i][1] /= (n / 2.0);
		dataOut[i] = 10 * log10(out[i][0] * out[i][0] + out[i][1] * out[i][1]);
	}
	
	dataOut[0] = dataOut[1];

	setGraphHeights(2);

	oplots[1]->setData(dataOut);
	oplots[0]->setXLabel("Sample");
	oplots[1]->setYLabel("Power dB");
	oplots[1]->setXLabel("Frequency");

	fftw_destroy_plan(p);
	fftw_free(in);
	fftw_free(out);
}

void	PupeDiagnosticsWin::setPlotData() {
	BIter	i;	
	// Bit Pattern Source 0
	int 	source = (int)osource->getValue();


	switch (source) {
	case 0:
		if (! otriggerStore->isChecked())
			updateGraphs(defSource0);
		else 
			updateGraphs(defSource0trigger);
		break;
	case 1: updateGraphs(defSource1);
		break;
	case 2: updateGraphs(defSource2);
		break;
	case 3: updateGraphs(defSource3);
		break;
	case 4:	updateGraphs(defSigma);
		doFft();
		break;
	case 5:	updateGraphs(defDeltaX);
		doFft();
		break;
	case 6:	updateGraphs(defDeltaY);
		doFft();
		break;
	default:
		BError	err;
		err.set(1,"Unsupported source type");
		warningDialog("Viewing Pupe Diagnostics",err);
		break;
	}
	ographControls->setMax(odata.size());
}




void	PupeDiagnosticsWin::saveFile() {
	BError		err;
	BString		fname;
	BString 	msg;
	int 		source = (int)osource->getValue();
	int		n;

	fname.printf("pupeSource%d.txt",(int)osource->getValue());	

	QFileDialog saveDialog(
        	            ".",
                	    "Pupe Diagnostics Dump File (*.txt)",
                	    this,
                	    "save file dialog",
                	    "Choose a file" );
	saveDialog.setMode(QFileDialog::QFileDialog::AnyFile);
	saveDialog.setSelection(fname.retStr());
		
	switch (n = saveDialog.exec()) {
	case 0:		return;		//Cancel Button
			break;	
	case 1:		break;		//Save Button
		default:	printf("Unknown Button (%d)\n",n);
	}
	if ((fname = saveDialog.selectedFile().ascii()) == "")
		return;
	if (ofile.open(fname,"w+")) {
		warningDialog("PupeDiagnostics",BString("Cannot open save file (") + fname + ")");
		return;
	}

	switch (source) {
	case 0:
		if (! otriggerStore->isChecked())
			writeFile(defSource0);
		else 
			writeFile(defSource0trigger);
		break;
	case 1: writeFile(defSource1);
		break;
	case 2: writeFile(defSource2);
		break;
	case 3: writeFile(defSource3);
		break;
	case 4: writeFile(defSigma);
		break;
	case 5: writeFile(defDeltaX);
		break;
	case 6: writeFile(defDeltaY);
		break;
	default:
		BError	err;
		err.set(1,"Unsupported source type");
		warningDialog("Viewing Pupe Diagnostics",err);
		break;
	}
	ofile.close();
	msg =  BString("File Saved (") + fname + ")";
	gstatusbar->message(msg.retStr(),2000);
}	
	


// Utility routines

void	PupeDiagnosticsWin::updateGraphs(PupeDiagBitDefinition	sourceDefs[]) {
	PupeDiagBitDefinition	def;
	int 			i = 0;
	
	while (sourceDefs[i].name) {
		def = sourceDefs[i];
		oplots[i]->setData(extractBitLinear(def));
		oplots[i]->setYLabel(def.name);
		i++;
	}	
	setGraphHeights(i);
	setXAxisLabel();
}

BArray<float>	PupeDiagnosticsWin::extractBitLinear(PupeDiagBitDefinition def) {
	unsigned int	n;
	float		f;
	BArray<float>	data;

	data.resize(odata.size());
	for(n = 0; n < odata.size(); n++){
		f = getBitValue(odata[n], def.startBit, def.nBits,def.isSigned);
		data[n] = def.gain * f;
	}
	return data;
}


void	PupeDiagnosticsWin::writeFile(PupeDiagBitDefinition defs[]) {
	unsigned int	n;
	float		f;
	PupeDiagBitDefinition	def;

	if (okst->isChecked()) {
		int 	i = 0;
		while (defs[i].name)
			ofile.printf("%s ",defs[i++].name);
		ofile.printf("\n");
	}	

	for(n = 0; n < odata.size(); n++){
		int 			i = 0;
		while (defs[i].name) {
			def = defs[i];
			f = getBitValue(odata[n], def.startBit, def.nBits,def.isSigned);
			f *= def.gain;
			ofile.printf("%f ",f);
			i++;
		}
		ofile.printf("\n");
	}
}

void	PupeDiagnosticsWin::setGraphHeights(int num) {
	BIter	i;
	int	height = (GRAPHS_TOTAL_HEIGHT - GRAPHS_X_LEDGEND_HEIGHT)/num;
	int	n = 1;
	
	for (oplots.start(i);! oplots.isEnd(i);oplots.next(i)) {
		oplots[i]->show();
		if (n < num) {
			oplots[i]->setMaximumHeight(height);
			oplots[i]->setMinimumHeight(height);
		}
		else if ( n == num) {
			oplots[i]->setMinimumHeight(height + GRAPHS_X_LEDGEND_HEIGHT);
		}
		else {
			oplots[i]->hide();
			oplots[i]->setMinimumHeight(height);
		}
		n++;
	}
}

void	PupeDiagnosticsWin::setXAxisLabel() {
	BIter	i,o;
	
	for (oplots.start(i);! oplots.isEnd(i);oplots.next(i)) {
		oplots[i]->setXLabel("");
	}		
	for (oplots.start(i);! oplots.isEnd(i);oplots.next(i)) {
		if (oplots[i]->isHidden())
			break;
		o = i;
	}
	oplots[o]->setXLabel("Sample");
}

bool PupeDiagnosticsWin::eventFilter( QObject *o, QEvent *e )
    {
        if ( e->type() == QEvent::Wheel ) {
        	QWheelEvent*	we = (QWheelEvent*)e;
	    
	    	int	delta = (we->delta() > 0) ? -1 : 1;
		ographControls->scroll(delta);
            return TRUE; 
        } else {
            // standard event processing
            return FALSE;
        }
    }

void	PupeDiagnosticsWin::warningDialog(BString title, BError err){
	BString		m;
	QMessageBox	b;
	
	m = BString("<h5>") + title + "</h5><p>" + err.getString() + "</p>";
	b.setMinimumWidth(300);
	b.setCaption("tmsControlGui - Warning");
	b.setIcon(QMessageBox::Warning);
	b.setText(m.retStr());
	b.exec();
}
