
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <unistd.h>
#include <pthread.h>
#include "pgLibTunnel.h"

void DebugLogOut(unsigned int uLevel, const char* lpszOut)
{
	printf("%s", lpszOut);
}

void GetInput(char* lpszBuf, unsigned int uBufSize)
{
	fgets(lpszBuf, uBufSize, stdin);

	// trim string.
	char* pszTemp0 = lpszBuf;
	while ((*pszTemp0) != '\0') {
		if (strchr(" \r\n\t", (*pszTemp0)) == 0) {
			break;
		}
		pszTemp0++;
	}

	char* pszTemp1 = lpszBuf + strlen(lpszBuf);
	while (pszTemp1 > pszTemp0) {
		if (strchr(" \r\n\t", (*(pszTemp1 - 1))) == 0) {
			break;
		}
		pszTemp1--;
	}

	int iSize = (pszTemp1 - pszTemp0);
	memmove(lpszBuf, pszTemp0, iSize);
	lpszBuf[iSize] = '\0';
}

static void ReplaceDevID(char* lpszSysInfo, char* lpszDevID)
{
	if (lpszDevID[0] == '\0') {
		return;
	}

	char* pszTemp = strstr(lpszSysInfo, "(DevID){");
	if (pszTemp == 0) {
		return;
	}

	pszTemp += strlen("(DevID){");
	char* pszTemp1 = strchr(pszTemp, '}');
	if (pszTemp1 == 0) {
		return;
	}

	unsigned int uDevIDSize = strlen(lpszDevID);
	unsigned int uDevIDSize1 = pszTemp1 - pszTemp;
	if (uDevIDSize != uDevIDSize1) {
		memmove((pszTemp + uDevIDSize), pszTemp1, strlen(pszTemp1));
	}

	memcpy(pszTemp, lpszDevID, uDevIDSize);
}

static void VersionGet()
{
	PG_TUNNEL_VERSION_S stVersion;
	memset(&stVersion, 0, sizeof(stVersion));

	int iErr = pgTunnelVersionGet(&stVersion);
	if (iErr == PG_TUNNEL_ERROR_OK) {
		printf("Version: %s\n", stVersion.szVersion);
	}
	else {
		printf("Get version failed, Error: %d\n", iErr);
	}
}

static void StatusGet()
{
	PG_TUNNEL_STATUS_S stStatus;
	memset(&stStatus, 0, sizeof(stStatus));

	int iErr = pgTunnelStatusGet(PG_TUNNEL_GET_STA_LOGIN, &stStatus);
	if (iErr == PG_TUNNEL_ERROR_OK) {
		printf("Status: %u\n", stStatus.uStatus);
	}
	else {
		printf("Get status failed, Error: %d\n", iErr);
	}
}

static void DomainSet()
{
	char szPasscode[128] = {0};

	printf("passcode: ");
	GetInput(szPasscode, sizeof(szPasscode));

	int iErr1 = pgTunnelDomainSet(szPasscode);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Set domain success\n");
	}
	else {
		printf("Set domain failed, Error: %d\n", iErr1);
	}
}

static void DomainGet()
{
	PG_TUNNEL_DOMAIN_S stDomain;
	memset(&stDomain, 0, sizeof(stDomain));

	int iErr = pgTunnelDomainGet(&stDomain);
	if (iErr == PG_TUNNEL_ERROR_OK) {
		printf("Domain: %s\n", stDomain.szDomain);
	}
	else {
		printf("Get domain failed, Error: %d\n", iErr);
	}
}

static void CommentSet()
{
	char szComment[128] = {0};

	printf("comment: ");
	GetInput(szComment, sizeof(szComment));

	int iErr1 = pgTunnelCommentSet(szComment);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Set comment success\n");
	}
	else {
		printf("Set comment failed, Error: %d\n", iErr1);
	}
}

static void CommentGet()
{
	PG_TUNNEL_COMMENT_S stComment;
	memset(&stComment, 0, sizeof(stComment));

	int iErr = pgTunnelCommentGet(&stComment);
	if (iErr == PG_TUNNEL_ERROR_OK) {
		printf("Comment: %s\n", stComment.szComment);
	}
	else {
		printf("Get comment failed, Error: %d\n", iErr);
	}
}

