/*
Copyright 2001-2022 John Wiseman G8BPQ

This file is part of LinBPQ/BPQ32.

LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

LinBPQ/BPQ32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with LinBPQ/BPQ32.  If not, see http://www.gnu.org/licenses
*/	


#pragma data_seg("_BPQDATA")

#define _CRT_SECURE_NO_DEPRECATE

#include <time.h>

#define SD_RECEIVE      0x00
#define SD_SEND         0x01
#define SD_BOTH         0x02


#include "kernelresource.h"
#include "cheaders.h"
#include "tncinfo.h"
#ifndef LINBPQ
#include <commctrl.h>
#endif
//#include <stdlib.h>
#include "bpq32.h"
#include "adif.h"


HANDLE hInstance;
extern HBRUSH bgBrush;
extern HWND ClientWnd, FrameWnd;
extern int OffsetH, OffsetW;

extern HMENU hMainFrameMenu;
extern HMENU hBaseMenu;
extern HANDLE hInstance;

extern HKEY REGTREE;

extern int Ver[];


int KillTNC(struct TNCINFO * TNC);
int RestartTNC(struct TNCINFO * TNC);

char * GetChallengeResponse(char * Call, char *  ChallengeString);

VOID __cdecl Debugprintf(const char * format, ...);
int FromLOC(char * Locator, double * pLat, double * pLon);
BOOL ToLOC(double Lat, double Lon , char * Locator);

int GetPosnFromAPRS(char * Call, double * Lat, double * Lon);
char * stristr (char *ch1, char *ch2);


static RECT Rect;

#define WSA_ACCEPT WM_USER + 1
#define WSA_DATA WM_USER + 2
#define WSA_CONNECT WM_USER + 3

int Winmor_Socket_Data(int sock, int error, int eventcode);

struct WL2KInfo * WL2KReports;

int WL2KTimer = 0;

int ModetoBaud[31] = {0,0,0,0,0,0,0,0,0,0,0,			// 0 = 10
					  200,600,3200,600,3200,3200,		// 11 - 16
					  0,0,0,0,0,0,0,0,0,0,0,0,0,600};	// 17 - 30

extern char HFCTEXT[];
extern int HFCTEXTLEN;


extern char WL2KCall[10];
extern char WL2KLoc[7];


VOID MoveWindows(struct TNCINFO * TNC)
{
#ifndef LINBPQ
	RECT rcClient;
	int ClientHeight, ClientWidth;

	GetClientRect(TNC->hDlg, &rcClient); 

	ClientHeight = rcClient.bottom;
	ClientWidth = rcClient.right;

	if (TNC->hMonitor)
		MoveWindow(TNC->hMonitor,2 , TNC->RigControlRow + 3, ClientWidth-4, ClientHeight - (TNC->RigControlRow + 3), TRUE);
#endif
}

char * Config;
static char * ptr1, * ptr2;

#ifndef LINBPQ

LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	MINMAXINFO * mmi;

	int i;
	struct TNCINFO * TNC;

	HKEY hKey;
	char Key[80];
	int retCode, disp;

	for (i=0; i<41; i++)
	{
		TNC = TNCInfo[i];
		if (TNC == NULL)
			continue;
		
		if (TNC->hDlg == hWnd)
			break;
	}

	if (TNC == NULL)
			return DefMDIChildProc(hWnd, message, wParam, lParam);

	switch (message) { 

	case WM_CREATE:

		break;

	case WM_PAINT:

//			hdc = BeginPaint (hWnd, &ps);
			
//			SelectObject( hdc, hFont) ;
			
//			EndPaint (hWnd, &ps);
//
//			wParam = hdc;
	
			break;        


	case WM_GETMINMAXINFO:

 		if (TNC->ClientHeight)
		{
			mmi = (MINMAXINFO *)lParam;
			mmi->ptMaxSize.x = TNC->ClientWidth;
			mmi->ptMaxSize.y = TNC->ClientHeight;
			mmi->ptMaxTrackSize.x = TNC->ClientWidth;
			mmi->ptMaxTrackSize.y = TNC->ClientHeight;
		}

		break;


	case WM_MDIACTIVATE:
	{
			 
		// Set the system info menu when getting activated
			 
		if (lParam == (LPARAM) hWnd)
		{
			// Activate

			RemoveMenu(hBaseMenu, 1, MF_BYPOSITION);

			if (TNC->hMenu)
				AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions");
			
			SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu);

//			SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu);
		}
		else
		{
			 // Deactivate
	
			SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL);
		 }
			 
		// call DrawMenuBar after the menu items are set
		DrawMenuBar(FrameWnd);

		return DefMDIChildProc(hWnd, message, wParam, lParam);
	}



	case WM_INITMENUPOPUP:

		if (wParam == (WPARAM)TNC->hMenu)
		{
			if (TNC->ProgramPath)
			{
				if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP")
					 || strstr(TNC->ProgramPath, "VARA") || stristr(TNC->ProgramPath, "FREEDATA"))
				{
					EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED);
					EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED);
		
					break;
				}
			}
			EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED);
			EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED);
		}
			
		break;

	case WM_COMMAND:

		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);

		switch (wmId)
		{
		case WINMOR_KILL:

			TNC->DontRestart = TRUE;
			KillTNC(TNC);
			break;

		case WINMOR_RESTART:

			TNC->DontRestart = FALSE;
			KillTNC(TNC);
			RestartTNC(TNC);
			break;

		case WINMOR_RESTARTAFTERFAILURE:

			TNC->RestartAfterFailure = !TNC->RestartAfterFailure;
			CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED);

			sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port);
	
			retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp);

			if (retCode == ERROR_SUCCESS)
			{
				RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4);
				RegCloseKey(hKey);
			}
			break;

		case ARDOP_ABORT:

			if (TNC->ForcedCloseProc)
				TNC->ForcedCloseProc(TNC, 0);

			break;
		}
		return DefMDIChildProc(hWnd, message, wParam, lParam);

	case WM_SIZING:
	case WM_SIZE:

		MoveWindows(TNC);
			
		return DefMDIChildProc(hWnd, message, wParam, lParam);

	case WM_SYSCOMMAND:

		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		
		switch (wmId)
		{ 

		case SC_RESTORE:

			TNC->Minimized = FALSE;
			break;

		case SC_MINIMIZE: 

			TNC->Minimized = TRUE;
			break;
		}
		
		return DefMDIChildProc(hWnd, message, wParam, lParam);

	case WM_CTLCOLORDLG:
		return (LONG)bgBrush;

		 
	case WM_CTLCOLORSTATIC:
	{
			HDC hdcStatic = (HDC)wParam;
			SetTextColor(hdcStatic, RGB(0, 0, 0));
			SetBkMode(hdcStatic, TRANSPARENT);
			return (LONG)bgBrush;
	}

	case WM_HSCROLL:
	{ 
		char value[16];

		switch (LOWORD(wParam))
		{     
		case TB_ENDTRACK:
		case TB_THUMBTRACK:

			TNC->TXOffset = SendMessage(TNC->xIDC_TXTUNE, TBM_GETPOS, 0, 0); 
			sprintf(value, "%d", TNC->TXOffset);
			MySetWindowText(TNC->xIDC_TXTUNEVAL, value);

            break;
		}

		default: 
			break; 
	}
	case WM_DESTROY:
		
		break;
	}
	return DefMDIChildProc(hWnd, message, wParam, lParam);
}
#endif

BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, int Width, int Height,
						VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream))
{
#ifdef LINBPQ
	return FALSE;
#else
    WNDCLASS  wc;
	char Title[80];
	int retCode, Type, Vallen;
	HKEY hKey=0;
	char Key[80];
	char Size[80];
	int Top, Left;
	HANDLE hDlg = 0;
	static int LP = 1235;

	if (TNC->hDlg)
	{
		ShowWindow(TNC->hDlg, SW_SHOWNORMAL);
		SetForegroundWindow(TNC->hDlg);
		return FALSE;							// Already open
	}

	wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE;
    wc.lpfnWndProc = WndProc;                                      
    wc.cbClsExtra = 0;                
    wc.cbWndExtra = DLGWINDOWEXTRA;
	wc.hInstance = hInstance;
    wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) );
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = bgBrush; 
	wc.lpszMenuName = NULL;	
	wc.lpszClassName = ClassName; 

	RegisterClass(&wc);

//	if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_TELNET ||TNC->Hardware == H_ARDOP ||
//			TNC->Hardware == H_V4 || TNC->Hardware == H_FLDIGI || TNC->Hardware == H_UIARQ || TNC->Hardware == H_VARA)
	if (TNC->PortRecord)
		sprintf(Title, "%s Status - Port %d %s", WindowTitle, TNC->Port, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION);
	else
		sprintf(Title, "Rigcontrol");
	
	if (TNC->Hardware == H_MPSK)
		sprintf(Title, "Rigcontrol for MultiPSK Port %d", TNC->Port);

	TNC->hDlg = hDlg =  CreateMDIWindow(ClassName, Title, 0,
		  0, 0, Width, Height, ClientWnd, hInstance, ++LP);
	
	//	CreateDialog(hInstance,ClassName,0,NULL);
	
	Rect.top = 100;
	Rect.left = 20;
	Rect.right = Width + 20;
	Rect.bottom = Height + 100;


	sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port);
	
	retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey);

	if (retCode == ERROR_SUCCESS)
	{
		Vallen=80;

		retCode = RegQueryValueEx(hKey,"Size",0,			
			(ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen);

		if (retCode == ERROR_SUCCESS)
		{
			sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &TNC->Minimized);

			if (Rect.top < - 500 || Rect.left < - 500)
			{
				Rect.left = 0;
				Rect.top = 0;
				Rect.right = 600;
				Rect.bottom = 400;
			}

			if (Rect.top < OffsetH)
			{
				int Error = OffsetH - Rect.top;
				Rect.top += Error;
				Rect.bottom += Error;
			}
		}

		if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_ARDOP|| TNC->Hardware == H_VARA)	
			retCode = RegQueryValueEx(hKey,"TNC->RestartAfterFailure",0,			
				(ULONG *)&Type,(UCHAR *)&TNC->RestartAfterFailure,(ULONG *)&Vallen);

		RegCloseKey(hKey);
	}

	Top = Rect.top;
	Left = Rect.left;

//	GetWindowRect(hDlg, &Rect);	// Get the real size

	MoveWindow(hDlg, Left - (OffsetW /2), Top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, TRUE);
	
	if (TNC->Minimized)
		ShowWindow(hDlg, SW_SHOWMINIMIZED);
	else
		ShowWindow(hDlg, SW_RESTORE);

	TNC->RigControlRow = RigControlRow;

	SetWindowText(TNC->xIDC_TNCSTATE, "Free");

	TNC->ForcedCloseProc = ForcedCloseProc;

	return TRUE;
#endif
}


// WL2K Reporting Code.

static SOCKADDR_IN sinx; 


VOID SendReporttoWL2KThread(void * unused);
VOID SendHTTPReporttoWL2KThread(void * unused);

VOID CheckWL2KReportTimer()
{
	if (WL2KReports == NULL)
		return;					// Shouldn't happen!

	WL2KTimer--;

	if (WL2KTimer != 0)
		return;

#ifdef WIN32
	WL2KTimer = 2 * 32910;			// Every 2 Hours - PC Tick is a bit slow 
#else
	WL2KTimer = 2 * 36000;			// Every 2 Hours 
#endif

	if (CheckAppl(NULL, "RMS         ") == NULL)
		if (CheckAppl(NULL, "RELAY       ") == NULL)
			return;

	_beginthread(SendHTTPReporttoWL2KThread, 0, 0);

	return;
}

static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n"
	"Accept: application/json\r\n"
//	"Accept-Encoding: gzip,deflate,gzip, deflate\r\n"
	"Content-Type: application/json\r\n"
	"Host: %s:%d\r\n"
	"Content-Length: %d\r\n"
	//r\nUser-Agent: BPQ32(G8BPQ)\r\n"
//	"Expect: 100-continue\r\n"
	"\r\n{%s}";

char Missing[] = "** Missing **";

VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value)
{
	char * ptr1, * ptr2;

	strcpy(Value, Missing);

	ptr1 = strstr(_REPLYBUFFER, Name);

	if (ptr1 == 0)
		return;

	ptr1 += (strlen(Name) + 1);

	ptr2 = strchr(ptr1, '"');
			
	if (ptr2)
	{
		size_t ValLen = ptr2 - ptr1;
		memcpy(Value, ptr1, ValLen);
		Value[ValLen] = 0;
	}

	return;
}


// Send Winlink Session Record

extern char LOC[7];
extern char TextVerstring[50];

double Distance(double laa, double loa, double lah, double loh, BOOL KM);
double Bearing(double lat2, double lon2, double lat1, double lon1);
VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return);
SOCKET OpenWL2KHTTPSock();



struct WL2KMode
{
	int Mode;
	char * WL2KString;
	char * ADIFString;
	char * BPQString;
};

struct WL2KMode WL2KModeList[] =
{
	{0,"Packet 1200"},
	{1,"Packet 2400"},
	{2, "Packet 4800"},
	{3, "Packet 9600"},
	{4, "Packet 19200"},
	{5, "Packet 38400"},
	{11, "Pactor 1"},
	{12, "Pactor 1,2"},
	{13, "Pactor 1,2,3"},
	{14, "Pactor 2"},
	{15, "Pactor 2,3"},
	{16, "Pactor 3"},
	{17, "Pactor 1,2,3,4"},
	{18, "Pactor 2,3,4"},
	{19, "Pactor 3,4"},
	{20, "Pactor 4"},
	{21, "WINMOR 500"},
	{22, "WINMOR 1600"},
	{30, "Robust Packet"},
	{40, "ARDOP 200"},
	{41, "ARDOP 500"},
	{42, "ARDOP 1000"},
	{43, "ARDOP 2000"},
	{44, "ARDOP 2000 FM"},
	{50, "VARA"},
	{51, "VARA FM"},
	{52, "VARA FM WIDE"},
	{53, "VARA 500"}
};

char WL2KModes [55][18] = {
	"Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "",
	"Pactor 1", "Pactor", "Pactor", "Pactor 2", "Pactor", "Pactor 3", "Pactor", "Pactor", "Pactor", "Pactor 4", // 11 - 20
	"Winmor 500", "Winmor 1600", "", "", "", "", "", "", "",				// 21 - 29
	"Robust Packet", "", "", "", "", "", "", "", "", "",					// 30 - 39
	"ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "",	// 40 - 49
	"VARA", "VARA FM", "VARA FM WIDE", "VARA 500", "VARA 2750"};


VOID SendWL2KSessionRecordThread(void * param)
{
	SOCKET sock;
	char Message[512];

	strcpy(Message, param);
	free(param);

	Debugprintf("Sending %s", Message);

	sock = OpenWL2KHTTPSock();

	if (sock)
	{
		SendHTTPRequest(sock, "/session/add", (char *)Message, (int)strlen(Message), NULL);
		closesocket(sock);
	}

	return;
}

VOID SendWL2KRegisterHybridThread(void * param)
{
	SOCKET sock;
	char Message[512];

	strcpy(Message, param);
	free(param);

	Debugprintf("Sending %s", Message);

	sock = OpenWL2KHTTPSock();

	if (sock)
	{
		SendHTTPRequest(sock, "/radioNetwork/params/add", (char *)Message, (int)strlen(Message), NULL);
		closesocket(sock);
	}

	return;
}

