#include <stdio.h>
#include <uhd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <thread>
#include <mutex>
#include "../waveform.h"
static bool stop_signal_called = false;
#define UHD_DO(X) \
{\
	uhd_error e = (X);\
	if (e) { fprintf(stderr,"Error in line %d, NO %d.",__LINE__,e);\
	return_code = 1;\
	stop_signal_called = true;\
	}\
	}

void sigint_handler(int code){
	(void)code;
	stop_signal_called = true;
}
std::list<char> lst_data;
std::mutex mtx_data;
int runSend()
{
	int return_code = EXIT_SUCCESS;
	char dev_args[] = "";
	char error_string[4096];
	const char * gainName = "PGA";
	//Sample rate in Hz
	double sprate		=  SIGRATE ;
	double tx_freq		= 200.0e6;
	double tx_sprate	= sprate;
	double tx_gain		= 70;
	double tx_bw		= sprate;
	//发射信号。MIMO时，可以指定0,1
	size_t tx_channel[]	= {0};

	//设备句柄
	uhd_usrp_handle usrp = 0;
	uhd_tx_streamer_handle tx_streamer = 0;
	uhd_tx_metadata_handle tx_meta[4]={0,0,0,0};
	fprintf(stderr, "Creating USRP with args \"%s\"...\n", dev_args);
	UHD_DO(uhd_usrp_make(&usrp, dev_args));

	//设置天线，子板号为0（multi-usrp支持级联)
	UHD_DO(uhd_usrp_set_tx_antenna(usrp,"TX/RX",tx_channel[0]));
	// Create TX streamer
	UHD_DO(uhd_tx_streamer_make(&tx_streamer));
	// Create TX metadata
	UHD_DO(uhd_tx_metadata_make(tx_meta, false, 0, 0.1, false, false));
	UHD_DO(uhd_tx_metadata_make(tx_meta+1, false, 0, 0.1, false, true));
	UHD_DO(uhd_tx_metadata_make(tx_meta+2, false, 0, 0.1, true, false));
	UHD_DO(uhd_tx_metadata_make(tx_meta+3, false, 0, 0.1, true, true));

	// Create other necessary structs for TX
	uhd_tune_request_t tx_tune_request = {
		.target_freq = tx_freq,
		.rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO,
		.rf_freq = 0,
		.dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO,
		.dsp_freq = 0,
		.args = 0
	};
	uhd_tune_result_t tx_tune_result;
	//char tx_cpu_format[] = "fc32";
	char tx_cpu_format[] = "sc16";
	char tx_otw_format[] = "sc16";
	char tx_args[] = "";
	const size_t tx_channel_count = sizeof(tx_channel)/sizeof(tx_channel[0]);

	uhd_stream_args_t tx_stream_args = {
		.cpu_format = tx_cpu_format,
		.otw_format = tx_otw_format,
		.args = tx_args,
		.channel_list = tx_channel,
		.n_channels = tx_channel_count
	};
	size_t tx_sps_buff = 0;

	// Set rate
	fprintf(stderr, "Setting TX Rate: %f...\n", tx_sprate);
	UHD_DO(uhd_usrp_set_tx_rate(usrp, tx_sprate, tx_channel[0]));
	// See what rate actually is
	UHD_DO(uhd_usrp_get_tx_rate(usrp, tx_channel[0], &tx_sprate));
	fprintf(stderr, "Actual TX Rate: %f...\n\n", tx_sprate);
	// Set gain
	fprintf(stderr, "Setting TX Gain: %f db...\n", tx_gain);
	UHD_DO(uhd_usrp_set_tx_gain(usrp, tx_gain, tx_channel[0], gainName));
	// See what gain actually is
	UHD_DO(uhd_usrp_get_tx_gain(usrp, tx_channel[0], gainName, &tx_gain));
	fprintf(stderr, "Actual TX Gain: %f...\n", tx_gain);
	// Set frequency
	fprintf(stderr, "Setting TX frequency: %f MHz...\n", tx_freq / 1e6);
	UHD_DO(uhd_usrp_set_tx_freq(usrp, &tx_tune_request, tx_channel[0], &tx_tune_result));
	// See what frequency actually is
	UHD_DO(uhd_usrp_get_tx_freq(usrp, tx_channel[0], &tx_freq));
	fprintf(stderr, "Actual TX frequency: %f MHz...\n", tx_freq / 1e6);
	//Band
	fprintf(stderr, "Setting TX Bandwidth: %f MHz...\n", tx_bw/1e6);
	UHD_DO(uhd_usrp_set_tx_bandwidth(usrp, tx_bw,tx_channel[0]));

	UHD_DO(uhd_usrp_get_tx_bandwidth(usrp, tx_channel[0], &tx_bw));
	fprintf(stderr, "Actual TX Bandwidth: %f MHz...\n", tx_bw / 1e6);

	// Set up streamer
	tx_stream_args.channel_list = tx_channel;
	UHD_DO(uhd_usrp_get_tx_stream(usrp, &tx_stream_args, tx_streamer));
	// Set up buffer
	UHD_DO(uhd_tx_streamer_max_num_samps(tx_streamer, &tx_sps_buff));
	fprintf(stderr, "Buffer size in samples: %zu\n", tx_sps_buff);

	fprintf(stderr, "Press Ctrl+C to stop streaming.\n");

	fprintf(stderr, "Input :");
	// Actual streaming, TX Threading
	time_t tm_idle = time(0);
	while (!stop_signal_called) {
		bool has_data = false;
		char cha = 0;
		mtx_data.lock();
		has_data = lst_data.size()>0;
		if (has_data)
		{
			cha = *lst_data.begin();
			lst_data.pop_front();
		}
		mtx_data.unlock();

		if (has_data)
		{
			for (int i=0;i<10;++i)
			{
				char c = 0;
				if (i==0)
					c = 0;
				else if (i<9)
					c = (cha >> (8 - i)) & 0x01;
				else
					c = 1;
				size_t total_sent = 0;
				int wfm = c==0?0:1;
				while (total_sent < WAVSIZE && !stop_signal_called )
				{
					size_t num_samps_sent = 0;
					SPTYPE * tx_buff = wav_spread[wfm][total_sent];
					const void ** tx_buff_ptr = (const void **) &tx_buff;
					int start_of_burst = total_sent==0?1:0;
					int end_of_burst = (total_sent + tx_sps_buff) >=WAVSIZE ?1:0;
					int metai = start_of_burst * 2 + end_of_burst;
					int send_sz = end_of_burst?(WAVSIZE - total_sent ):(tx_sps_buff);
					UHD_DO(uhd_tx_streamer_send(tx_streamer, tx_buff_ptr,
												send_sz, tx_meta + metai,
												1, &num_samps_sent));
					total_sent += num_samps_sent;
				}
			}
			tm_idle = time(0);
		}
		else
		{
			char c = 1;
			size_t total_sent = 0;
			int wfm = c==0?0:1;
			while (total_sent < WAVSIZE && !stop_signal_called )
			{
				size_t num_samps_sent = 0;
				SPTYPE * tx_buff = wav_spread[wfm][total_sent];
				const void ** tx_buff_ptr = (const void **) &tx_buff;
				int start_of_burst = total_sent==0?1:0;
				int end_of_burst = (total_sent + tx_sps_buff) >=WAVSIZE ?1:0;
				int metai = start_of_burst * 2 + end_of_burst;
				int send_sz = end_of_burst?(WAVSIZE - total_sent ):(tx_sps_buff);
				UHD_DO(uhd_tx_streamer_send(tx_streamer, tx_buff_ptr,
											send_sz, tx_meta + metai,
											1, &num_samps_sent));
				total_sent += num_samps_sent;
			}
			if (tm_idle + 10 < time(0))
			{
				tm_idle = time(0);
				char buf_tm[256];
				sprintf(buf_tm,"Time=%lld\n",tm_idle);
				int sl = strlen(buf_tm);
				mtx_data.lock();
				std::copy(buf_tm,buf_tm+sl,std::back_inserter(lst_data));
				mtx_data.unlock();
			}
		}
	}


	if (tx_streamer) uhd_tx_streamer_free(&tx_streamer);
	for (int i=0;i<4;++i)
		if (tx_meta[i])
			uhd_tx_metadata_free(tx_meta+i);

	if(return_code != EXIT_SUCCESS && usrp != NULL){
		uhd_usrp_last_error(usrp, error_string, 512);
		fprintf(stderr, "USRP reported the following error: %s\n", error_string);
	}
	uhd_usrp_free(&usrp);

	fprintf(stderr, (return_code ? "Failure\n" : "Success\n"));
	return return_code;


}

int main()
{
	// Ctrl+C will exit loop
	init_wavform();
	signal(SIGINT, &sigint_handler);
	std::thread th(runSend);

	while (!stop_signal_called)
	{
		char c = getchar();
		mtx_data.lock();
		lst_data.push_back(c);
		mtx_data.unlock();
	}


	th.join();
	return 0;
}


