#include <QCoreApplication>
#include <QVector>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <string>
#include <memory>
#include <QFile>
#include "cmdlineparser.h"
#include "tb_interface.h"
using namespace TASKBUS;
const int OFFLINEDEBUG = 0;
//数据源方法
int do_fir(const cmdlineParser & args);
//全局的终止标记
static bool bfinished = false;
using namespace std;
int main(int argc , char * argv[])
{
	QCoreApplication a(argc, argv);
	//重要！设置输入输出为二进制！
	init_client();

	//解释命令行
	cmdlineParser args;
	if (OFFLINEDEBUG==0)
		args.parser(argc,argv);
	else
	{
		FILE * old_stdin, *old_stdout;
		auto ars = debug("debug/pid4113",&old_stdin,&old_stdout);
		args.parser(ars);
	}

	int ret = 0;
	//每个模块要响应 --information参数,打印自己的功能定义字符串。或者提供一个json文件。
	if (args.contains("information"))
	{
		QFile fp(":/filter_fir.json");
		if (fp.open(QIODevice::ReadOnly)==false)
		{
			fp.setFileName(":/json/filter_fir.json");
			fp.open(QIODevice::ReadOnly);
		}
		if (fp.isOpen())
		{
			QByteArray arr = fp.readAll();
			arr.push_back('\0');
			puts(arr.constData());
			fflush(stdout);
		}
		ret = 0;
	}
	else if (args.contains("function"/*,"filter_fir"*/))//正常运行模式
	{
		ret = do_fir(args);
	}
	else
	{
		fprintf(stderr,"Error:Function does not exits.");
		ret = -1;
	}

	return ret;
}