VOID SendWL2KRegisterHybrid(struct TNCINFO * TNC)
{
	char Message[512];
	char Date[80] ;
	int Len;
	struct TCPINFO * TCP = TNC->TCPInfo;
	time_t T;
	struct tm * tm;
	char Call[10];

	if (TCP == NULL || TCP->GatewayLoc[0] == 0)
		return;

	strcpy(Call, TCP->GatewayCall);
	strlop(Call, '-');

	T = time(NULL);
	tm = gmtime(&T);

	//2021-10-31-14=35=29

	sprintf(Date, "%04d-%02d-%02d-%02d:%02d:%02d",
		tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
		tm->tm_hour, tm->tm_min, tm->tm_sec);
	
// "Callsign":"String","Password":"String","Param":"String","Value":"String","Key":"String"

	Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"RMSRelayVersion\",\"Value\":\"%s|%s|*HARMNNNN|%s|%s|\"",
		Call, TCP->SecureCMSPassword, Date, "3.1.11.2",
		TCP->HybridServiceCode, TCP->GatewayLoc);

	SendWL2KRegisterHybridThread(_strdup(Message));

	Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"CoLocatedRMS\",\"Value\":\"%s\"",
		Call, TCP->SecureCMSPassword, TCP->HybridCoLocatedRMS);

	SendWL2KRegisterHybridThread(_strdup(Message));

	Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"AllowFreq\",\"Value\":\"%s\"",
		Call, TCP->SecureCMSPassword, TCP->HybridFrequencies);

	SendWL2KRegisterHybridThread(_strdup(Message));

	return;
}

BOOL NoSessionAccount = FALSE;
BOOL SessionAccountChecked = FALSE;

BOOL SendWL2KSessionRecord(ADIF * ADIF, int BytesSent, int BytesReceived)
{
/*
The API is /session/add https://api.winlink.org/json/metadata?op=SessionAdd

The important parameters are (others can be omitted):

Application (gateway program name)
Server (gateway callsign)
ServerGrid
Client (client callsign)
ClientGrid
Mode (Pactor, winmor, vara, etc)
Frequency
MessagesSent
MessagesReceived
BytesSent 
BytesReceived 
HoldingSeconds (duration of connection)
IdTag (random alphanumeric, 12 chars)

"Application":"RMS Trimode",
"Version":"1.3.25.0",
"Cms":"CMS-A",
"Server":"AB4NX",
"ServerGrid":"EM73WT",
"Client":"VE2SCA","ClientGrid":"",
"Sid":"","Mode":"WINMOR16",
"Frequency":10145000,
"Kilometers":0,
"Degrees":0,
"LastCommand":"&gt;",
"MessagesSent":0,
"MessagesReceived":0,
"BytesSent":179,
"BytesReceived":0,
"HoldingSeconds":126,
"IdTag":"ATK9S3QGL2E1"}
*/
	time_t T;

	char Message[4096] = "";
	char * MessagePtr;
	int MessageLen;
	int Dist = 0;
	int intBearing = 0;

	double Lat, Lon;
	double myLat, myLon;

	char Tag[32];

	SOCKET sock;
	char Response[1024];
	int Len;

	// Only report if the CMSCall has a WL2KAccount

	if (NoSessionAccount)
		return TRUE;

	if (!SessionAccountChecked)
	{
		// only check once

		sock = OpenWL2KHTTPSock();

		if (sock)
		{
			SessionAccountChecked = TRUE;

			Len = sprintf(Message, "\"Callsign\":\"%s\"", ADIF->CMSCall);

			SendHTTPRequest(sock, "/account/exists", Message, Len, Response);
			closesocket(sock);

			if (strstr(Response, "\"CallsignExists\":false"))
			{
				WritetoConsole("WL2K Traffic Reporting disabled - Gateway ");
				WritetoConsole(ADIF->CMSCall);
				WritetoConsole(" does not have a Winlink Account\r\n");
				Debugprintf("WL2K Traffic Reporting disabled - Gateway %s does not have a Winlink Account", ADIF->CMSCall);
				NoSessionAccount = TRUE;
				return TRUE;
			}
		}
	}

	if (ADIF == NULL || ADIF->LOC[0] == 0 || ADIF->Call[0] == 0)
		return TRUE;

	if (ADIF->StartTime == 0 || ADIF->ServerSID[0] == 0 || ADIF->CMSCall[0] == 0)
		return TRUE;

	T = time(NULL);

	// Extract Info we need

	// Distance and Bearing

	if (LOC[0] && ADIF->LOC[0])
	{
		if (FromLOC(LOC, &myLat, &myLon) == 0)  	// Basic checks on LOCs
			return TRUE;
		if (FromLOC(ADIF->LOC, &Lat, &Lon) == 0)
			return TRUE;

		Dist = (int)Distance(myLat, myLon, Lat, Lon, TRUE);
		intBearing = (int)Bearing(Lat, Lon, myLat, myLon);
	}

	MessageLen = sprintf(Message, "\"Application\":\"%s\",", "BPQ32");
	MessageLen += sprintf(&Message[MessageLen], "\"Version\":\"%s\",", TextVerstring);
	MessageLen += sprintf(&Message[MessageLen], "\"Cms\":\"%s\",", "CMS");
	MessageLen += sprintf(&Message[MessageLen], "\"Server\":\"%s\",", ADIF->CMSCall);
	MessageLen += sprintf(&Message[MessageLen], "\"ServerGrid\":\"%s\",", LOC);
	MessageLen += sprintf(&Message[MessageLen], "\"Client\":\"%s\",", ADIF->Call);
	MessageLen += sprintf(&Message[MessageLen], "\"ClientGrid\":\"%s\",", ADIF->LOC);
	MessageLen += sprintf(&Message[MessageLen], "\"Sid\":\"%s\",", ADIF->UserSID);
	MessageLen += sprintf(&Message[MessageLen], "\"Mode\":\"%s\",", WL2KModes[ADIF->Mode]);
	MessageLen += sprintf(&Message[MessageLen], "\"Frequency\":%lld,", ADIF->Freq);
	MessageLen += sprintf(&Message[MessageLen], "\"Kilometers\":%d,", Dist);
	MessageLen += sprintf(&Message[MessageLen], "\"Degrees\":%d,", intBearing);
	MessageLen += sprintf(&Message[MessageLen], "\"LastCommand\":\"%s\",", ADIF->Termination);
	MessageLen += sprintf(&Message[MessageLen], "\"MessagesSent\":%d,", ADIF->Sent);
	MessageLen += sprintf(&Message[MessageLen], "\"MessagesReceived\":%d,", ADIF->Received);
	MessageLen += sprintf(&Message[MessageLen], "\"BytesSent\":%d,", BytesSent);
	MessageLen += sprintf(&Message[MessageLen], "\"BytesReceived\":%d,", BytesReceived);
	MessageLen += sprintf(&Message[MessageLen], "\"HoldingSeconds\":%d,", (int)(T - ADIF->StartTime));
	sprintf(Tag, "%012X", (int)T * (rand() + 1));
	MessageLen += sprintf(&Message[MessageLen], "\"IdTag\":\"%s\"", Tag);

	MessagePtr = _strdup(Message);
	_beginthread(SendWL2KSessionRecordThread, 0, (void *)MessagePtr);

	return TRUE;
}

char APIKey[] = ",\"Key\":\"0D0C7AD6B38C45A7A9534E67111C38A7\"";


VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return)
{
	int InputLen = 0;
	int inptr = 0;
	char Buffer[2048];
	char Header[2048];
	char * ptr, * ptr1;
	int Sent;

	strcat(Params, APIKey);
	Len += (int)strlen(APIKey);

	sprintf(Header, HeaderTemplate, Request, "api.winlink.org", 80, Len + 2, Params);
	Sent = send(sock, Header, (int)strlen(Header), 0);

	if (Sent == -1)
	{
		int Err = WSAGetLastError();
		Debugprintf("Error %d from WL2K Update send()", Err);
		return;
	}

	while (InputLen != -1)
	{
		InputLen = recv(sock, &Buffer[inptr], 2048 - inptr, 0);

		if (InputLen == -1 || InputLen == 0)
		{
			int Err = WSAGetLastError();
			Debugprintf("Error %d from WL2K Update recv()", Err);
			return;
		}

		//	As we are using a persistant connection, can't look for close. Check
		//	for complete message

		inptr += InputLen;

		Buffer[inptr] = 0;
		
		ptr = strstr(Buffer, "\r\n\r\n");

		if (ptr)
		{
			// got header

			int Hddrlen = (int)(ptr - Buffer);
					
			ptr1 = strstr(Buffer, "Content-Length:");

			if (ptr1)
			{
				// Have content length

				int ContentLen = atoi(ptr1 + 16);

				if (ContentLen + Hddrlen + 4 == inptr)
				{
					// got whole response

					if (strstr(Buffer, " 200 OK"))
					{
						if (Return)
						{
							memcpy(Return, ptr + 4, ContentLen); 
							Return[ContentLen] = 0;
						}
						else
							Debugprintf("WL2K Database update ok");
					
					}
					else
					{
						strlop(Buffer, 13);
						Debugprintf("WL2K Update Params - %s", Params);
						Debugprintf("WL2K Update failed - %s", Buffer);
					}
					return;
				}
			}
			else
			{
				ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:");
				
				if (ptr1)
				{
					// Just accept anything until I've sorted things with Lee
					Debugprintf("%s", ptr1);
					Debugprintf("WL2K Database update ok");
					return;
				}
			}
		}
	}
}

BOOL WL2KAccountChecked = FALSE;
BOOL NoWL2KAccount = FALSE;

VOID SendHTTPReporttoWL2KThread(void * unused)
{
	// Uses HTTP/JSON Interface

	struct WL2KInfo * WL2KReport = WL2KReports;
	char * LastHost = NULL;
	char * LastRMSCall = NULL;
	char Message[512];
	int LastSocket = 0;
	SOCKET sock = 0;
	struct sockaddr_in destaddr;
	int addrlen=sizeof(sinx);
	struct hostent * HostEnt;
	int err;
	u_long param=1;
	BOOL bcopt=TRUE;
	int Len;

	// Send all reports in list

	char Response[1024];

	// Only report if the CMSCall has a WL2KAccount

	if (NoWL2KAccount)
		return;

	if (!WL2KAccountChecked)
	{
		// only check once

		sock = OpenWL2KHTTPSock();

		if (sock)
		{
			WL2KAccountChecked = TRUE;

			Len = sprintf(Message, "\"Callsign\":\"%s\"",
				WL2KReport->BaseCall);

			SendHTTPRequest(sock, "/account/exists", Message, Len, Response);
			closesocket(sock);

			if (strstr(Response, "\"CallsignExists\":false"))
			{
				WritetoConsole("WL2K Reporting disabled - Gateway ");
				WritetoConsole(WL2KReport->BaseCall);
				WritetoConsole(" does not have a Winlink Account\r\n");
				NoWL2KAccount = TRUE;
				return;
			}
		}
	}

	while (WL2KReport)
	{
		// Resolve Name if needed

		if (LastHost && strcmp(LastHost, WL2KReport->Host) == 0)		// Same host?
			goto SameHost;

		// New Host - Connect to it
	
		LastHost = WL2KReport->Host;
	
		destaddr.sin_family = AF_INET; 
		destaddr.sin_addr.s_addr = inet_addr(WL2KReport->Host);
		destaddr.sin_port = htons(WL2KReport->WL2KPort);

		if (destaddr.sin_addr.s_addr == INADDR_NONE)
		{
			//	Resolve name to address

			Debugprintf("Resolving %s", WL2KReport->Host);
			HostEnt = gethostbyname (WL2KReport->Host);
		 
			if (!HostEnt)
			{
				err = WSAGetLastError();

				Debugprintf("Resolve Failed for %s %d %x", WL2KReport->Host, err, err);
				return;			// Resolve failed
			}
	
			memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);	
		}

		//   Allocate a Socket entry

		if (sock)
			closesocket(sock);

		sock = socket(AF_INET, SOCK_STREAM, 0);

		if (sock == INVALID_SOCKET)
  	 		return; 

//		ioctlsocket(sock, FIONBIO, &param);
 
		setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt, 4);

		destaddr.sin_family = AF_INET;

		if (sock == INVALID_SOCKET)
		{
			sock = 0;
			return; 
		}

		setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);

		// Connect to Host

		if (connect(sock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) != 0)
		{
			err=WSAGetLastError();

			//
			//	Connect failed
			//

			Debugprintf("Connect Failed");
			closesocket(sock);
			sock = 0;
			break;
		}

	SameHost:

		Len = sprintf(Message,
				"\"Callsign\":\"%s\","
				"\"BaseCallsign\":\"%s\","
				"\"GridSquare\":\"%s\","
				"\"Frequency\":%lld,"
				"\"Mode\":%d,"
				"\"Baud\":%d,"
				"\"Power\":%d,"
				"\"Height\":%d,"
				"\"Gain\":%d,"
				"\"Direction\":%d,"
				"\"Hours\":\"%s\","
				"\"ServiceCode\":\"%s\"",

				WL2KReport->RMSCall, WL2KReport->BaseCall, WL2KReport->GridSquare,
				WL2KReport->Freq, WL2KReport->mode, WL2KReport->baud, WL2KReport->power,
				WL2KReport->height, WL2KReport->gain, WL2KReport->direction,
				WL2KReport->Times, WL2KReport->ServiceCode);

		Debugprintf("Sending %s", Message);

		SendHTTPRequest(sock, "/channel/add", Message, Len, NULL);
	
		
		//	Send Version Message


		if (LastRMSCall == NULL || strcmp(WL2KReport->RMSCall, LastRMSCall) != 0)
		{
			int Len;
			
			LastRMSCall = WL2KReport->RMSCall;

	//	"Callsign":"String","Program":"String","Version":"String","Comments":"String"
		
			Len = sprintf(Message, "\"Callsign\":\"%s\",\"Program\":\"BPQ32\","
				"\"Version\":\"%d.%d.%d.%d\",\"Comments\":\"Test Comment\"",
				WL2KReport->RMSCall, Ver[0], Ver[1], Ver[2], Ver[3]);

			Debugprintf("Sending %s", Message);

			SendHTTPRequest(sock, "/version/add", Message, Len, NULL);
		}

		WL2KReport = WL2KReport->Next;
	}

	Sleep(100);
	closesocket(sock);
	sock = 0;

}

struct WL2KInfo * DecodeWL2KReportLine(char *  buf)
{
	//06'<callsign>', '<base callsign>', '<grid square>', <frequency>, <mode>, <baud>, <power>,
	// <antenna height>, <antenna gain>, <antenna direction>, '<hours>', <group reference>, '<service code>'

	 // WL2KREPORT  service, api.winlink.org, 80, GM8BPQ, IO68VL, 00-23, 144800000, PKT1200, 10, 20, 5, 0, BPQTEST
	
	char * Context;
	char * p_cmd;
	char * param;
	char errbuf[256];
	struct WL2KInfo * WL2KReport = zalloc(sizeof(struct WL2KInfo));
	char * ptr;
	char Param[8][256];
	char * ptr1, * ptr2;
	int n = 0;

	memset(Param, 0, 2048);

	strcpy(errbuf, buf); 

	p_cmd = strtok_s(&buf[10], ", \t\n\r", &Context);
	if (p_cmd == NULL) goto BadLine;
	
	strcpy(WL2KReport->ServiceCode, p_cmd);

	// Can default Host and Port, so cant use strtok for them
	
	ptr1 = Context;

