#ifndef ALLINONE_H
#define ALLINONE_H
#include <QCoreApplication>
#include <QThread>
#include <QUdpSocket>
#include <QHostAddress>
#include <QTextStream>
#include <QNetworkDatagram>
#include <windows.h>
static const int tests = 10000;
static const int port = 23456;
static const int period = 20;
static const int batch = 100;
static const int packsize = 512;//pack size in 8 bytes
/*!
 * \brief test_Loop Test QUDPSocket using simple loop.
 * \return total send packages.
 */
inline int test_Loop()
{
	qint64 startID = -1, endID = -1;
	QUdpSocket * p = new QUdpSocket;
	char revdata[packsize*8];
	const long long * d = reinterpret_cast<const long long *>(revdata);
	if (p->bind(QHostAddress::LocalHost,port))
	{
		for (int i=0;i<tests;++i)
		{
			while (!(p->hasPendingDatagrams()));
				p->waitForReadyRead();
			int sz = p->readDatagram(revdata,packsize*8);
			if (sz<8)
				continue;
			if (d)
			{
				if (startID==-1)
					startID = *d;
				endID = *d;
			}
		}
		p->close();
	}
	p->deleteLater();
	return endID - startID + 1;
}

/*!
 * \brief The TestObj class test QUDPSocket using signal-slots.
 */
class TestObj:public QObject
{
	Q_OBJECT
public:
	explicit TestObj(QObject * pa = nullptr):QObject(pa){
		connect (this,&TestObj::_cmd_start,this,&TestObj::slot_start,Qt::QueuedConnection);
	}
	bool isFinished() const {return m_finished;}
	int startID() const {return m_startID;}
	int endID() const {return m_endID;}
protected slots:
	void readPdDt(){
		static char revdata[packsize*8];
		static const long long * d = reinterpret_cast<const long long *>(revdata);
		while (m_sock->hasPendingDatagrams())
		{
			int sz = m_sock->readDatagram(revdata,packsize*8);
			if (sz<8)
				continue;
			if (++m_total>tests)
			{
				m_sock->close();
				m_finished = true;
				QThread::currentThread()->quit();
				break;
			}
			if (d)
			{
				if (m_startID==-1)
					m_startID = *d;
				m_endID = *d;
			}
		}
	}
public:
	void start(){
		emit _cmd_start();
	}
private slots:
	void slot_start(){
		m_sock = new QUdpSocket(this);
		connect (m_sock,&QUdpSocket::readyRead,this,&TestObj::readPdDt);
		if (!m_sock->bind(QHostAddress::LocalHost,port))
		{
			m_sock->deleteLater();
			return;
		}
	}
signals:
	void _cmd_start();
private:
	int m_startID = -1;
	int m_endID = -1;
	int m_total = 0;
	bool m_finished = false;
	QUdpSocket * m_sock = nullptr;
};


/*!
 * \brief local_test test udp recv using local socket API
 * \return total send
 */
inline int local_test()
{
	//init wsa
	const WORD sockVision = MAKEWORD(2,2);
	WSADATA wsadata;
	//sockets
	SOCKET sock;
	if( WSAStartup(sockVision,&wsadata) != 0 )
	{
		printf("WSA initial failed.\n");
		return 0;
	}

	//Create sock
	sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
	if(sock == INVALID_SOCKET)
	{
		printf("socket creating failed.\n");
		return 0;
	}

	struct sockaddr_in sin;
	//Bind to Localhost:port
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//local host
	if( bind(sock,(LPSOCKADDR)&sin,sizeof(sin)) == SOCKET_ERROR )
	{
		printf("Can not bind to Address.\n");
		return 0;
	}
	struct sockaddr_in remoteAddr;
	int nAddrlen = sizeof(remoteAddr);
	int startID = -1, endID = -1;
	int total = 0;
	char revdata[packsize*8];
	const long long * pL = reinterpret_cast<const long long *>(revdata);
	while (total<tests)
	{
		int rev  = recvfrom(sock,revdata,packsize*8,0,(SOCKADDR*)&remoteAddr,&nAddrlen);
		if(rev >=8)
		{
			if (startID==-1)
				startID = *pL;
			endID = *pL;
			++total;
		}
	}
	closesocket(sock);
	WSACleanup();
	return endID - startID + 1;
}


/*!
 * \brief The sendingThread class send testing packages.
 */
class sendingThread: public QThread{
	Q_OBJECT
public:
	explicit sendingThread(QObject * pa = nullptr):QThread(pa){}
	void stop(){term = true;}
protected:
	void run() override{
		qint64 SendBuf[packsize] = {0};
		const char * dtp = reinterpret_cast<const char *>(SendBuf);

		int iResult;
		WSADATA wsaData;

		SOCKET SendSocket = INVALID_SOCKET;
		sockaddr_in RecvAddr;
		//----------------------
		// Initialize Winsock
		iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (iResult != NO_ERROR) {
			wprintf(L"WSAStartup failed with error: %d\n", iResult);
			return;
		}

		//---------------------------------------------
		// Create a socket for sending data
		SendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (SendSocket == INVALID_SOCKET) {
			wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
			WSACleanup();
			return;
		}
		//---------------------------------------------
		RecvAddr.sin_family = AF_INET;
		RecvAddr.sin_port = htons(port);
		RecvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

		//---------------------------------------------
		// Send a datagram to the receiver
		while(!term)
		{
			for (int i=0;i<batch;++i)
			{
				const int BufLen = sizeof(SendBuf) - rand()%(sizeof(SendBuf)-100);
				++SendBuf[0];
				iResult = sendto(SendSocket,
								 dtp, BufLen, 0, (SOCKADDR *) & RecvAddr, sizeof (RecvAddr));
				if (iResult == SOCKET_ERROR) {
					wprintf(L"sendto failed with error: %d\n", WSAGetLastError());
					closesocket(SendSocket);
					WSACleanup();
					return ;
				}
			}
			if (period>0)
				msleep(period);
		}
		//---------------------------------------------
		// When the application is finished sending, close the socket.
		wprintf(L"Finished sending. Closing socket.\n");
		iResult = closesocket(SendSocket);
		if (iResult == SOCKET_ERROR) {
			wprintf(L"closesocket failed with error: %d\n", WSAGetLastError());
			WSACleanup();
			return;
		}
		//---------------------------------------------
		// Clean up and quit.
		wprintf(L"Exiting.\n");
		WSACleanup();

	}
private:
	bool term = false;
};
#endif // ALLINONE_H