#include "specwidget.h"
#include "ui_specwidget.h"
#include "qtgui/bookmarks.h"
#include <QSettings>
#include <QDir>
#include <QDateTime>
#include <QFileInfo>
#include <QFileDialog>
specWidget::specWidget(QWidget *parent) :
	QWidget(parent),
	ui(new Ui::specWidget),	
	m_attenMod(new QStandardItemModel(this)),
	m_runth(new uhd_io_thread(this)),
	m_saveth(new uhd_io_thread(this)),
	m_bSaveToFile(false)
{
	ui->setupUi(this);
	Bookmarks::create();

	ui->plotter->setFftFill(false);
	ui->plotter->setPeakHold(true);
	ui->plotter->setFftPlotColor(QColor(255,255,255));
	ui->plotter->setRunningState(true);
	ui->plotter->setFreqUnits(1000000);//MHz

	//选择带宽
	ui->plotter->setFilterBoxEnabled(true);


	ui->plotter->setTooltipsEnabled(true);
	ui->plotter->setPeakDetection(false,128.0);

	m_attenMod->appendRow(new QStandardItem("TX/RX"));
	m_attenMod->appendRow(new QStandardItem("RX2"));
	m_attenMod->appendRow(new QStandardItem("RX"));
	m_attenMod->appendRow(new QStandardItem("TX"));
	ui->comboBox_atn->setModel(m_attenMod);

	//连接
	//connect (ui->plotter,&CPlotter::newDemodFreq,ui->freqCtrl,&CFreqCtrl::setFrequency);
	connect (ui->plotter,&CPlotter::newCenterFreq,this, &specWidget::evt_newCenterFreq);
	connect (ui->plotter,&CPlotter::newDemodFreq,this, &specWidget::evt_newDemodFreq);
	connect (ui->plotter,&CPlotter::newDemodFreq,this, &specWidget::slot_newDemodFreq);
	connect (ui->plotter,&CPlotter::newLowCutFreq,this, &specWidget::evt_newLowCutFreq);
	connect (ui->plotter,&CPlotter::newHighCutFreq,this, &specWidget::evt_newHighCutFreq);
	connect (ui->plotter,&CPlotter::newFilterFreq,this, &specWidget::evt_newFilterFreq);
	connect (ui->plotter,&CPlotter::pandapterRangeChanged,this, &specWidget::evt_pandapterRangeChanged);
	connect (ui->plotter,&CPlotter::newZoomLevel,this, &specWidget::evt_newZoomLevel);
	connect (ui->freqCtrl,&CFreqCtrl::newFrequency,this, &specWidget::setCenterFreq);

	loadSettings();

}

specWidget::~specWidget()
{
	freeFFT();

	delete ui;
}

void specWidget::slot_newDemodFreq(qint64 freq, qint64 delta)/* delta is the offset from the center */
{
	qint64 offfreq = freq - ui->plotter->getCenterFreq();
	if (ui->plotter->getSpanFreq()==0)
		return;
	double ratio = offfreq *1.0/ ui->plotter->getSpanFreq() + 0.5;

	int v = m_nFFTSize * ratio + .5;
	if (v<0) v = 0;

	if (v>=m_nFFTSize)
		v = m_nFFTSize - 1;
	m_nCurrentCenter = v;

}

