// pgTunnelApp.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "pgSvcWin.h"
#include "pgLibTunnel.h"
#include "pgTunnelStatic.h"


#define SVC_NAME _T("pgTunnelStatic")


void DebugLogOut(unsigned int uLevel, const char* lpszOut)
{
}

static int UnicodeToUTF8(TCHAR* lpszUnicode, char* lpszChars, unsigned int uSize)
{
	const wchar_t* pwsSrc = (const wchar_t*)lpszUnicode;
	unsigned int uWCLen = wcslen(pwsSrc);

	int iMBCLen = ::WideCharToMultiByte(CP_UTF8, 0, pwsSrc, uWCLen, 0, 0, 0, 0);
	if ((int)uSize > iMBCLen) {
		iMBCLen = ::WideCharToMultiByte(CP_UTF8, 0, pwsSrc, uWCLen, lpszChars, uSize, 0, 0);
		lpszChars[iMBCLen] = '\0';
	}
	else {
		iMBCLen = 0;
		lpszChars[iMBCLen] = '\0';
	}

	return iMBCLen;
}

static void BuildCfgFile(TCHAR* lpszExt, char* lpszFile, unsigned int uSize)
{
	TCHAR tzPath[_MAX_PATH] = {0};
	if (::GetModuleFileName(0, tzPath, _MAX_PATH) <= 0) {
		pgSvcWinLog("pgTunnelStatic: GetModuleFileName failed");
		return;
	}

	TCHAR* ptzFile = _tcsrchr(tzPath, _T('\\'));
	if (ptzFile != 0) {
		ptzFile += 1;
	}
	else {
		ptzFile = tzPath;
	}

	TCHAR* ptzExt = _tcsrchr(ptzFile, _T('.'));
	if (ptzExt != 0) {
		_tcscpy(ptzExt, lpszExt);
	}
	else {
		_tcscat(ptzFile, lpszExt);
	}

	UnicodeToUTF8(ptzFile, lpszFile, uSize);
}

static void SetCurrentDir()
{
	TCHAR tzPath[_MAX_PATH] = { 0 };
	if (::GetModuleFileName(0, tzPath, _MAX_PATH) <= 0) {
		pgSvcWinLog("pgTunnelStatic: GetModuleFileName failed");
		return;
	}

	TCHAR* ptzFile = _tcsrchr(tzPath, _T('\\'));
	if (ptzFile != 0) {
		*ptzFile = _T('\0');
	}

	if (!::SetCurrentDirectory(tzPath)) {
		char szPath[_MAX_PATH] = {0};
		UnicodeToUTF8(tzPath, szPath, sizeof(szPath));
		pgSvcWinLog("pgTunnelStatic: SetCurrentDirectory failed, szPath=%s", szPath);
	}
}

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 StrTrim(char* lpszStr)
{
	char* pszTemp0 = lpszStr;
	while ((*pszTemp0) != '\0') {
		if (strchr(" \r\n\t", (*pszTemp0)) == 0) {
			break;
		}
		pszTemp0++;
	}

	char* pszTemp1 = lpszStr + strlen(lpszStr);
	while (pszTemp1 > pszTemp0) {
		if (strchr(" \r\n\t", (*(pszTemp1 - 1))) == 0) {
			break;
		}
		pszTemp1--;
	}
	
	int iSize = (pszTemp1 - pszTemp0);
	memmove(lpszStr, pszTemp0, iSize);
	lpszStr[iSize] = '\0';
}

static unsigned int LoadDevID(const char* lpszCfgFile, char* lpszDevID, unsigned int uSize)
{
	unsigned int uRet = 0;

	FILE* pFile = fopen(lpszCfgFile, "r");
	if (pFile != 0) {
		while (1) {
			char szLine[256] = {0};
			if (fgets(szLine, sizeof(szLine), pFile) == 0) {
				break;
			}

			StrTrim(szLine);

			if (strstr(szLine, "id=") == szLine) {
				unsigned int uSizeLine = strlen(szLine);
				unsigned int uSizeTag = strlen("id=");
				if ((uSizeLine - uSizeTag) < uSize) {
					strcpy(lpszDevID, &(szLine[uSizeTag]));
					StrTrim(lpszDevID);
					uRet = 1;
				}
				break;
			}
		}

		fclose(pFile);
	}
	else {
		pgSvcWinLog("pgTunnelStatic: fopen failed, errno=%d, Path=%s", errno, lpszCfgFile);
	}

	return uRet;
}