static void TunnelSet()
{
	char szPasscode[128] = {0};
	char szComment[128] = {0};

	printf("passcode: ");
	GetInput(szPasscode, sizeof(szPasscode));

	printf("comment: ");
	GetInput(szComment, sizeof(szComment));

	int iErr1 = pgTunnelTunnelSet(szPasscode, szComment);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Set tunnel success\n");
	}
	else {
		printf("Set tunnel failed, Error: %d\n", iErr1);
	}
}

static void PeerInfoGet()
{
	char szPeerID[128] = {0};

	PG_TUNNEL_PEER_INFO_S stPeerInfo;
	memset(&stPeerInfo, 0, sizeof(stPeerInfo));

	printf("peer id: ");
	GetInput(szPeerID, sizeof(szPeerID));

	int iErr1 = pgTunnelPeerInfoGet(szPeerID, &stPeerInfo);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Get peer info success\n");

		printf("peer id: %s\n", stPeerInfo.szPeerID);
		printf("type: %u\n", stPeerInfo.uType);
		printf("local login addr: %s\n", stPeerInfo.szAddrLocal);
		printf("remote login addr: %s\n", stPeerInfo.szAddrRemote);
		printf("local tunnel addr: %s\n", stPeerInfo.szTunnelLocal);
		printf("remote tunnel addr: %s\n", stPeerInfo.szTunnelRemote);
		printf("remote private addr: %s\n", stPeerInfo.szPrivateRemote);
	}
	else {
		printf("Get peer info failed, Error: %d\n", iErr1);
	}
}

static void ConnectAdd()
{
	char szPeerID[128] = {0};
	char szType[32] = {0};
	char szEncrypt[32] = {0};
	char szListenAddr[64] = {0};
	char szClientAddr[64] = {0};

	PG_TUNNEL_CLIENT_ADDR_S stClientAddr;
	memset(&stClientAddr, 0, sizeof(stClientAddr));

	printf("peer id: ");
	GetInput(szPeerID, sizeof(szPeerID));
	printf("type: ");
	GetInput(szType, sizeof(szType));
	printf("encrypt: ");
	GetInput(szEncrypt, sizeof(szEncrypt));
	printf("listen addr: ");
	GetInput(szListenAddr, sizeof(szListenAddr));
	printf("client addr: ");
	GetInput(szClientAddr, sizeof(szClientAddr));

	int iErr1 = pgTunnelConnectAdd(
		"",
		szPeerID,
		(unsigned int)atoi(szType),
		(unsigned int)atoi(szEncrypt),
		szListenAddr,
		szClientAddr,
		&stClientAddr);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Add connect success\n");
	}
	else {
		printf("Add connect failed, Error: %d\n", iErr1);
	}
}

static void ConnectDelete()
{
	char szPeerID[128] = {0};
	char szType[32] = {0};
	char szEncrypt[32] = {0};
	char szListenAddr[64] = {0};
	char szClientAddr[64] = {0};

	printf("peer id: ");
	GetInput(szPeerID, sizeof(szPeerID));
	printf("type: ");
	GetInput(szType, sizeof(szType));
	printf("encrypt: ");
	GetInput(szEncrypt, sizeof(szEncrypt));
	printf("listen addr: ");
	GetInput(szListenAddr, sizeof(szListenAddr));
	printf("client addr: ");
	GetInput(szClientAddr, sizeof(szClientAddr));

	int iErr1 = pgTunnelConnectDelete(
		"",
		szPeerID,
		(unsigned int)atoi(szType),
		(unsigned int)atoi(szEncrypt),
		szListenAddr,
		szClientAddr);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Delete connect success\n");
	}
	else {
		printf("Delete connect failed, Error: %d\n", iErr1);
	}
}