void specWidget::setNewFftData(const double *  rfftData, int size)
{
	ui->plotter->setNewFftData(rfftData,size);
}
void specWidget::setNewFftDataWf(const double *  rfftData, const double *  rwfData, int size)
{
	ui->plotter->setNewFftData(rfftData,rwfData,size);
}
void specWidget::setMaxPixmapSize(int w, int h)
{
	ui->plotter->setMaxPixmapSize(QSize(w,h));
}
void specWidget::setRunningState(bool running)
{
	ui->plotter->setRunningState(running);
}
void specWidget::setClickResolution(int clickres)
{
	ui->plotter->setClickResolution(clickres);
}
void specWidget::setFilterClickResolution(int clickres)
{
	ui->plotter->setFilterClickResolution(clickres);
}
void specWidget::setFilterBoxEnabled(bool enabled)
{
	ui->plotter->setFilterBoxEnabled(enabled);
}
void specWidget::setCenterLineEnabled(bool enabled)
{
	ui->plotter->setCenterLineEnabled(enabled);
}
void specWidget::setTooltipsEnabled(bool enabled){
	ui->plotter->setTooltipsEnabled(enabled);
}
void specWidget::setBookmarksEnabled(bool enabled){
	ui->plotter->setBookmarksEnabled(enabled);
}
void specWidget::setCenterFreq(quint64 f){
	if (usrp)
	{
		using uhd::tune_request_t;
		tune_request_t tune_request_rx = tune_request_t((double)f, 0);
		usrp->set_rx_freq(tune_request_rx,m_channel);
		ui->plotter->setCenterFreq(usrp->get_rx_freq(m_channel));
	}
	else
	{
		ui->plotter->setCenterFreq(f);
	}
	if (ui->doubleSpinBox_center->value() * 1e6 !=ui->freqCtrl->getFrequency())
		ui->doubleSpinBox_center->setValue(ui->freqCtrl->getFrequency()/1e6);
	//ui->plotter->setCenterFreq(f);
}
void specWidget::setFreqUnits(qint32 unit){
	ui->plotter->setFreqUnits(unit);
}
void specWidget::setDemodCenterFreq(quint64 f){
	ui->plotter->setDemodCenterFreq(f);
}
void specWidget::setFilterOffset(qint64 freq_hz){
	ui->plotter->setFilterOffset(freq_hz);
}
qint64 specWidget::getFilterOffset(void){
	return ui->plotter->getFilterOffset();
}
int specWidget::getFilterBw(){
	return ui->plotter->getFilterBw();
}
void specWidget::setHiLowCutFrequencies(int LowCut, int HiCut){
	ui->plotter->setHiLowCutFrequencies(LowCut,HiCut);
}
void specWidget::getHiLowCutFrequencies(int *LowCut, int *HiCut){
	ui->plotter->getHiLowCutFrequencies(LowCut,HiCut);
}
void specWidget::setDemodRanges(int FLowCmin, int FLowCmax, int FHiCmin, int FHiCmax, bool symetric){
	ui->plotter->setDemodRanges(FLowCmin, FLowCmax, FHiCmin, FHiCmax, symetric);
}
void specWidget::setSpanFreq(quint32 s){
	ui->plotter->setSpanFreq(s);
}
void specWidget::setMaxBandiwdth(int m){
	ui->plotter->setMaxBandiwdth(m);
}
void specWidget::setHdivDelta(int delta){
	ui->plotter->setHdivDelta(delta);
}
void specWidget::setVdivDelta(int delta){
	ui->plotter->setVdivDelta(delta);
}
void specWidget::setFreqDigits(int digits){
	ui->plotter->setFreqDigits(digits);
}
void specWidget::setSampleRate(double rate){
	if (usrp)
	{
		double bw = rate;
		if (bw >56e6)
			bw = 56e6;
		if (bw <200000)
			bw = 200000;
		fprintf(stderr,"bw = %lf\n",bw);
		usrp->set_rx_rate(rate);
		usrp->set_rx_bandwidth(bw,m_channel);
		ui->plotter->setSampleRate(usrp->get_rx_rate(m_channel));
	}
	else
	{
		ui->plotter->setSampleRate(rate);
	}
}
double specWidget::getSampleRate(void){
	return ui->plotter->getSampleRate();
}
void specWidget::setFftCenterFreq(qint64 f){
	ui->plotter->setFftCenterFreq(f);
}
int     specWidget::getNearestPeak(QPoint pt){
	return ui->plotter->getNearestPeak(pt);
}
void    specWidget::setWaterfallSpan(quint64 span_ms){
	ui->plotter->setWaterfallSpan(span_ms);
}
quint64 specWidget::getWfTimeRes(void){
	return ui->plotter->getWfTimeRes();
}
void    specWidget::setFftRate(int rate_hz){
	ui->plotter->setFftRate(rate_hz);
}
void    specWidget::clearWaterfall(void){
	return ui->plotter->clearWaterfall();
}
bool    specWidget::saveWaterfall(const QString & filename)
{
	return ui->plotter->saveWaterfall(filename);
}