	while (ptr1 && *ptr1 && n < 2)
	{
		while(ptr1 && *ptr1 && *ptr1 == ' ')
			ptr1++;	
		
		ptr2 = strchr(ptr1, ',');
		if (ptr2) *ptr2++ = 0;

		strcpy(&Param[n][0], ptr1);
		strlop(Param[n++], ' ');
		ptr1 = ptr2;

	}

	if (n < 2)
		goto BadLine;

	if (Param[1][0] == 0)
		WL2KReport->WL2KPort = 80;			// HTTP Interface
	else
		WL2KReport->WL2KPort = atoi(&Param[1][0]);

	if (Param[0][0] == 0)
		WL2KReport->Host = _strdup("api.winlink.org");
	else
	{
		_strlwr(&Param[0][0]);

		if (strstr(&Param[0][0], "winlink.org"))
		{
			WL2KReport->WL2KPort = 80;		// HTTP Interface
			WL2KReport->Host = _strdup("api.winlink.org");
		}
		else
			WL2KReport->Host = _strdup(&Param[0][0]);
	}

	Context = ptr1;

	p_cmd = strtok_s(NULL, ", \t\n\r", &Context);
	if (p_cmd == NULL) goto BadLine;

	if (WL2KReport->WL2KPort == 0) goto BadLine;

	strcpy(WL2KReport->RMSCall, p_cmd);
	strcpy(WL2KReport->BaseCall, p_cmd);
	strlop(WL2KReport->BaseCall, '-');					// Remove any SSID
	
	strcpy(WL2KCall, WL2KReport->BaseCall);				// For SYSOP Update

	p_cmd = strtok_s(NULL, " ,\t\n\r", &Context);		
	if (p_cmd == NULL) goto BadLine;
	if (strlen(p_cmd) != 6) goto BadLine;

	strcpy(WL2KReport->GridSquare, p_cmd);
	strcpy(WL2KLoc, p_cmd);

	p_cmd = strtok_s(NULL, " ,\t\n\r", &Context);
	if (p_cmd == NULL) goto BadLine;
	if (strlen(p_cmd) > 79) goto BadLine;
	
	// Convert any : in times to comma

	ptr = strchr(p_cmd, ':');

	while (ptr)
	{
		*ptr = ',';
		ptr = strchr(p_cmd, ':');
	}

	strcpy(WL2KReport->Times, p_cmd);

	p_cmd = strtok_s(NULL, " ,\t\n\r", &Context);
	if (p_cmd == NULL) goto BadLine;

	WL2KReport->Freq = strtoll(p_cmd, NULL, 10);

	if (WL2KReport->Freq == 0)	// Invalid
		goto BadLine;					

	param = strtok_s(NULL, " ,\t\n\r", &Context);

	// Mode Designator - one of

	// PKTnnnnnn
	// WINMOR500
	// WINMOR1600
	// ROBUST
	// P1 P12 P123 P1234 etc

	if (memcmp(param, "PKT", 3) == 0)
	{
		int Speed, Mode;

		Speed = atoi(&param[3]);

		 WL2KReport->baud = Speed;
			
		 if (Speed <= 1200)
			 Mode = 0;					// 1200
		 else if (Speed <= 2400)
			 Mode = 1;					// 2400
		 else if (Speed <= 4800)
			 Mode = 2;					// 4800
		 else if (Speed <= 9600)
			 Mode = 3;					// 9600
		 else if (Speed <= 19200)
			 Mode = 4;					// 19200
		 else if (Speed <= 38400)
			 Mode = 5;					// 38400
		 else
			 Mode = 6;					// >38400

		WL2KReport->mode = Mode;
	}
	else if (_stricmp(param, "WINMOR500") == 0)
		WL2KReport->mode = 21;
	else if (_stricmp(param, "WINMOR1600") == 0)
		WL2KReport->mode = 22;
	else if (_stricmp(param, "ROBUST") == 0)
	{
		WL2KReport->mode = 30;
		WL2KReport->baud = 600;
	}
	else if (_stricmp(param, "ARDOP200") == 0)
		WL2KReport->mode = 40;
	else if (_stricmp(param, "ARDOP500") == 0)
		WL2KReport->mode = 41;
	else if (_stricmp(param, "ARDOP1000") == 0)
		WL2KReport->mode = 42;
	else if (_stricmp(param, "ARDOP2000") == 0)
		WL2KReport->mode = 43;
	else if (_stricmp(param, "ARDOP2000FM") == 0)
		WL2KReport->mode = 44;
	else if (_stricmp(param, "P1") == 0)
		WL2KReport->mode = 11;
	else if (_stricmp(param, "P12") == 0)
		WL2KReport->mode = 12;
	else if (_stricmp(param, "P123") == 0)
		WL2KReport->mode = 13;
	else if (_stricmp(param, "P2") == 0)
		WL2KReport->mode = 14;
	else if (_stricmp(param, "P23") == 0)
		WL2KReport->mode = 15;
	else if (_stricmp(param, "P3") == 0)
		WL2KReport->mode = 16;
	else if (_stricmp(param, "P1234") == 0)
		WL2KReport->mode = 17;
	else if (_stricmp(param, "P234") == 0)
		WL2KReport->mode = 18;
	else if (_stricmp(param, "P34") == 0)
		WL2KReport->mode = 19;
	else if (_stricmp(param, "P4") == 0)
		WL2KReport->mode = 20;
	else if (_stricmp(param, "VARA") == 0)
		WL2KReport->mode = 50;
	else if (_stricmp(param, "VARA2300") == 0)
		WL2KReport->mode = 50;
	else if (_stricmp(param, "VARAFM") == 0)
		WL2KReport->mode = 51;
	else if (_stricmp(param, "VARAFM12") == 0)
		WL2KReport->mode = 51;
	else if (_stricmp(param, "VARAFM96") == 0)
		WL2KReport->mode = 52;
	else if (_stricmp(param, "VARA500") == 0)
		WL2KReport->mode = 53;
	else if (_stricmp(param, "VARA2750") == 0)
		WL2KReport->mode = 54;
	else
		goto BadLine;
	
	param = strtok_s(NULL, " ,\t\n\r", &Context);

	// Optional Params

	WL2KReport->power = (param)? atoi(param) : 0;
	param = strtok_s(NULL, " ,\t\n\r", &Context);
	WL2KReport->height = (param)? atoi(param) : 0;
	param = strtok_s(NULL, " ,\t\n\r", &Context);
	WL2KReport->gain = (param)? atoi(param) : 0;
	param = strtok_s(NULL, " ,\t\n\r", &Context);
	WL2KReport->direction = (param)? atoi(param) : 0;

	WL2KTimer = 60;

	WL2KReport->Next = WL2KReports;
	WL2KReports = WL2KReport;

	return WL2KReport;

BadLine:

	WritetoConsole(" Bad config record ");
	WritetoConsole(errbuf);
	WritetoConsole("\r\n");

	return 0;
}

VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis);

VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction)
{
	UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, TRUE);
}
VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report)
{
	UpdateMHSupport(TNC, Call, Mode, Direction, LOC, Report, FALSE);
}

VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction)
{
	UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, FALSE);
}

VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis)
{
	PMHSTRUC MH = TNC->PortRecord->PORTCONTROL.PORTMHEARD;
	PMHSTRUC MHBASE = MH;
	UCHAR AXCall[72] = "";
	int i;
	char * LOC, *  LOCEND;
	char ReportMode[20];
	char NoLOC[7] = "";
	double Freq;
	char ReportFreq[350] = "";
	int OldCount = 0;
	char ReportCall[16];

	if (MH == 0) return;

	if (Digis)
	{
		// Call is an ax.25 digi string not a text call

		memcpy(AXCall, Call, 7 * 9);
		ReportCall[ConvFromAX25(Call, ReportCall)] = 0;

		// if this is a UI frame with a locator or APRS position
		// we could derive a position from it

	}
	else
	{
		strcpy(ReportCall, Call);
		ConvToAX25(Call, AXCall);
		AXCall[6] |= 1;					// Set End of address
	}

	// Adjust freq to centre

//	if (Mode != ' ' && TNC->RIG->Valchar[0])
	if (TNC->RIG->Valchar[0])
	{
		if (TNC->Hardware == H_UZ7HO)	
		{
			// See if we have Center Freq Info
			if (TNC->AGWInfo->CenterFreq)
			{
				Freq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0);
			}
#ifdef WIN32
			else if (TNC->AGWInfo->hFreq)
			{
				char Centre[16];
				double ModemFreq;

				SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre);

				ModemFreq = atof(Centre);

				Freq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000);
			}