static void ConnectQuery()
{
	char szPeerID[128] = {0};
	char szType[32] = {0};
	char szEncrypt[32] = {0};
	char szListenAddr[64] = {0};

	PG_TUNNEL_CLIENT_ADDR_S stClientAddr;
	memset(&stClientAddr, 0, sizeof(stClientAddr));

	printf("peer id: ");
	GetInput(szPeerID, sizeof(szPeerID));
	printf("type: ");
	GetInput(szType, sizeof(szType));
	printf("encrypt: ");
	GetInput(szEncrypt, sizeof(szEncrypt));
	printf("listen addr: ");
	GetInput(szListenAddr, sizeof(szListenAddr));
	printf("client addr: ");
	GetInput(stClientAddr.szClientAddr, sizeof(stClientAddr.szClientAddr));

	int iErr1 = pgTunnelConnectQuery(
		szPeerID,
		(unsigned int)atoi(szType),
		(unsigned int)atoi(szEncrypt),
		szListenAddr,
		&stClientAddr);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("The connect's client addr: %s\n", stClientAddr.szClientAddr);
	}
	else {
		printf("Query connect failed, Error: %d\n", iErr1);
	}
}

static void ConnectLocalDelete()
{
	char szClientAddr[64] = {0};

	printf("client addr: ");
	GetInput(szClientAddr, sizeof(szClientAddr));

	int iErr1 = pgTunnelConnectLocalDelete("", szClientAddr);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Delete connect by local address success\n");
	}
	else {
		printf("Delete connect by local address failed, Error: %d\n", iErr1);
	}
}

static void ConnectLocalQuery()
{
	char szClientAddr[64] = {0};

	PG_TUNNEL_CONNECT_INFO_S stConnectInfo;
	memset(&stConnectInfo, 0, sizeof(stConnectInfo));

	printf("client addr: ");
	GetInput(szClientAddr, sizeof(szClientAddr));

	int iErr1 = pgTunnelConnectLocalQuery(szClientAddr, &stConnectInfo);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Query connect by local address success\n");

		printf("peer id: %s\n", stConnectInfo.szPeerID);
		printf("type: %u\n", stConnectInfo.uType);
		printf("encrypt: %u\n", stConnectInfo.uEncrypt);
		printf("listen addr: %s\n", stConnectInfo.szListenAddr);
		printf("client addr: %s\n", stConnectInfo.szClientAddr);
	}
	else {
		printf("Query connect by local address failed, Error: %d\n", iErr1);
	}
}

static void UserExtend()
{
	char szReqData[256] = {0};

	unsigned char ucReplyData[256] = {0};
	memset(ucReplyData, 0, sizeof(ucReplyData));
	PG_TUNNEL_DATA_S& stReplyData = *((PG_TUNNEL_DATA_S*)ucReplyData);
	stReplyData.uSize = sizeof(ucReplyData) - sizeof(unsigned int);

	printf("request data: ");
	GetInput(szReqData, sizeof(szReqData));

	int iErr1 = pgTunnelUserExtend(szReqData, &stReplyData, 0);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("User extend request success\n");

		printf("reply data: %s\n", stReplyData.szData);
	}
	else {
		printf("User extend request failed, Error: %d\n", iErr1);
	}
}

static void PushGet()
{
	unsigned char ucPushData[256] = {0};
	memset(ucPushData, 0, sizeof(ucPushData));
	PG_TUNNEL_DATA_S& stPushData = *((PG_TUNNEL_DATA_S*)ucPushData);
	stPushData.uSize = sizeof(ucPushData) - sizeof(unsigned int);

	int iErr1 = pgTunnelPushGet(&stPushData, 1000);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Push get success\n");

		printf("push data: %s\n", stPushData.szData);
	}
	else {
		printf("Push get failed, Error: %d\n", iErr1);
	}
}

static void SelfGet()
{
	PG_TUNNEL_SELF_S stSelf;
	memset(&stSelf, 0, sizeof(stSelf));

	int iErr1 = pgTunnelSelfGet(&stSelf);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Self get success\n");

		printf("Self ID: %s\n", stSelf.szSelf);
	}
	else {
		printf("Self get failed, Error: %d\n", iErr1);
	}
}