void specWidget::on_pushButton_reset_clicked()
{
	saveSettings();
	if (m_runth->isRunning())
	{
		if (m_nTimerID>=0)
			killTimer(m_nTimerID);

		if (usrp)
		{
			close_device();
		}
		ui->pushButton_reset->setText(tr("Start"));
	}
	else
	{
		loadSettings();
		resetFFT();
	}
}

void specWidget::loadSettings()
{
	QSettings settings(QCoreApplication::applicationFilePath()+".ini", QSettings::IniFormat);

	const bool checkBox_iq = true;
	const double doubleSpinBox_spr = settings.value("spectrum/doubleSpinBox_spr",10).toDouble();
	ui->doubleSpinBox_spr->setValue(doubleSpinBox_spr);
	//Sample Rate
	ui->plotter->setSampleRate(doubleSpinBox_spr*1000000/(checkBox_iq?1:2));

	const double doubleSpinBox_center = settings.value("spectrum/doubleSpinBox_center",1200).toDouble();
	ui->doubleSpinBox_center->setValue(doubleSpinBox_center);
	//中心频率
	ui->plotter->setCenterFreq(doubleSpinBox_center * 1000000);
	//选择频率
	ui->plotter->setDemodCenterFreq(doubleSpinBox_center*1000000);
	ui->plotter->setHiLowCutFrequencies(-1000000,1000000);

	if (checkBox_iq)
	{
		ui->plotter->setSpanFreq(doubleSpinBox_spr*1000000);
		//选择带宽
		ui->plotter->setMaxBandiwdth(doubleSpinBox_spr*1000000);
	}
	else
	{
		ui->plotter->setSpanFreq(doubleSpinBox_spr*1000000/2);
		//选择带宽
		ui->plotter->setMaxBandiwdth(doubleSpinBox_spr*1000000/2);
	}

	const int spinBox_fftUpdate = settings.value("spectrum/spinBox_fftUpdate",20).toInt();
	ui->spinBox_fftUpdate->setValue(spinBox_fftUpdate);

	const int comboBox_fftSize = settings.value("spectrum/comboBox_fftSize",6).toInt();
	ui->comboBox_fftSize->setCurrentIndex(comboBox_fftSize);

	m_nFFTSize = (1<<comboBox_fftSize) * 128;
	if (m_nFFTSize>65536)
		m_nFFTSize = 65536;
	if (m_nFFTSize<128)
		m_nFFTSize = 128;

	const int comboBox_atn = settings.value("spectrum/comboBox_atn",0).toInt();
	ui->comboBox_atn->setCurrentIndex(comboBox_atn);

	const int spinBox_chan = settings.value("spectrum/spinBox_chan",0).toInt();
	ui->spinBox_chan->setValue(spinBox_chan);

	const int spinBox_gain = settings.value("spectrum/spinBox_gain",0).toInt();
	ui->spinBox_gain->setValue(spinBox_gain);

	const double doubleSpinBox_range_max = settings.value("spectrum/doubleSpinBox_range_max",0).toInt();
	ui->doubleSpinBox_range_max->setValue(doubleSpinBox_range_max);

	const double doubleSpinBox_range_min = settings.value("spectrum/doubleSpinBox_range_min",-150).toInt();
	ui->doubleSpinBox_range_min->setValue(doubleSpinBox_range_min);

	const QString lineEdit_dev = settings.value("spectrum/lineEdit_dev","").toString();
	ui->lineEdit_dev->setText(lineEdit_dev);

	const QString lineEdit_folder = settings.value("spectrum/lineEdit_folder","./").toString();
	ui->lineEdit_folder->setText(lineEdit_folder);

}
void specWidget::saveSettings()
{
	QSettings settings(QCoreApplication::applicationFilePath()+".ini", QSettings::IniFormat);
	const double doubleSpinBox_spr = ui->doubleSpinBox_spr->value();
	settings.setValue("spectrum/doubleSpinBox_spr",doubleSpinBox_spr);
	const double doubleSpinBox_center = ui->doubleSpinBox_center->value();
	settings.setValue("spectrum/doubleSpinBox_center",doubleSpinBox_center);
	const int spinBox_fftUpdate = ui->spinBox_fftUpdate->value();
	settings.setValue("spectrum/spinBox_fftUpdate",spinBox_fftUpdate);
	const int comboBox_fftSize = ui->comboBox_fftSize->currentIndex();
	settings.setValue("spectrum/comboBox_fftSize",comboBox_fftSize);
	const int comboBox_atn = ui->comboBox_atn->currentIndex();
	settings.setValue("spectrum/comboBox_atn",comboBox_atn);
	const int spinBox_gain = ui->spinBox_gain->value();
	settings.setValue("spectrum/spinBox_gain",spinBox_gain);
	const int spinBox_chan = ui->spinBox_chan->value();
	settings.setValue("spectrum/spinBox_chan",spinBox_chan);
	const double doubleSpinBox_range_max = ui->doubleSpinBox_range_max->value();
	settings.setValue("spectrum/doubleSpinBox_range_max",doubleSpinBox_range_max);
	const double doubleSpinBox_range_min = ui->doubleSpinBox_range_min->value();
	settings.setValue("spectrum/doubleSpinBox_range_min",doubleSpinBox_range_min);

	const QString lineEdit_dev = ui->lineEdit_dev->text();
	settings.setValue("spectrum/lineEdit_dev",lineEdit_dev);
	const QString lineEdit_folder = ui->lineEdit_folder->text();
	settings.setValue("spectrum/lineEdit_folder",lineEdit_folder);

}
void specWidget::freeFFT()
{
	if (m_nTimerID>=0)
	{
		killTimer(m_nTimerID);
		m_nTimerID = -1;
	}
	if (m_runth->isRunning())
	{
		stop_signal_called = true;
		m_runth->wait();
		m_saveth->wait();
	}

	if (m_pFFTPlan)
	{
		fftw_destroy_plan(m_pFFTPlan);
		m_pFFTPlan = nullptr;
	}
	if (m_pFFTIn)
	{
		fftw_free(m_pFFTIn);
		m_pFFTIn = nullptr;
	}
	if (m_pFFTOut)
	{
		fftw_free(m_pFFTOut);
		m_pFFTOut = nullptr;
	}
	m_dHammingWnd.clear();
	m_dFFTAmp.clear();

}
void specWidget::resetFFT()
{
	ui->pushButton_reset->setText(tr("Start"));
	if (m_nTimerID>=0)
		killTimer(m_nTimerID);
	freeFFT();
	if (usrp)
	{
		close_device();
	}

	m_pFFTIn = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * m_nFFTSize);
	m_pFFTOut = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * m_nFFTSize);
	m_pFFTPlan = fftw_plan_dft_1d(m_nFFTSize,m_pFFTIn,m_pFFTOut,FFTW_FORWARD,FFTW_ESTIMATE);
	for (int i=0;i<m_nFFTSize;++i)
	{
		m_dHammingWnd.push_back(0.54 - 0.46 * cos (2*3.1415927 * i / (m_nFFTSize-1)));
		m_dFFTAmp.push_back(0);
	}
	int updateIntelv = 1000.0 /ui->spinBox_fftUpdate->value() + .5;

	m_channel = ui->spinBox_chan->value();
	if (!usrp)
		open_device();

	if (!usrp)
		return;

	//开始
	setSampleRate(getSampleRate());
	setCenterFreq(ui->plotter->getCenterFreq());
	QString atn = ui->comboBox_atn->currentText();
	if (atn.size())
		usrp->set_rx_antenna(atn.toStdString(),m_channel);

	on_spinBox_gain_valueChanged(ui->spinBox_gain->value());

	m_runth->setRunner([&](void){
		run_IO();
	});

	//保存文件
	m_strFolder = ui->lineEdit_folder->text();

	save_count = 0;
	m_bSaveToFile = ui->checkBox_save->isChecked();
	double dfreq = ui->doubleSpinBox_center->value();
	double spr = ui->doubleSpinBox_spr->value();
	m_saveth->setRunner([&,dfreq,spr](void){
		QFile fOut;
		while (!stop_signal_called)
		{
			if (save_count + 100 < rx_count)
			{
				std::shared_ptr< short >  buf = vec_buffer[save_count % bufsz];
				size_t len = vec_buffersz[save_count % bufsz];
				++save_count;
				if (!m_bSaveToFile)
				{
					QThread::msleep(10);
					continue;
				}
				if (fOut.isOpen())
				{
					if (fOut.size()>=1024*1024*1024)
						fOut.close();
				}
				if (!fOut.isOpen())
				{
					QDir dir;
					dir.mkpath(m_strFolder);
					QDateTime dtm = QDateTime::currentDateTime();
					QString sdtm = dtm.toString("yyyy_MM_dd_HH_mm_ss.zzz");
					QString fm = m_strFolder + "/";
					fm += QString("%1MHz_%2Sps_%3.iq.sc16Intel.pcm").arg(dfreq,0,'f',6)
							.arg(spr,0,'f',6).arg(sdtm);
					fOut.setFileName(fm);
					if (!fOut.open(QIODevice::WriteOnly))
						m_bSaveToFile = false;
				}
				if (!m_bSaveToFile)
					continue;
				fOut.write((char *)buf.get(),len * sizeof(short)*2);
			}
			else
				QThread::msleep(10);
		}
		if (fOut.isOpen())
			fOut.close();
	});
	m_runth->start();
	m_saveth->start();
	m_nTimerID = startTimer(updateIntelv);
	ui->pushButton_reset->setText(tr("Stop"));
}