#endif	
			else
				Freq = atof(TNC->RIG->Valchar) + 0.0015;		// Assume 1500
		}
		else

			// Not UZ7HO or Linux
		
			Freq = atof(TNC->RIG->Valchar) + 0.0015;

		_gcvt(Freq, 9, ReportFreq);
	}

	if (TNC->Hardware == H_ARDOP)	
	{
		LOC = memchr(Call, '[', 20);

		if (LOC)
		{
			LOCEND = memchr(Call, ']', 30);
			if (LOCEND)
			{
				LOC--;
				*(LOC++) = 0;
				*(LOCEND) = 0;
				LOC++;
				if (strlen(LOC) != 6 && strlen(LOC) != 0)
				{
					Debugprintf("Corrupt LOC %s %s", Call, LOC);
					LOC = NoLOC;
				}
				goto NOLOC;
			}		
		}
	}

	else if (TNC->Hardware != H_WINMOR)			// Only WINMOR has a locator
	{
		LOC = NoLOC;
		goto NOLOC;
	}

 	LOC = memchr(Call, '(', 20);

	if (LOC)
	{
		LOCEND = memchr(Call, ')', 30);
		if (LOCEND)
		{
			LOC--;
			*(LOC++) = 0;
			*(LOCEND) = 0;
			LOC++;
			if (strlen(LOC) != 6 && strlen(LOC) != 0)
			{
				Debugprintf("Corrupt LOC %s %s", Call, LOC);
				LOC = NoLOC;
			}
		}		
	}
	else
		LOC = NoLOC;

NOLOC:

	if (Loc)
		LOC = Loc;		// Supplied Locator overrides

	for (i = 0; i < MHENTRIES; i++)
	{
		if (Mode == ' ' || Mode == '*')			// Packet
		{
			if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && MH->MHDIGI == Mode)) // Spare or our entry
			{
				OldCount = MH->MHCOUNT;
				goto DoMove;
			}
		}
		else
		{
			if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) &&
				MH->MHDIGI == Mode && strcmp(MH->MHFreq, ReportFreq) == 0)) // Spare or our entry
			{
				OldCount = MH->MHCOUNT;
				goto DoMove;
			}
		}
		MH++;
	}

	//	TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP

	i = MHENTRIES - 1;
		
	// Move others down and add at front
DoMove:

	if (i != 0)				// First
		memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC));

//	memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9);	
	memcpy (MHBASE->MHCALL, AXCall, 7 * 9);	// Save Digis
	MHBASE->MHDIGI = Mode;
	MHBASE->MHTIME = time(NULL);
	MHBASE->MHCOUNT = ++OldCount;

	memcpy(MHBASE->MHLocator, LOC, 6);
	strcpy(MHBASE->MHFreq, ReportFreq);

	// Report to NodeMap

	if (Report == FALSE)
		return;

	if (Mode == '*')
		return;							// Digi'ed Packet
	
	if (Mode == ' ') 					// Packet Data
	{
		if (TNC->PktUpdateMap == 1)
			Mode = '!';
		else	
			return;
	}
			
	ReportMode[0] = TNC->Hardware + '@';
	ReportMode[1] = Mode;
	if (TNC->Hardware == H_HAL)
		ReportMode[2] = TNC->CurrentMode; 
	else
		ReportMode[2] = (TNC->RIG->CurrentBandWidth) ? TNC->RIG->CurrentBandWidth : '?';
	ReportMode[3] = Direction;
	ReportMode[4] = 0;

	// If no position see if we have an APRS posn

	if (LOC[0] == 0)
	{
		double Lat, Lon;

		if (GetPosnFromAPRS(ReportCall, &Lat, &Lon) && Lat != 0.0)
		{
			ToLOC(Lat, Lon, LOC);
		}
	}

 	SendMH(TNC, ReportCall, ReportFreq, LOC, ReportMode);

	return;
}

VOID CloseDriverWindow(int port)
{
#ifndef LINBPQ

	struct TNCINFO * TNC;

	TNC = TNCInfo[port];
	if (TNC == NULL)
		return;

	if (TNC->hDlg == NULL)
		return;

	PostMessage(TNC->hDlg, WM_CLOSE,0,0);
//	DestroyWindow(TNC->hDlg);

	TNC->hDlg = NULL;
#endif
	return;
}

VOID SaveWindowPos(int port)
{
#ifndef LINBPQ

	struct TNCINFO * TNC;
	char Key[80];

	TNC = TNCInfo[port];

	if (TNC == NULL)
		return;

	if (TNC->hDlg == NULL)
		return;
	
	sprintf(Key, "PACTOR\\PORT%d", port);

	SaveMDIWindowPos(TNC->hDlg, Key, "Size", TNC->Minimized);

#endif
	return;
}

VOID ShowTraffic(struct TNCINFO * TNC)
{
	char Status[80];

	sprintf(Status, "RX %d TX %d ACKED %d ", 
		TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
#ifndef LINBPQ
	SetDlgItemText(TNC->hDlg, IDC_TRAFFIC, Status);
#endif
}

BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC)
{
	// See if this port, or any interlocked ports are reporting channel busy

	struct TNCINFO * TNC;
	int i;
	int rxInterlock = ThisTNC->RXRadio;
	int txInterlock = ThisTNC->TXRadio;

	if (ThisTNC->Busy)
		return TRUE;				// Our port is busy
		
	if (rxInterlock == 0 && txInterlock == 0)
		return ThisTNC->Busy;		// No Interlock

	for (i=1; i <= MAXBPQPORTS; i++)
	{
		TNC = TNCInfo[i];
	
		if (TNC == NULL)
			continue;

		if (TNC == ThisTNC)
			continue;

		if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio)	// Same Group	
			if (TNC->Busy)
				return TRUE;				// Interlocked port is busy

	}
	return FALSE;					// None Busy
}

char ChallengeResponse[13];

char * GetChallengeResponse(char * Call, char *  ChallengeString)
{
	// Generates a response to the CMS challenge string...

	long long Challenge = _atoi64(ChallengeString);
	long long CallSum = 0;
	long long Mask;
	long long Response;
	long long XX = 1065484730;

	char CallCopy[10];
	UINT i;


	if (Challenge == 0)
		return "000000000000";

// Calculate Mask from Callsign

	memcpy(CallCopy, Call, 10);
	strlop(CallCopy, '-');
	strlop(CallCopy, ' ');

	for (i = 0; i < strlen(CallCopy); i++)
	{
		CallSum += CallCopy[i];
	}
	
	Mask = CallSum + CallSum * 4963 + CallSum * 782386;

	Response = (Challenge % 930249781);
	Response ^= Mask;

	sprintf(ChallengeResponse, "%012lld", Response);

	return ChallengeResponse;
}

SOCKET OpenWL2KHTTPSock()
{
	SOCKET sock = 0;
	struct sockaddr_in destaddr;
	struct sockaddr_in sinx; 
	int addrlen=sizeof(sinx);
	struct hostent * HostEnt;
	int err;
	u_long param=1;
	BOOL bcopt=TRUE;
		
	destaddr.sin_family = AF_INET; 
	destaddr.sin_port = htons(80);

	//	Resolve name to address

	HostEnt = gethostbyname ("api.winlink.org");
		 
	if (!HostEnt)
	{
		err = WSAGetLastError();

		Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err);
		return 0 ;			// Resolve failed
	}
	
	memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);	
	
	//   Allocate a Socket entry

	sock = socket(AF_INET,SOCK_STREAM,0);

	if (sock == INVALID_SOCKET)
  	 	return 0; 
 
	setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);

	sinx.sin_family = AF_INET;
	sinx.sin_addr.s_addr = INADDR_ANY;
	sinx.sin_port = 0;

	if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 )
  	 	return FALSE; 

	if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0)
	{
		err=WSAGetLastError();
		closesocket(sock);
		return 0;
	}

	return sock;
}

BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER)
{
	SOCKET sock = 0;
	int Len;
	char Message[1000];
		
	sock = OpenWL2KHTTPSock();

	if (sock == 0)
		return 0;
	
	// {"Callsign":"String"}
			
	Len = sprintf(Message, "\"Callsign\":\"%s\"", Call);
		
	SendHTTPRequest(sock, "/sysop/get", Message, Len, _REPLYBUFFER);

	closesocket(sock);

	return _REPLYBUFFER[0];
}

BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL)
{
	SOCKET sock = 0;
	struct sockaddr_in destaddr;
	struct sockaddr_in sinx; 
	int len = 100;
	int addrlen=sizeof(sinx);
	struct hostent * HostEnt;
	int err;
	u_long param=1;
	BOOL bcopt=TRUE;
	char Buffer[1000];
	char SendBuffer[1000];
		
	destaddr.sin_family = AF_INET; 
	destaddr.sin_addr.s_addr = inet_addr("api.winlink.org");
	destaddr.sin_port = htons(80);

	HostEnt = gethostbyname ("api.winlink.org");
		 
	if (!HostEnt)
	{
		err = WSAGetLastError();

		Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err);
		return 0 ;			// Resolve failed
	}
	
	memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);	

	//   Allocate a Socket entry

	sock = socket(AF_INET,SOCK_STREAM,0);

	if (sock == INVALID_SOCKET)
  	 	return 0; 
 
	setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);

	sinx.sin_family = AF_INET;
	sinx.sin_addr.s_addr = INADDR_ANY;
	sinx.sin_port = 0;

	if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 )
  	 	return FALSE; 

	if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0)
	{
		err=WSAGetLastError();
		closesocket(sock);
		return 0;
	}

	len = recv(sock, &Buffer[0], len, 0);

	len = sprintf(SendBuffer, "02%07d%-12s%s%s", (int)strlen(SQL), Call, GetChallengeResponse(Call, Buffer), SQL);

	send(sock, SendBuffer, len, 0);

	len = 1000;

	len = recv(sock, &Buffer[0], len, 0);

	Buffer[len] = 0;
	Debugprintf(Buffer);

	closesocket(sock);

	return TRUE;

}
// http://server.winlink.org:8085/csv/reply/ChannelList?Modes=40,41,42,43,44&ServiceCodes=BPQTEST,PUBLIC

// Process config lines that are common to a number of HF modes

static char ** SeparateMultiString(char * MultiString)
{
	char ** Value;
	int Count = 0;
	char * ptr, * ptr1;

	// Convert to string array

	Value = zalloc(sizeof(void *));				// always NULL entry on end even if no values
	Value[0] = NULL;

	strlop(MultiString, 13);
	ptr = MultiString;

	while (ptr && strlen(ptr))
	{
		ptr1 = strchr(ptr, '|');
			
		if (ptr1)
			*(ptr1++) = 0;

		if (strlen(ptr))
		{
			Value = realloc(Value, (Count+2) * sizeof(void *));
			Value[Count++] = _strdup(ptr);
		}
		ptr = ptr1;
	}

	Value[Count] = NULL;
	return Value;
}

extern int nextDummyInterlock;

int standardParams(struct TNCINFO * TNC, char * buf)
{
	if (_memicmp(buf, "WL2KREPORT", 10) == 0)
		TNC->WL2K = DecodeWL2KReportLine(buf);
	else if (_memicmp(buf, "SESSIONTIMELIMIT", 16) == 0)
		TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit = atoi(&buf[17]) * 60;
	else if (_memicmp(buf, "ATTACHTIMELIMIT", 15) == 0)
		TNC->AttachTimeLimit = atoi(&buf[16]) * 60;
	else if (_memicmp(buf, "BUSYHOLD", 8) == 0)		// Hold Time for Busy Detect
		TNC->BusyHold = atoi(&buf[8]);
	else if (_memicmp(buf, "BUSYWAIT", 8) == 0)		// Wait time before failing connect if busy
		TNC->BusyWait = atoi(&buf[8]);
	else if (_memicmp(buf, "AUTOSTARTDELAY", 14) == 0) // Time to wait for TNC to start
		TNC->AutoStartDelay = atoi(&buf[15]);
	else if (_memicmp(buf, "DEFAULTRADIOCOMMAND", 19) == 0)
		TNC->DefaultRadioCmd = _strdup(&buf[20]);
	else if (_memicmp(buf, "MYCALLS", 7) == 0)
	{
		TNC->LISTENCALLS = _strdup(&buf[8]);
		strlop(TNC->LISTENCALLS, '\r');
	}
	else if (_memicmp(buf, "NRNEIGHBOUR", 11) == 0)
		TNC->NRNeighbour = _strdup(&buf[12]);
	else if (_memicmp(buf, "MAXCONREQ", 9) == 0)		// Hold Time for Busy Detect
		TNC->MaxConReq = atoi(&buf[9]);

	else if (_memicmp(buf, "FREQUENCY", 9) == 0)
		TNC->Frequency = _strdup(&buf[10]);
	else if (_memicmp(buf, "SendTandRtoRelay", 16) == 0)
		TNC->SendTandRtoRelay = atoi(&buf[17]);
	else if (_memicmp(buf, "Radio", 5) == 0)		// Rig Control RADIO for TX amd RX (Equiv to INTERLOCK)
		TNC->RXRadio = TNC->TXRadio = atoi(&buf[6]);
	else if (_memicmp(buf, "TXRadio", 7) == 0)		// Rig Control RADIO for TX
		TNC->TXRadio = atoi(&buf[8]);
	else if (_memicmp(buf, "RXRadio", 7) == 0)		// Rig Control RADIO for RXFRETRIES
		TNC->RXRadio = atoi(&buf[8]);
	else if (_memicmp(buf, "TXFreq", 6) == 0)		// For PTT Sets Freq mode
		TNC->TXFreq = strtoll(&buf[7], NULL, 10);
	else if (_memicmp(buf, "DefaultTXFreq", 13) == 0)	// Set at end of session
		TNC->DefaultTXFreq = atof(&buf[14]);
	else if (_memicmp(buf, "DefaultRXFreq", 13) == 0)	// Set at end of session
		TNC->DefaultRXFreq = atof(&buf[14]);
	else if (_memicmp(buf, "ActiveTXFreq", 12) == 0)	// Set at start of session
		TNC->ActiveTXFreq = atof(&buf[13]);
	else if (_memicmp(buf, "ActiveRXFreq", 12) == 0)	// Set at start of session
		TNC->ActiveRXFreq = atof(&buf[13]);
	else if (_memicmp(buf, "DisconnectScript", 16) == 0)	// Set at end of session
		TNC->DisconnectScript = SeparateMultiString(&buf[17]);
	else if (_memicmp(buf, "PTTONHEX", 8) == 0)
	{
		// Hex String to use for PTT on for this port

		char * ptr1 = &buf[9];
		char * ptr2 = TNC->PTTOn;
		int i, j, len;

		_strupr(ptr1);

		TNC->PTTOnLen = len = strlen(ptr1) / 2;

		if (len < 240)
		{
			while ((len--) > 0)
			{
				i = *(ptr1++);
				i -= '0';
				if (i > 9)
					i -= 7;

				j = i << 4;

				i = *(ptr1++);
				i -= '0';
				if (i > 9)
					i -= 7;

				*(ptr2++) = j | i;
			}
		}
	}
	else if (_memicmp(buf, "PTTOFFHEX", 9) == 0)
	{
		// Hex String to use for PTT off

		char * ptr = &buf[10];
		char * ptr2 = TNC->PTTOff;
		int i, j, len;

		_strupr(ptr);

		TNC->PTTOffLen = len = strlen(ptr) / 2;

		if (len < 240)
		{
			while ((len--) > 0)
			{
				i = *(ptr++);
				i -= '0';
				if (i > 9)
					i -= 7;

				j = i << 4;

				i = *(ptr++);
				i -= '0';
				if (i > 9)
					i -= 7;

				*(ptr2++) = j | i;
			}
		}
	}
	else
		return FALSE;

	return TRUE;
}