static void ConnectEnum()
{
	char szIndex[64] = {0};

	PG_TUNNEL_CONNECT_INFO_S stConnectInfo;
	memset(&stConnectInfo, 0, sizeof(stConnectInfo));

	printf("index: ");
	GetInput(szIndex, sizeof(szIndex));

	int iErr1 = pgTunnelConnectEnum((unsigned int)atoi(szIndex), &stConnectInfo);
	if (iErr1 == PG_TUNNEL_ERROR_OK) {
		printf("Connect enum success\n");

		printf("peer id: %s\n", stConnectInfo.szPeerID);
		printf("type: %u\n", stConnectInfo.uType);
		printf("encrypt: %u\n", stConnectInfo.uEncrypt);
		printf("listen addr: %s\n", stConnectInfo.szListenAddr);
		printf("client addr: %s\n", stConnectInfo.szClientAddr);
	}
	else {
		printf("Connect enum failed, Error: %d\n", iErr1);
	}
}

static void ConnectList()
{
	PG_TUNNEL_CONNECT_INFO_S stConnectInfo;
	memset(&stConnectInfo, 0, sizeof(stConnectInfo));

	unsigned int uIndex = 0;
	while (1) {
		int iErr1 = pgTunnelConnectEnum(uIndex, &stConnectInfo);
		if (iErr1 != PG_TUNNEL_ERROR_OK) {
			if (iErr1 != PG_TUNNEL_ERROR_NOEXIST) {
				printf("Connect enum failed, Error: %d\n", iErr1);
			}
			break;
		}

		printf("-----------------------------\n");
		printf("index: %u\n", uIndex);
		printf("peer id: %s\n", stConnectInfo.szPeerID);
		printf("type: %u\n", stConnectInfo.uType);
		printf("encrypt: %u\n", stConnectInfo.uEncrypt);
		printf("listen addr: %s\n", stConnectInfo.szListenAddr);
		printf("client addr: %s\n\n", stConnectInfo.szClientAddr);

		uIndex++;
	}
}

static void LoginNow()
{
	int iErr1 = pgTunnelControl(PG_TUNNEL_CONTROL_LOGIN_NOW, "");
	if (iErr1 != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelControl: login now, iErr=%d\n", iErr1);
	}
}

static void ChannelAuto()
{
	char szPeerID[128] = { 0 };

	printf("peer id: ");
	GetInput(szPeerID, sizeof(szPeerID));

	int iErr1 = pgTunnelChannelSet(szPeerID, PG_TUNNEL_CHANNEL_AUTO, "");
	if (iErr1 != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelChannelSet: iErr=%d\n", iErr1);
	}
}

static void ChannelRelay()
{
	char szPeerID[128] = { 0 };

	printf("peer id: ");
	GetInput(szPeerID, sizeof(szPeerID));

	int iErr1 = pgTunnelChannelSet(szPeerID, PG_TUNNEL_CHANNEL_RELAY, "");
	if (iErr1 != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelChannelSet: iErr=%d\n", iErr1);
	}
}

static void PeerFwdCfg()
{
	char szValue[128] = { 0 };

	int iSpeed = -1;
	printf("speed: ");
	GetInput(szValue, sizeof(szValue));
	if (szValue[0] != '\0') {
		iSpeed = atoi(szValue);
	}

	int iGate = -1;
	printf("gate: ");
	GetInput(szValue, sizeof(szValue));
	if (szValue[0] != '\0') {
		iGate = atoi(szValue);
	}

	int iUse = -1;
	printf("use: ");
	GetInput(szValue, sizeof(szValue));
	if (szValue[0] != '\0') {
		iUse = atoi(szValue);
	}

	int iMaxSess = -1;
	printf("maxsess: ");
	GetInput(szValue, sizeof(szValue));
	if (szValue[0] != '\0') {
		iMaxSess = atoi(szValue);
	}

	int iErr1 = pgTunnelPeerFwdCfg(iSpeed, iGate, iUse, iMaxSess, "");
	if (iErr1 != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelPeerFwdCfg: iErr=%d\n", iErr1);
	}
}