void specWidget::appendWavComplex(const double (* pWav)[2], const int count, double voltage_ref)
{
	if (!m_pFFTIn || !m_pFFTOut || !m_pFFTPlan || m_dHammingWnd.size()!=m_nFFTSize
			|| m_dFFTAmp.size()!=m_nFFTSize)
		return;
	memset(m_pFFTIn,0,sizeof(fftw_complex)*m_nFFTSize);
	const int nFFTCp = m_nFFTSize<count?m_nFFTSize:count;
	for (long long i=0;i<nFFTCp ;++i)
	{
		long long nHammingIdx = i * m_nFFTSize / nFFTCp;
		m_pFFTIn[i][0] = pWav[i][0] * m_dHammingWnd[nHammingIdx];
		m_pFFTIn[i][1] = pWav[i][1] * m_dHammingWnd[nHammingIdx];
	}
	fftw_execute(m_pFFTPlan);
	const double minus = 10 *log(m_nFFTSize * voltage_ref * m_nFFTSize)/log(10);

	for (long i = 0; i< m_nFFTSize ;++i)
	{
		m_dFFTAmp[(i+m_nFFTSize/2) % m_nFFTSize] = 5 * log (m_pFFTOut[i][0] * m_pFFTOut[i][0] + m_pFFTOut[i][1] * m_pFFTOut[i][1])/log(10) - minus + m_dManuAdd;
	}

	setNewFftData(m_dFFTAmp.data(),m_nFFTSize);
}