///
// class CPGSvcMod
class CPGSvcMod : public CPGSvcWin
{
public:
	void OnExit() {
		m_uExit = 1;
	}

	void OnRun() {
		pgSvcWinLog("pgTunnelStatic: Start");

		// Set current directly to exe path.
		SetCurrentDir();

		// Reset exit flag.
		m_uExit = 0;

		// Build ID file name.
		char szFile[256] = { 0 };
		BuildCfgFile(_T(".id"), szFile, sizeof(szFile));

		// Load device id form file.
		char szDevID[128] = {0};
		if (!LoadDevID(szFile, szDevID, sizeof(szDevID))) {
			pgSvcWinLog("pgTunnelStatic: Load device id from file failed, szFile=%s", szFile);
			return;
		}

		// Get the sysinfo.
		char szSysInfo[256] = {0};
		BuildCfgFile(_T(".cfg"), szFile, sizeof(szFile));
		if (pgTunnelSysInfo(szFile, szSysInfo, sizeof(szSysInfo)) != PG_TUNNEL_ERROR_OK) {
			pgSvcWinLog("pgTunnelStatic: Get system info failed, szFile=%s", szFile);
			return;
		}

		// Replace the device id.
		ReplaceDevID(szSysInfo, szDevID);

		TCHAR szMutexName[256] = { 0 };
		_stprintf(szMutexName, _T("Global\\_PG_TUNNEL_STATIC_%s"), m_szSvcName);

		m_hMutex = ::CreateMutex(0, FALSE, szMutexName);
		if (m_hMutex == 0) {
			pgSvcWinLog("pgTunnelStatic: Create mutex failed, szSvcName=%s", m_szSvcName);
			return;
		}

		if (::GetLastError() == ERROR_ALREADY_EXISTS) {
			pgSvcWinLog("pgTunnelStatic: Mutex has exist, szSvcName=%s", m_szSvcName);
			::CloseHandle(m_hMutex);
			m_hMutex = 0;
			return;
		}

		// Start tunnel
		int iErr = pgTunnelStart(szFile, szSysInfo, 0, DebugLogOut);
		if (iErr != PG_TUNNEL_ERROR_OK) {
			pgSvcWinLog("pgTunnelStatic: Tunnel start failed, Error=%d", iErr);
			return;
		}

		// Loop to wait exit.
		while (!m_uExit) {
			::Sleep(1000);
		}

		// Stop tunnel.
		pgTunnelStop();

		::CloseHandle(m_hMutex);
		m_hMutex = 0;

		pgSvcWinLog("pgTunnelStatic: exit");
	}

	void SetSvcName(LPCTSTR lpszSvcName) {
		_tcscpy(m_szSvcName, lpszSvcName);
	}

	CPGSvcMod() {
		m_uExit = 0;
		m_hMutex = 0;
		m_szSvcName[0] = _T('\0');
	}

	virtual ~CPGSvcMod() {
		if (m_hMutex != 0) {
			::CloseHandle(m_hMutex);
			m_hMutex = 0;
		}
	}


private:
	unsigned int m_uExit;
	HANDLE m_hMutex;

	TCHAR m_szSvcName[128];
};


static unsigned int CheckSvcName(TCHAR* lpszSvcName)
{
	if (_tcscmp(lpszSvcName, _T("install")) == 0
		|| _tcscmp(lpszSvcName, _T("uninstall")) == 0
		|| _tcscmp(lpszSvcName, _T("installm")) == 0
		|| _tcscmp(lpszSvcName, _T("run")) == 0)
	{
		return 0;
	}
	return 1;
}