static void PeerAuthSet()
{
	char szPeerID[128] = { 0 };
	char szInfo[256] = { 0 };

	printf("peer id: ");
	GetInput(szPeerID, sizeof(szPeerID));

	printf("auth info: ");
	GetInput(szInfo, sizeof(szInfo));

	int iErr = pgTunnelPeerAuthSet(szPeerID, szInfo);
	if (iErr != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelPeerAuthSet: iErr=%d\n", iErr);
	}
}

static void PeerAuthReply()
{
	char szHandle[128] = { 0 };
	char szValue[128] = { 0 };
	char szParam[256] = { 0 };

	printf("handle: ");
	GetInput(szHandle, sizeof(szHandle));

	printf("errcode: ");
	GetInput(szValue, sizeof(szValue));
	unsigned int uErrCode = (unsigned int)atoi(szValue);

	printf("param: ");
	GetInput(szParam, sizeof(szParam));

	int iErr = pgTunnelPeerAuthReply(szHandle, uErrCode, szParam);
	if (iErr != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelPeerAuthReply: iErr=%d\n", iErr);
	}
}

static void ReloadCfg()
{
	const char* pszTemp = \
		"(SvrFilter){"
		"  (0){127.255.255.255:8080}"
		"}";

	int iErr1 = pgTunnelControl(PG_TUNNEL_CONTROL_RELOAD_CFG, pszTemp);
	if (iErr1 != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelControl: reload cfg, iErr=%d\n", iErr1);
	}
}

#if 0
static void TestConnect()
{
	char szTestTimes[64] = {0};

	printf("test times: ");
	GetInput(szTestTimes, sizeof(szTestTimes));

	unsigned int uTestTimes = (unsigned int)atoi(szTestTimes);

	unsigned int uInd = 0;
	while (uInd < uTestTimes) {
		PG_TUNNEL_CLIENT_ADDR_S stClientAddr;
		memset(&stClientAddr, 0, sizeof(stClientAddr));
		
		char szCltAddr[128] = {0};
//		sprintf(szCltAddr, "127.0.0.1:1800%u", uInd);
		sprintf(szCltAddr, "127.0.0.1:18000");

		int iErr = pgTunnelConnectAdd("", "0000", 0, 0,
			"127.0.0.1:8000", szCltAddr, &stClientAddr);
		if (iErr == PG_TUNNEL_ERROR_OK) {
			printf("The connect's local client addr: %s\n", stClientAddr.szClientAddr);
		}
		else {
			printf("Add connect failed, Error: %d\n", iErr);
		}

//		usleep(200 * 1000);

		int iErr1 = pgTunnelConnectDelete("", "0000", 0, 0,
			"127.0.0.1:8000", szCltAddr);
		if (iErr1 == PG_TUNNEL_ERROR_OK) {
			printf("Delete connect success\n");
		}
		else {
			printf("Delete connect failed, Error: %d\n", iErr1);
		}

		usleep(500 * 1000);
		uInd++;
	}
}
#endif

