#include "uhd_device.h"
#include <QThread>
#include <QDebug>
#include <QCoreApplication>
using uhd::tune_request_t;
using uhd::tx_streamer;
using uhd::usrp::multi_usrp;
using uhd::rx_streamer;
using std::cerr;
using std::endl;
using std::string;
using std::vector;
using std::shared_ptr;

uhd_io_thread::uhd_io_thread (std::function<void (void)> runner,QObject * p)
	:QThread(p)
	,m_runner(runner)

{

}
uhd_io_thread::uhd_io_thread (QObject * p)
	:QThread(p)

{

}

void uhd_io_thread::run()
{
	if (m_runner)
		m_runner();
}

uhd_device::uhd_device()
	:stop_signal_called(false)
	,streaming(false)
	,rx_count(0)
{
	m_buffer_tmFrag = new double [bufsz];
	m_buffer_tmSec  = new long long [bufsz];
	m_buffer_iq_all = new short [bufsz * m_spb_size * 2];
	m_buffer_iq = new short * [bufsz];
	m_buf_iqsize = new int [bufsz];
	for (int i=0;i<bufsz;++i)
	{
		m_buffer_tmFrag[i] = 0;
		m_buffer_tmSec[i] = 0;
		m_buffer_iq[i] = &m_buffer_iq_all[i * m_spb_size * 2];
		m_buf_iqsize[i] = 0;
	}
}

uhd_device::uhd_device(const std::string &args)
	:stop_signal_called(false)
	,streaming(false)
	,m_dev_args (args)
	,rx_count(0)

{
	m_buffer_tmFrag = new double [bufsz];
	m_buffer_tmSec  = new long long [bufsz];
	m_buffer_iq_all = new short [bufsz * m_spb_size * 2];
	m_buffer_iq = new short * [bufsz];
	m_buf_iqsize = new int [bufsz];
	for (int i=0;i<bufsz;++i)
	{
		m_buffer_tmFrag[i] = 0;
		m_buffer_tmSec[i] = 0;
		m_buffer_iq[i] = &m_buffer_iq_all[i * m_spb_size * 2];
		m_buf_iqsize[i] = 0;
	}
}
uhd_device::~uhd_device()
{
	stop_signal_called = true;
	while (streaming)
	{
		QThread::msleep(100);
	}

	delete[]	m_buffer_tmFrag;
	delete[]	m_buffer_tmSec;
	delete[]	m_buffer_iq_all ;
	delete[]	m_buffer_iq;
	delete[]	m_buf_iqsize;


}

void uhd_device::setDevArgs(const std::string &args)
{
	this->m_dev_args = args;

}
std::string uhd_device::devArgs() const
{
	return m_dev_args;
}

bool uhd_device::open_device()
{
	string ref = "internal";
	std::atomic<bool> finished(false);
	std::thread thr([&]()->void{
		try{
			usrp = multi_usrp::make(m_dev_args);
		}
		catch (...)
		{
			fprintf (stderr,"Error in USRP init.\n");
		}
		finished = true;
	});

	while (!finished)
	{
		QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
		QThread::msleep(20);
	}
	thr.join();



	if (!usrp)
		return false;
	usrp->set_clock_source(ref,multi_usrp::ALL_MBOARDS);
	//if (rx_op.subdev.size())
	//	usrp->set_rx_subdev_spec(rx_op.subdev,multi_usrp::ALL_MBOARDS);
	stop_signal_called = false;
	return usrp?true:false;
}

bool uhd_device::close_device()
{
	stop_signal_called = true;
	while (streaming)
	{
		QThread::msleep(1000);
	}
	usrp.reset();

	return true;
}

/*!
 * 范例落盘函数,使用环形队列保持跟随收发
 */
void uhd_device::run_IO()
{
	//创建流对象实例
	uhd::stream_args_t stream_args_rx("sc16", "sc16");
	stream_args_rx.channels.push_back(m_channel);
	rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args_rx);

	rx_count = 0;
	//接收线程
	auto thcall_rx = [&]()->void{
		uhd::rx_metadata_t md_rx;
		uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
		stream_cmd.num_samps  = size_t(m_spb_size);
		stream_cmd.stream_now = true;
		stream_cmd.time_spec  = uhd::time_spec_t();
		streaming = true;
		rx_stream->issue_stream_cmd(stream_cmd);

		try {
			while (!stop_signal_called)
			{
				const int off = rx_count % bufsz;
				m_buf_iqsize[off] = rx_stream->recv((void *)(m_buffer_iq[off]),m_spb_size,md_rx,10,false);
				++rx_count;
				//md_rx可以读取时戳
				m_buffer_tmFrag[off] = md_rx.time_spec.get_frac_secs();
				m_buffer_tmSec[off] = md_rx.time_spec.get_full_secs();
				if (md_rx.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT)
				{
					fputs("Tout",stderr);
					rx_stream->issue_stream_cmd(stream_cmd);
				}
				else if (md_rx.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW)
				{
					fputs("O",stderr);
					rx_stream->issue_stream_cmd(stream_cmd);
				}
				else if (md_rx.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE)
				{
					cerr << "Receiver error: "<< md_rx.strerror() << endl ;
					rx_stream->issue_stream_cmd(stream_cmd);
				}
			}

		}  catch (...) {
			stop_signal_called = true;
		}
		stream_cmd.stream_now = false;
		rx_stream->issue_stream_cmd(stream_cmd);
		streaming = false;
	};
	//启动线程
	uhd_io_thread * rx_thread = new uhd_io_thread(thcall_rx,0);
	rx_thread->start(QThread::TimeCriticalPriority);
	//主线程不断打印状态
	while (!stop_signal_called)
	{
		//cerr<<"RX" << rx_count<<"\r";
		std::this_thread::sleep_for(std::chrono::milliseconds(400));
	}
	//退出
	rx_thread->wait();
	rx_thread->deleteLater();

}