void specWidget::timerEvent(QTimerEvent * e)
{
	static std::shared_ptr<double> ppbuf(new double [65536*4]);
	static double (*pBufIQ)[2] = (double (*)[2])ppbuf.get();

	const int fft_bytes_needed = m_nFFTSize *2 * 2;
	//int fft
	if (e->timerId()==m_nTimerID )
	{
		if (!m_bSaveToFile)
		{
			const int sppoints = fft_bytes_needed/(sizeof(short))/(2);
			Q_ASSERT(sppoints == m_nFFTSize);
			double ref1v = 16384;
			int curr_tx = (rx_count + bufsz -  100) % bufsz ;
			int total_cp = 0;
			memset(pBufIQ,0,sizeof(double)*2*sppoints);
			while (total_cp < sppoints)
			{
				const size_t sz = vec_buffersz[curr_tx];
				const short (*datap)[2]  = (const short (*)[2])vec_buffer[curr_tx].get();
				for (size_t i=0;i<sz  &&  total_cp < sppoints;++i,++total_cp)
				{
					double hv = (1 -0.46 ) - 0.46 * cos(2*3.1415926*total_cp/(sppoints-1));
					pBufIQ[total_cp][0] =datap[i][0] * hv;
					pBufIQ[total_cp][1] =datap[i][1] * hv;

				}
				++curr_tx;
				curr_tx %= bufsz;
				if (!sz)
					break;
			}
			appendWavComplex(pBufIQ,sppoints,ref1v);

			if (m_nCurrentCenter>=0 && m_nCurrentCenter < m_nFFTSize)
			{
				ui->sMeter->setLevel(m_dFFTAmp[m_nCurrentCenter]);
				//ui->sMeter->setSqlLevel(m_dFFTAmp[m_nCurrentCenter]);
			}
		}
		else
		{
			int he = rx_count - save_count;

			ui->lcdNumber_saveBehind->display(he);

			if (he >= bufsz)
				m_bSaveToFile = false;
		}

		static int ccct = 0;
		if (++ccct % 10==0)
		{
			if (!m_runth->isRunning())
				ui->pushButton_reset->setText(tr("Start"));
			else
				ui->pushButton_reset->setText(tr("Stop"));

		}
	}
}