static void TestConnect()
{
	char szPeerID[128] = { 0 };
	unsigned int uTestTimes = 0;

	printf("test times: ");
	char szTestTimes[64] = { 0 };
	GetInput(szTestTimes, sizeof(szTestTimes));
	uTestTimes = (unsigned int)atoi(szTestTimes);

	printf("peer id: ");
	GetInput(szPeerID, sizeof(szPeerID));

	unsigned int uInd = 0;
	while (uInd < uTestTimes) {

		unsigned int uInd1 = 0;
		while (uInd1 < 10) {
			PG_TUNNEL_CLIENT_ADDR_S stClientAddr;
			memset(&stClientAddr, 0, sizeof(stClientAddr));

			char szAddrListen[64] = { 0 };
			sprintf(szAddrListen, "127.0.0.1:800%u", uInd1);

			char szAddrClient[64] = { 0 };
			sprintf(szAddrClient, "127.0.0.1:1800%u", uInd1);

			int iErr = pgTunnelConnectAdd("", szPeerID, 0, 0,
				szAddrListen, szAddrClient, &stClientAddr);
			if (iErr == PG_TUNNEL_ERROR_OK) {
				printf("The connect's local client addr: %s\n", stClientAddr.szClientAddr);
			}
			else {
				printf("Add connect failed, Error: %d\n", iErr);
			}

			uInd1++;
		}

		uInd1 = 0;
		while (uInd1 < 10) {
			PG_TUNNEL_CONNECT_INFO_S stConnectInfo;
			memset(&stConnectInfo, 0, sizeof(stConnectInfo));
			int iErr1 = pgTunnelConnectEnum(uInd1, &stConnectInfo);
			if (iErr1 == PG_TUNNEL_ERROR_OK) {
				printf("Connect enum success\n");

				printf("peer id: %s\n", stConnectInfo.szPeerID);
				printf("type: %u\n", stConnectInfo.uType);
				printf("encrypt: %u\n", stConnectInfo.uEncrypt);
				printf("listen addr: %s\n", stConnectInfo.szListenAddr);
				printf("client addr: %s\n", stConnectInfo.szClientAddr);
			}
			else {
				printf("Connect enum failed, Error: %d\n", iErr1);

				char szAddrClient[64] = { 0 };
				sprintf(szAddrClient, "127.0.0.1:1800%u", uInd1);
				int iErr1 = pgTunnelConnectLocalQuery(szAddrClient, &stConnectInfo);
				if (iErr1 == PG_TUNNEL_ERROR_OK) {
					printf("Connect local query success\n");

					printf("peer id: %s\n", stConnectInfo.szPeerID);
					printf("type: %u\n", stConnectInfo.uType);
					printf("encrypt: %u\n", stConnectInfo.uEncrypt);
					printf("listen addr: %s\n", stConnectInfo.szListenAddr);
					printf("client addr: %s\n", stConnectInfo.szClientAddr);
				}
			}

			uInd1++;
		}

		usleep(200 * 1000);

		uInd1 = 0;
		while (uInd1 < 10) {
			char szAddrListen[64] = { 0 };
			sprintf(szAddrListen, "127.0.0.1:800%u", uInd1);

			char szAddrClient[64] = { 0 };
			sprintf(szAddrClient, "127.0.0.1:1800%u", uInd1);

			int iErr1 = pgTunnelConnectDelete("", szPeerID, 0, 0,
				szAddrListen, szAddrClient);
			if (iErr1 == PG_TUNNEL_ERROR_OK) {
				printf("Delete connect success\n");
			}
			else {
				printf("Delete connect failed, Error: %d\n", iErr1);
			}

			uInd1++;
		}

		usleep(1000 * 1000);
		uInd++;
	}
}

static pthread_t s_tThread;
static unsigned int s_uRunning = 0;

static void* TestThreadProc(void* p)
{
	unsigned int uInd = 0;
	while (uInd < 50) {
		PG_TUNNEL_CLIENT_ADDR_S stClientAddr;
		memset(&stClientAddr, 0, sizeof(stClientAddr));
		
		char szCltAddr[128] = {0};
//		sprintf(szCltAddr, "127.0.0.1:1900%u", uInd);
		sprintf(szCltAddr, "127.0.0.1:19000");

		int iErr = pgTunnelConnectAdd("", "0000", 0, 0,
			"127.0.0.1:8000", szCltAddr, &stClientAddr);
		if (iErr == PG_TUNNEL_ERROR_OK) {
			printf("Thread: The connect's local client addr: %s\n", stClientAddr.szClientAddr);
		}
		else {
			printf("Thread: Add connect failed, Error: %d\n", iErr);
		}

//		usleep(200 * 1000);

		int iErr1 = pgTunnelConnectDelete("", "0000", 0, 0,
			"127.0.0.1:8000", szCltAddr);
		if (iErr1 == PG_TUNNEL_ERROR_OK) {
			printf("Thread: Delete connect success\n");
		}
		else {
			printf("Thread: Delete connect failed, Error: %d\n", iErr1);
		}

		usleep(200 * 1000);
		uInd++;
	}
	
	pthread_exit(NULL);
	return (void*)0;
}