int do_fir(const cmdlineParser & args)
{
	using namespace TASKBUS;
	int res = 0;
	//获得平台告诉自己的实例名
	const unsigned int instance	  = args.toInt("instance",0);
	const unsigned int iinput	  = args.toInt("in",0);
	const unsigned int i_tmin	  = args.toInt("in_time",0);
	const unsigned int i_tmout	  = args.toInt("out_time",0);
	const int iout		  = args.toInt("out",0);
	std::vector<double> hn = args.toDoubleArray("hn");
	//工作模式
	const int sptype	=	args.toInt("sptype",0);					fprintf(stderr,"sptype is %d.",sptype);
	int type	=	args.toInt("type",1);
	if (type<0 || type >1)
		type = 0;

	fflush(stderr);

	//计算加速表,乘法预先做完

	const int stages = hn.size();
	if (stages<1)
	{
		fprintf(stderr,"h(t) is empty. failed.\n");
		return -1;
	}
	fprintf(stderr,"Init fast filter Boost table %.1lf M bytes.\n",stages * 65536/1024.0/1024.0*sizeof(int));
	std::shared_ptr<int [][65536]> boostTb(new int[stages][65536]);

	if (boostTb)
	{
		int (*pBTB)[65536] = boostTb.get();

		for (int s = 0;s<stages;++s)
		{
			for (unsigned int v = 0; v<=0xffff;++v)
			{
				unsigned short vs = v & 0xffff;
				short * vp = (short *)&vs;
				double vr = hn[s] * (*vp);
				if (vr > 32766)
					vr = 32766;
				if (vr < -32766)
					vr = -32766;
				pBTB[s][vs] = int(vr * 32768 + (vr<0?-0.5:0.5));
			}
		}
	}
	else
	{
		fprintf(stderr,"Failed.");
		return -1;
	}
	const int (*BTB)[65536] = boostTb.get();
	fprintf(stderr,"Succeeded.");
	std::vector<short> vec_regs[2];
	vec_regs[0] = std::vector<short>(stages,0);
	vec_regs[1] = std::vector<short>(stages,0);
	short * reg[2] = {vec_regs[0].data(),vec_regs[1].data()};
	unsigned short * ureg[2] = {(unsigned short *)vec_regs[0].data(),(unsigned short *)vec_regs[1].data()};
	unsigned long long clk = 0;
	try{
		//判断参数合法性
		if (instance==0)
			throw "function=quit;{\"error\":\"instance is 0, quit.\"}";
		int failed_header = 0;
		QVector<qint16> vec_output;

		while (false==bfinished)
		{
			subject_package_header header;
			vector<unsigned char> packagedta = pull_subject(&header);
			if (is_valid_header(header)==false)
			{
				if (++failed_header>16)
					bfinished = true;
				continue;
			}
			if ( is_control_subject(header))
			{
				//收到命令进程退出的广播消息,退出
				if (strstr(control_subject(header,packagedta).c_str(),"function=quit;")!=nullptr)
					bfinished = true;
			}
			else if (header.subject_id==i_tmin && i_tmout)
			{
				push_subject(i_tmout,instance,packagedta.size(),
							 (unsigned char *)packagedta.data()
							 );
			}
			else if (header.subject_id == iinput)
			{
				if (vec_output.size() < packagedta.size() * 2)
					vec_output.resize(packagedta.size() * 2);
				short * outptr = (short *) vec_output.data();
				//Input Buffer
				switch(sptype)
				{
				//Intel 16bit
				case 0:{
					if (type==0)
					{
						const short * pdata = (const short *) packagedta.data();
						const int samples = packagedta.size()/sizeof(short);
						for (int spid = 0;spid < samples; ++ spid)
						{
							int curr_pos = clk % stages;
							reg[0][curr_pos] = pdata[spid];
							int res = 0;
							for (int ids = 0;ids<stages;++ids)
								res += BTB[ids][ureg[0][(curr_pos + ids + 1) % stages]];
							outptr[spid] = (res >>15);
							++clk;
						}
					}
					else
					{
						const short (* pdata)[2] = (const short (*)[2]) packagedta.data();
						const int samples = packagedta.size()/sizeof(short)/2;
						for (int spid = 0;spid < samples; ++ spid)
						{
							int curr_pos = clk % stages;
							reg[0][curr_pos] = pdata[spid][0];
							reg[1][curr_pos] = pdata[spid][1];
							int res[2] = {0,0};
							for (int ids = 0;ids<stages;++ids)
							{
								res[0] += BTB[ids][ureg[0][(curr_pos + ids + 1) % stages]];
								res[1] += BTB[ids][ureg[1][(curr_pos + ids + 1) % stages]];
							}
							outptr[spid*2] = (res[0] >>15);
							outptr[spid*2+1] = (res[1] >>15);
							++clk;
						}
					}

				}
					break;
					//16bit U
				case 1:{
					if (type==0)
					{
						const unsigned char * pdata = (const unsigned char *) packagedta.data();
						const int samples = packagedta.size()/sizeof(short);
						for (int spid = 0;spid < samples; ++ spid)
						{
							unsigned char pt[2] = {pdata[spid*2+1],pdata[spid*2]};
							short * ptv = (short *)pt;
							int curr_pos = clk % stages;
							reg[0][curr_pos] = *ptv;					push_subject(iout,instance,vec_output.size()*2,
																					 (unsigned char *)vec_output.constData()
																					 );
							int res = 0;
							for (int ids = 0;ids<stages;++ids)
								res += BTB[ids][ureg[0][(curr_pos + ids + 1) % stages]];
							outptr[spid] = (res >>15);
							++clk;
						}
					}
					else
					{
						const unsigned char * pdata = (const unsigned char *) packagedta.data();
						const int samples = packagedta.size()/sizeof(short)/2;
						for (int spid = 0;spid < samples; ++ spid)
						{
							unsigned char pt[2][2] = {
								{pdata[spid*4+1],pdata[spid*4]},
								{pdata[2+spid*4+1],pdata[2+spid*4]}
													 };
							short * ptv[2] = {(short *)pt[0],(short *)pt[1]};
							int curr_pos = clk % stages;
							reg[0][curr_pos] = *(ptv[0]);
							reg[1][curr_pos] = *(ptv[1]);
							int res[2] = {0,0};
							for (int ids = 0;ids<stages;++ids)
							{
								res[0] += BTB[ids][ureg[0][(curr_pos + ids + 1) % stages]];
								res[1] += BTB[ids][ureg[1][(curr_pos + ids + 1) % stages]];
							}
							outptr[spid*2] = (res[0] >>15);
							outptr[spid*2+1] = (res[1] >>15);
							++clk;
						}
					}

				}
					break;
					//int8
				case 2:{
					if (type==0)
					{
						const char * pdata = (const char *) packagedta.data();
						const int samples = packagedta.size()/sizeof(char);
						for (int spid = 0;spid < samples; ++ spid)
						{
							int curr_pos = clk % stages;
							reg[0][curr_pos] = pdata[spid];
							int res = 0;
							for (int ids = 0;ids<stages;++ids)
								res += BTB[ids][ureg[0][(curr_pos + ids + 1) % stages]];
							outptr[spid] = (res >>15);
							++clk;
						}
					}
					else
					{
						const char (* pdata)[2] = (const char ( *)[2]) packagedta.data();
						const int samples = packagedta.size()/sizeof(char)/2;
						for (int spid = 0;spid < samples; ++ spid)
						{
							int curr_pos = clk % stages;
							reg[0][curr_pos] = pdata[spid][0];
							reg[1][curr_pos] = pdata[spid][1];
							int res[2] = {0,0};
							for (int ids = 0;ids<stages;++ids)
							{
								res[0] += BTB[ids][ureg[0][(curr_pos + ids + 1) % stages]];
								res[1] += BTB[ids][ureg[1][(curr_pos + ids + 1) % stages]];
							}
							outptr[spid*2] = (res[0] >>15);
							outptr[spid*2+1] = (res[1] >>15);
							++clk;
						}
					}

				}
					break;
				case 3:{
					if (type==0)
					{
						const unsigned char * pdata = (const unsigned char *) packagedta.data();
						const int samples = packagedta.size()/sizeof(char);
						for (int spid = 0;spid < samples; ++ spid)
						{
							int curr_pos = clk % stages;
							reg[0][curr_pos] = pdata[spid];
							int res = 0;
							for (int ids = 0;ids<stages;++ids)
								res += BTB[ids][ureg[0][(curr_pos + ids + 1) % stages]];
							outptr[spid] = (res >>15);
							++clk;
						}
					}
					else
					{
						const unsigned (* pdata)[2] = (const unsigned ( *)[2]) packagedta.data();
						const int samples = packagedta.size()/sizeof(char)/2;
						for (int spid = 0;spid < samples; ++ spid)
						{
							int curr_pos = clk % stages;
							reg[0][curr_pos] = pdata[spid][0];
							reg[1][curr_pos] = pdata[spid][1];
							int res[2] = {0,0};
							for (int ids = 0;ids<stages;++ids)
							{
								res[0] += BTB[ids][ureg[0][(curr_pos + ids + 1) % stages]];
								res[1] += BTB[ids][ureg[1][(curr_pos + ids + 1) % stages]];
							}
							outptr[spid*2] = (res[0] >>15);
							outptr[spid*2+1] = (res[1] >>15);
							++clk;
						}
					}
				}
					break;
				default:
					break;
				}


				//播发
				if (iout)
				{
					push_subject(iout,instance,vec_output.size()*2,
								 (unsigned char *)vec_output.constData()
								 );
				}


			}
		}


	}
	catch (const char * errMessage)
	{
		//向所有部位广播，偶要退出。
		push_subject(control_subect_id(),/*instance,broadcast_destin_id(),*/0,errMessage);
		fprintf(stderr,"Error:%s.",errMessage);
		fflush (stderr);
		res = -1;
	}

	return res;
}