void DecodePTTString(struct TNCINFO * TNC, char * ptr)
{
	if (_stricmp(ptr, "CI-V") == 0)
		TNC->PTTMode = PTTCI_V;
	else if (_stricmp(ptr, "CAT") == 0)
		TNC->PTTMode = PTTCI_V;
	else if (_stricmp(ptr, "RTS") == 0)
		TNC->PTTMode = PTTRTS;
	else if (_stricmp(ptr, "DTR") == 0)
		TNC->PTTMode = PTTDTR;
	else if (_stricmp(ptr, "DTRRTS") == 0)
		TNC->PTTMode = PTTDTR | PTTRTS;
	else if (_stricmp(ptr, "CM108") == 0)
		TNC->PTTMode = PTTCM108;
	else if (_stricmp(ptr, "HAMLIB") == 0)
		TNC->PTTMode = PTTHAMLIB;
	else if (_stricmp(ptr, "FLRIG") == 0)
		TNC->PTTMode = PTTFLRIG;
}

extern SOCKET ReportSocket;
extern char LOCATOR[80];
extern char ReportDest[7];
extern int NumberofPorts;
extern struct RIGPORTINFO * PORTInfo[34];		// Records are Malloc'd

time_t LastModeReportTime;
time_t LastFreqReportTime;

VOID SendReportMsg(char * buff, int txlen);

void sendModeReport()
{
	// if TNC is connected send mode and frequencies to Node Map as a MODE record
	// Are we better sending scan info as a separate record ??

	// MODE Port, HWType, Interlock 

	struct PORTCONTROL * PORT = PORTTABLE;

	struct TNCINFO * TNC;
	MESSAGE AXMSG;
	PMESSAGE AXPTR = &AXMSG;
	char Msg[300] = "MODE ";
	int i, Len = 5;

	if ((CurrentSecs - LastModeReportTime) < 900)	// Every 15 Mins
		return;

	LastModeReportTime = CurrentSecs;

	for (i = 0; i < NUMBEROFPORTS; i++)
	{	
		if (PORT->PROTOCOL == 10)
		{
			PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT;
			TNC = TNCInfo[PORT->PORTNUMBER];
			PORT = PORT->PORTPOINTER;

			if (TNC == NULL)
				continue;

			if (TNC->CONNECTED == 0 && TNC->TNCOK == 0)
				continue;

			if (ReportSocket == 0 || LOCATOR[0] == 0)
				continue;

			if (TNC->Frequency)
				Len += sprintf(&Msg[Len], "%d,%d,%d,%.6f/", TNC->Port, TNC->Hardware, TNC->RXRadio, atof(TNC->Frequency));
			else
				Len += sprintf(&Msg[Len], "%d,%d,%d/", TNC->Port, TNC->Hardware, TNC->RXRadio);

			if (Len > 240)
				break;
		}
		else
			PORT = PORT->PORTPOINTER;
	}

	if (Len == 5)
		return;			// Nothing to send
	
	// Block includes the Msg Header (7 bytes), Len Does not!

	memcpy(AXPTR->DEST, ReportDest, 7);
	memcpy(AXPTR->ORIGIN, MYCALL, 7);
	AXPTR->DEST[6] &= 0x7e;			// Clear End of Call
	AXPTR->DEST[6] |= 0x80;			// set Command Bit

	AXPTR->ORIGIN[6] |= 1;			// Set End of Call
	AXPTR->CTL = 3;		//UI
	AXPTR->PID = 0xf0;
	memcpy(AXPTR->L2DATA, Msg, Len);

	SendReportMsg((char *)&AXMSG.DEST, Len + 16) ;
}

void sendFreqReport(char * From)
{
	// Send info from rig control or Port Frequency info to Node Map for Mode page.

	MESSAGE AXMSG;
	PMESSAGE AXPTR = &AXMSG;
	char Msg[300] = "FREQ ";
	int i, Len = 5, p;

	struct RIGPORTINFO * RIGPORT;
	struct RIGINFO * RIG;
	struct TimeScan * Band;	
	struct PORTCONTROL * PORT = PORTTABLE;
	struct TNCINFO * TNC;

	if ((CurrentSecs - LastFreqReportTime) < 7200)	// Every 2 Hours
		return;

	LastFreqReportTime = CurrentSecs;

	for (p = 0; p < NumberofPorts; p++)
	{
		RIGPORT = PORTInfo[p];

		for (i = 0; i < RIGPORT->ConfiguredRigs; i++)
		{
			int j = 1, k = 0;

			RIG = &RIGPORT->Rigs[i];

			if (RIG->reportFreqs)
			{
				Len += sprintf(&Msg[Len], "%d/00:00/%s,\\|",RIG->Interlock,RIG->reportFreqs); 
			}
			else
			{
				if (RIG->TimeBands)
				{
					Len += sprintf(&Msg[Len], "%d/",RIG->Interlock); 
					while (RIG->TimeBands[j])
					{
						Band = RIG->TimeBands[j];
						k = 0;

						if (Band->Scanlist[0])
						{
							Len += sprintf(&Msg[Len], "%02d:%02d/", Band->Start / 3600, Band->Start % 3600); 

							while (Band->Scanlist[k])
							{
								Len += sprintf(&Msg[Len],"%.0f,", Band->Scanlist[k]->Freq + RIG->rxOffset);
								k++;
							}
							Len += sprintf(&Msg[Len], "\\"); 
						}
						j++;
					}
					Len += sprintf(&Msg[Len], "|"); 
				}
			}
		}
	}

	// Look for Port freq info

	for (i = 0; i < NUMBEROFPORTS; i++)
	{	
		if (PORT->PROTOCOL == 10)
		{
			PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT;
			TNC = TNCInfo[PORT->PORTNUMBER];
			PORT = PORT->PORTPOINTER;

			if (TNC == NULL)
				continue;

			if (TNC->Frequency == NULL)
				continue;

			if (TNC->RIG->TimeBands && TNC->RIG->TimeBands[1]->Scanlist)
				continue;					// Have freq info from Rigcontrol
			
			if (TNC->RXRadio == 0)		// Replace with dummy
				TNC->RXRadio = nextDummyInterlock++;

			// Use negative port no instead of interlock group

			Len += sprintf(&Msg[Len], "%d/00:00/%.0f|", TNC->RXRadio, atof(TNC->Frequency) * 1000000.0);
		}
		else
			PORT = PORT->PORTPOINTER;
	}

	if (Len == 5)
		return;			// Nothing to send

	// Block includes the Msg Header (7 bytes), Len Does not!

	memcpy(AXPTR->DEST, ReportDest, 7);
	memcpy(AXPTR->ORIGIN, MYCALL, 7);
	AXPTR->DEST[6] &= 0x7e;			// Clear End of Call
	AXPTR->DEST[6] |= 0x80;			// set Command Bit

	AXPTR->ORIGIN[6] |= 1;			// Set End of Call
	AXPTR->CTL = 3;		//UI
	AXPTR->PID = 0xf0;
	memcpy(AXPTR->L2DATA, Msg, Len);

	SendReportMsg((char *)&AXMSG.DEST, Len + 16) ;
}