static void TestThreadStart()
{
	// In this demo, we use a thread to simulate video input.
	pthread_attr_t attr;
	pthread_attr_init(&attr);

	s_uRunning = 1;
	int iRet = pthread_create(&s_tThread, &attr, TestThreadProc, 0);
	if (iRet != 0) {
		s_uRunning = 0;
	}

	pthread_attr_destroy(&attr);
}

static void TestThreadStop()
{
	s_uRunning = 0;
}

static void PrintHelp()
{
	printf("  quit                Quit this demo program\n");
	printf("  versionget          Get the tunnel SDK version.\n");
	printf("  statusget           Get the login status of tunnel SDK.\n");
	printf("  domainget\n");
	printf("  domainset\n");
	printf("  commentget\n");
	printf("  commentset\n");
	printf("  tunnelset\n");
	printf("  peerinfoget         Query the connect status of remote p2p id\n");
	printf("  connectadd          Add a tunnel map.\n");
	printf("  connectdelete       Delete a tunnel map via all parameters of tunnel.\n");
	printf("  connectquery        Query a tunnel map via all parameters of tunnel.\n");
	printf("  connectlocaldelete  Delete a tunnel map via client address.\n");
	printf("  connectlocalquery   Query a tunnel map via client address.\n");
	printf("  userextend          Send a extend request to p2p server.\n");
	printf("  pushget             Get one push message from p2p server.\n");
	printf("  selfget             Get self p2p id.\n");
	printf("  connectenum         Enum tunnel map one by one.\n");
	printf("  connectlist         List all the tunnel maps.\n");
	printf("  loginnow            Try to send login request immidiately.\n");
	printf("  chnauto             Self-adapt to select p2p channel or relay channel.\n");
	printf("  chnrelay            Force to use relay channel\n");
	printf("  peerfwdcfg          Config peer forward parameters\n");
	printf("  peerauthset         Set peer auth info\n");
	printf("  peerauthreply       Reply the peer auth request\n");
	printf("  reloadcfg           Reload config parameters from string\n");
}


static void TunnelEventPeerAuthRequest(const char* lpszParam)
{
	const char* pszTemp = lpszParam;

	char szHandle[128] = { 0 };
	pszTemp = strstr(pszTemp, "\"handle\":");
	if (pszTemp != 0) {
		pszTemp += strlen("\"handle\":\"");
		const char* pszTemp1 = strchr(pszTemp, '\"');
		if (pszTemp1 != 0) {
			unsigned int uSizeHnd = pszTemp1 - pszTemp;
			if (uSizeHnd < sizeof(szHandle)) {
				memcpy(szHandle, pszTemp, uSizeHnd);
				szHandle[uSizeHnd] = '\0';
				pszTemp = pszTemp1 + 1;
			}
		}
	}

	char szInfo[256] = { 0 };
	pszTemp = strstr(pszTemp, "\"info\":");
	if (pszTemp != 0) {
		pszTemp += strlen("\"info\":\"");
		const char* pszTemp1 = strchr(pszTemp, '\"');
		if (pszTemp1 != 0) {
			unsigned int uSizeInfo = pszTemp1 - pszTemp;
			if (uSizeInfo < sizeof(szInfo)) {
				memcpy(szInfo, pszTemp, uSizeInfo);
				szInfo[uSizeInfo] = '\0';
			}
		}
	}

	unsigned int uErrCode;
	if (strcmp(szInfo, "abcd1234") == 0) {
		uErrCode = PG_TUNNEL_ERROR_OK;
	}
	else {
		uErrCode = -PG_TUNNEL_ERROR_REJECT;
	}

	pgTunnelPeerAuthReply(szHandle, uErrCode, "test reply");
}

static void TunnelEventProc(unsigned int uEvent, const char* lpszParam)
{
	printf("Callback: uEvent=%u, sParam=%s\n", uEvent, lpszParam);

	if (uEvent == PG_TUNNEL_EVENT_PEER_AUTH_REQUEST) {
		TunnelEventPeerAuthRequest(lpszParam);
	}
}