void specWidget::on_doubleSpinBox_center_2_valueChanged(double arg1)
{
	m_dManuAdd = arg1;
}

void specWidget::on_comboBox_fftSize_activated(int index)
{
	if (index)
		m_nFFTSize = 128 * (1<<index);
	if (m_nFFTSize>65536)
		m_nFFTSize = 65536;
	if (m_nFFTSize<128)
		m_nFFTSize = 128;
	resetFFT();
	saveSettings();
}

void specWidget::on_doubleSpinBox_center_valueChanged(double arg1)
{
	if (arg1 * 1e6 != ui->freqCtrl->getFrequency())
	{
		ui->freqCtrl->setFrequency(arg1 * 1e6);
	}
}

void specWidget::on_spinBox_gain_valueChanged(int arg1)
{
	if (usrp)
		usrp->set_rx_gain(arg1,m_channel);
	//usrp->set_rx_agc(true,m_channel);

}

void specWidget::on_doubleSpinBox_range_max_valueChanged(double arg1)
{
	ui->plotter->setWaterfallRange(ui->doubleSpinBox_range_min->value(),ui->doubleSpinBox_range_max->value());
}

void specWidget::on_doubleSpinBox_range_min_valueChanged(double arg1)
{
	ui->plotter->setWaterfallRange(ui->doubleSpinBox_range_min->value(),ui->doubleSpinBox_range_max->value());
}

void specWidget::on_comboBox_atn_currentIndexChanged(const QString &arg1)
{
	if (arg1.size())
	{
		if (usrp)
			usrp->set_rx_antenna(arg1.toStdString(),m_channel);
	}
}

void specWidget::on_spinBox_chan_valueChanged(int arg1)
{
	m_channel = arg1;
}

void specWidget::on_toolButton_br_clicked()
{
	QString str = QFileDialog::getExistingDirectory(this,tr("Output to"),ui->lineEdit_folder->text());
	if (str.length())
		ui->lineEdit_folder->setText(str);
}

void specWidget::on_checkBox_save_stateChanged(int arg1)
{
	m_bSaveToFile = arg1==0?false:true;
}