int APIENTRY _tWinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	// Parse arguments.
	UINT argc = 0;
	TCHAR argv[4][128] = {_T(""), _T(""), _T(""), _T("")};
	TCHAR *ptzTemp0 = lpCmdLine;
	while (argc < 4 && ptzTemp0 != 0) {
		while ((*ptzTemp0) == _T(' ')) {
			ptzTemp0++;
		}
		if ((*ptzTemp0) == _T('\0')) {
			break;
		}
		TCHAR *ptzTemp1 = _tcschr(ptzTemp0, _T(' '));
		if (ptzTemp1 != 0) {
			UINT uSizeTemp = (ptzTemp1 - ptzTemp0);
			memcpy(argv[argc], ptzTemp0, (uSizeTemp * sizeof(TCHAR)));
			argv[argc][uSizeTemp] = _T('\0');
			ptzTemp0 = ptzTemp1 + 1;
		}
		else {
			_tcscpy(argv[argc], ptzTemp0);
			ptzTemp0 = 0;
		}
		argc++;
	}

	// The first argument is cmd.
	TCHAR tzCmd[256]= {0};
	if (argc > 0) {
		_tcscpy(tzCmd, argv[0]);
	}

	CPGSvcMod* pSvcMod = new CPGSvcMod;
	if (pSvcMod != 0) {

		if (_tcscmp(tzCmd, _T("install")) == 0) {

			// Build service name with arguments.
			TCHAR tzSvcName[128] = {0};
			if (argc > 1) {
				_tcscpy(tzSvcName, argv[1]);
			}
			else {
				_tcscpy(tzSvcName, SVC_NAME);
			}
			if (!CheckSvcName(tzSvcName)) {
				delete pSvcMod;
				return 0;
			}

			// Install to service list and start it.
			TCHAR tzSvcDesc[256] = {0};
			_stprintf(tzSvcDesc, _T("%s service"), tzSvcName);
			pSvcMod->SetSvcName(tzSvcName);
			if (pSvcMod->Install(tzSvcName, tzSvcDesc, 1)) {
				::Sleep(1000);
				pSvcMod->Start(tzSvcName);
			}
		}
		else if (_tcscmp(tzCmd, _T("installm")) == 0) {

			// Build service name with arguments.
			TCHAR tzSvcName[128] = {0};
			if (argc > 1) {
				_tcscpy(tzSvcName, argv[1]);
			}
			else {
				_tcscpy(tzSvcName, SVC_NAME);
			}
			if (!CheckSvcName(tzSvcName)) {
				delete pSvcMod;
				return 0;
			}

			// Install to service list and start it.
			TCHAR tzSvcDesc[256] = {0};
			_stprintf(tzSvcDesc, _T("%s service"), tzSvcName);
			pSvcMod->SetSvcName(tzSvcName);
			pSvcMod->Install(tzSvcName, tzSvcDesc, 0); // Not auto run.
		}
		else if (_tcscmp(tzCmd, _T("uninstall")) == 0) {

			// Build service name with arguments.
			TCHAR tzSvcName[128] = {0};
			if (argc > 1) {
				_tcscpy(tzSvcName, argv[1]);
			}
			else {
				_tcscpy(tzSvcName, SVC_NAME);
			}
			if (!CheckSvcName(tzSvcName)) {
				delete pSvcMod;
				return 0;
			}

			// Install from service list.
			pSvcMod->SetSvcName(tzSvcName);
			pSvcMod->Uninstall(tzSvcName);
		}
		else if (_tcscmp(tzCmd, _T("run")) == 0) {
			pSvcMod->SetSvcName(SVC_NAME);
			pSvcMod->Run();
		}
		else {
			// Build service name with arguments.
			TCHAR tzSvcName[128] = {0};
			if (argc > 0) {
				_tcscpy(tzSvcName, argv[0]);
			}
			else {
				_tcscpy(tzSvcName, SVC_NAME);
			}
			if (!CheckSvcName(tzSvcName)) {
				delete pSvcMod;
				return 0;
			}

			// Run app at forground.
			pSvcMod->SetSvcName(tzSvcName);
			pSvcMod->Init(tzSvcName);
		}

		delete pSvcMod;
	}

	return 0;
}