int main(int argc, char* argv[])
{
	char szDevID[128] = {0};

	printf("Input the device id: ");
	GetInput(szDevID, sizeof(szDevID));
	if (szDevID[0] == '\0') {
		printf("empty device id\n");
		return 0;
	}

	char szSysInfo[256] = {0};
	int iErr = pgTunnelSysInfo("demoTunnel.cfg", szSysInfo, sizeof(szSysInfo));
	if (iErr != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelSysInfo: iErr=%d\n", iErr);
		sprintf(szSysInfo, "(DevID){%u}", 123456);
	}

	// Replace the DevID in 'szSysInfo'.
	ReplaceDevID(szSysInfo, szDevID);
	printf("%s\n", szSysInfo);

	// Set event callback
	pgTunnelCallbackSet(TunnelEventProc);

	// Start tunnel.
	iErr = pgTunnelStart("demoTunnel.cfg", szSysInfo, 0, DebugLogOut);
	if (iErr != PG_TUNNEL_ERROR_OK) {
		printf("pgTunnelStart: iErr=%d\n", iErr);
		return 0;
	}

	char szBuf[256] = {0};
	while (1) {
		printf("\n> ");

		GetInput(szBuf, sizeof(szBuf));
		if (strcmp(szBuf, "quit") == 0) {
			break;
		}
		
		if (strcmp(szBuf, "versionget") == 0) {
			VersionGet();
		}
		else if (strcmp(szBuf, "statusget") == 0) {
			StatusGet();
		}
		else if (strcmp(szBuf, "domainset") == 0) {
			DomainSet();
		}
		else if (strcmp(szBuf, "domainget") == 0) {
			DomainGet();
		}
		else if (strcmp(szBuf, "commentset") == 0) {
			CommentSet();
		}
		else if (strcmp(szBuf, "commentget") == 0) {
			CommentGet();
		}
		else if (strcmp(szBuf, "tunnelset") == 0) {
			TunnelSet();
		}
		else if (strcmp(szBuf, "peerinfoget") == 0) {
			PeerInfoGet();
		}
		else if (strcmp(szBuf, "connectadd") == 0) {
			ConnectAdd();
		}
		else if (strcmp(szBuf, "connectdelete") == 0) {
			ConnectDelete();
		}
		else if (strcmp(szBuf, "connectquery") == 0) {
			ConnectQuery();
		}
		else if (strcmp(szBuf, "connectlocaldelete") == 0) {
			ConnectLocalDelete();
		}
		else if (strcmp(szBuf, "connectlocalquery") == 0) {
			ConnectLocalQuery();
		}
		else if (strcmp(szBuf, "userextend") == 0) {
			UserExtend();
		}
		else if (strcmp(szBuf, "pushget") == 0) {
			PushGet();
		}
		else if (strcmp(szBuf, "selfget") == 0) {
			SelfGet();
		}
		else if (strcmp(szBuf, "connectenum") == 0) {
			ConnectEnum();
		}
		else if (strcmp(szBuf, "connectlist") == 0) {
			ConnectList();
		}
		else if (strcmp(szBuf, "loginnow") == 0) {
			LoginNow();
		}
		else if (strcmp(szBuf, "chnauto") == 0) {
			ChannelAuto();
		}
		else if (strcmp(szBuf, "chnrelay") == 0) {
			ChannelRelay();
		}
		else if (strcmp(szBuf, "peerfwdcfg") == 0) {
			PeerFwdCfg();
		}
		else if (strcmp(szBuf, "peerauthset") == 0) {
			PeerAuthSet();
		}
		else if (strcmp(szBuf, "peerauthreply") == 0) {
			PeerAuthReply();
		}
		else if (strcmp(szBuf, "reloadcfg") == 0) {
			ReloadCfg();
		}
		else if (strcmp(szBuf, "testconnect") == 0) {
			TestConnect();
		}
		else if (strcmp(szBuf, "testthreadstart") == 0) {
			TestThreadStart();
		}
		else if (strcmp(szBuf, "testthreadstop") == 0) {
			TestThreadStop();
		}
		else {
			PrintHelp();
		}
	}

	pgTunnelStop();

	return 0;
}

