#include "VrNTPUtils.h"

#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <WS2tcpip.h>
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
#else
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <arpa/inet.h>

#define SOCKET int
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#endif
#include <time.h>

struct   NTP_Packet
{
	int			Control_Word;
	int			root_delay;
	int			root_dispersion;
	int			reference_identifier;
	long long	reference_timestamp;
	long long	originate_timestamp;
	long long	receive_timestamp;
	int			transmit_timestamp_seconds;
	int			transmit_timestamp_fractions;
};

CrNTPUtils::CrNTPUtils()
	: m_bValidIP(false)
	, m_nPort(123)
{
}

CrNTPUtils::~CrNTPUtils()
{
}


/// 初始化NTPClient
bool CrNTPUtils::InitNTPClient(char* sIP, int nPort)
{
	bool bRet = true;

#ifdef _WIN32
	WORD    wVersionRequested;
	WSADATA wsaData;

	// 初始化版本
	wVersionRequested = MAKEWORD(1, 1);
	if (0 != WSAStartup(wVersionRequested, &wsaData))
	{
		WSACleanup();
		return false;
	}
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
	{
		WSACleanup();
		return false;
	}
#endif
	if (nullptr != sIP)
	{
		m_bValidIP = true;
#ifdef _WIN32
		strcpy_s(m_sIP, sIP);
#else
		strcpy(m_sIP, sIP);
#endif // _WIN32

	}
	return true;
}


/// 退出NTPClient
bool CrNTPUtils::ExitNTPClient()
{
	bool bRet = true;


	return bRet;
}

///获取时间
bool CrNTPUtils::GetSystemTime(SYSTEMTIME& newtime)
{
	// 这个IP是中国大陆时间同步服务器地址，可自行修改
	SOCKET soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	struct sockaddr_in addrSrv;

	// time.ustc.edu.cn 202.38.64.7
	// ntp.aliyun.com  203.107.6.88
#ifdef _WIN32
	if (!m_bValidIP)
	{
		inet_pton(AF_INET, "203.107.6.88", &addrSrv);//time.ustc.edu.cn");
	}
	else
	{
		inet_pton(AF_INET, m_sIP, &addrSrv);
	}
#else
	if (!m_bValidIP)
	{
		addrSrv.sin_addr.s_addr = inet_addr("203.107.6.88");//time.ustc.edu.cn");
	}
	else
	{
		addrSrv.sin_addr.s_addr = inet_addr(m_sIP);
	}
#endif // _WIN32

	
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(m_nPort);

	NTP_Packet NTP_Send, NTP_Recv;
	NTP_Send.Control_Word = htonl(0x1B000000);
	NTP_Send.root_delay = 0;
	NTP_Send.root_dispersion = 0;
	NTP_Send.reference_identifier = 0;
	NTP_Send.reference_timestamp = 0;
	NTP_Send.originate_timestamp = 0;
	NTP_Send.receive_timestamp = 0;
	NTP_Send.transmit_timestamp_seconds = 0;
	NTP_Send.transmit_timestamp_fractions = 0;

	if (SOCKET_ERROR == sendto(soc, (const char*)&NTP_Send, sizeof(NTP_Send),
		0, (struct sockaddr*)&addrSrv, sizeof(addrSrv)))
	{
#ifdef _WIN32
		closesocket(soc);
#else
		close(soc);
#endif // _WIN32

		return false;
	}
	socklen_t sockaddr_Size = sizeof(addrSrv);
	if (SOCKET_ERROR == recvfrom(soc, (char*)&NTP_Recv, sizeof(NTP_Recv), 0, (struct sockaddr*)&addrSrv, &sockaddr_Size))
	{
#ifdef _WIN32
		closesocket(soc);
#else
		close(soc);
#endif // _WIN32
		return false;
	}
#ifdef _WIN32
	closesocket(soc);
	WSACleanup();
#else
	close(soc);
#endif

	float			Splitseconds;
	struct tm*		lpLocalTime = nullptr;
	time_t			ntp_time;

	// 获取时间服务器的时间
	ntp_time = ntohl(NTP_Recv.transmit_timestamp_seconds) - 2208988800;
#ifdef _WIN32
	localtime_s(lpLocalTime , &ntp_time);
#else
	lpLocalTime = localtime(&ntp_time);
#endif

	if (lpLocalTime == nullptr)
	{
		return false;
	}

	// 获取新的时间
	newtime.wYear = lpLocalTime->tm_year + 1900;
	newtime.wMonth = lpLocalTime->tm_mon + 1;
	newtime.wDayOfWeek = lpLocalTime->tm_wday;
	newtime.wDay = lpLocalTime->tm_mday;
	newtime.wHour = lpLocalTime->tm_hour;
	newtime.wMinute = lpLocalTime->tm_min;
	newtime.wSecond = lpLocalTime->tm_sec;

	// 设置时间精度
	Splitseconds = (float)ntohl(NTP_Recv.transmit_timestamp_fractions);
	Splitseconds = (float)0.000000000200 * Splitseconds;
	Splitseconds = (float)1000.0 * Splitseconds;
	newtime.wMilliseconds = (unsigned   short)Splitseconds;
	return true;
}

/************************************************************************/
/* 函数说明:自动与时间服务器同步更新
/* 参数说明:无
/* 返 回 值:成功返回TRUE，失败返回FALSE
/************************************************************************/
bool CrNTPUtils::_UpdateDate()
{
	SYSTEMTIME newtime;
	bool bRet = GetSystemTime(newtime);
	// 修改本机系统时间
#ifdef _WIN32
	SetLocalTime(&newtime);
#endif
	return true;
}