/*
Copyright 2001-2018 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
*/	

//
//	DLL to inteface KAM TNC in Pactor Mode to BPQ32 switch 
//
//	Uses BPQ EXTERNAL interface
//

// Version 1.2.1.2 July 2010

// Send Change to ISS before each transmission
// Support up to 32 BPQ Ports

// Version 1.2.1.3 August 2010 

// Drop RTS as well as DTR on close

// Version 1.2.1.4 August 2010 

// Save Minimized State

// Version 1.2.1.5 September 2010

// Fix Freq Display after Node reconfig
// Only use AutoConnect APPL for Pactor Connects

// Version 1.2.2.1 September 2010

// Add option to get config from bpq32.cfg

//#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE

#include <stdio.h>
#include <stdlib.h>
#include "time.h"

#include "CHeaders.h"
#include "tncinfo.h"

#include "bpq32.h"

#define NARROWMODE Report_P1
#define WIDEMODE Report_P1		// Only supports PI

static char ClassName[]="KAMPACTORSTATUS";
static char WindowTitle[] = "KAM Pactor";
static int RigControlRow = 165;

extern UCHAR LogDirectory[];
static RECT Rect;

extern struct TNCINFO * TNCInfo[41];		// Records are Malloc'd

int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);

static FILE * LogHandle[32] = {0};

//char * Logs[4] = {"1", "2", "3", "4"};

static char BaseDir[MAX_PATH]="c:\\";

static BOOL WRITELOG = FALSE;

BOOL KAMStopPort(struct PORTCONTROL * PORT)
{
	// Disable Port - close TCP Sockets or Serial Port

	struct TNCINFO * TNC = PORT->TNC;

	TNC->CONNECTED = FALSE;
	TNC->Alerted = FALSE;

	if (TNC->Streams[0].Attached)
		TNC->Streams[0].ReportDISC = TRUE;

	if (TNC->hDevice)
	{
		CloseCOMPort(TNC->hDevice);
		TNC->hDevice = 0;
	}

	TNC->HostMode = FALSE;

	sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped");
	MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE);

	return TRUE;
}

BOOL KAMStartPort(struct PORTCONTROL * PORT)
{
	// Restart Port - Open Sockets or Serial Port

	struct TNCINFO * TNC = PORT->TNC;

	TNC->ReopenTimer = 999;
	TNC->ReinitState = 0;
		
	sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted");
	MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE);

	return TRUE;
}



static VOID CloseLogFile(int Flags)
{
	if (WRITELOG)
	{
		fclose(LogHandle[Flags]);
		LogHandle[Flags] = NULL;
	}
}

static BOOL OpenLogFile(int Flags)
{
	if (WRITELOG)
	{
		UCHAR FN[MAX_PATH];

		time_t T;
		struct tm * tm;

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

		sprintf(FN,"%s/logs/KAMLog_%02d%02d_%d.txt", LogDirectory, tm->tm_mon + 1, tm->tm_mday, Flags);

		LogHandle[Flags] = fopen(FN, "ab");
	
		return (LogHandle[Flags] != NULL);
	}
	return 0;
}

static void WriteLogLine(int Port, char * Msg, int MsgLen)
{
	if (WRITELOG)
	{
		OpenLogFile(Port);

		if (LogHandle[Port])
		{
			fwrite(Msg, 1, MsgLen, LogHandle[Port]); 
			fwrite("\r\n", 1, 2, LogHandle[Port]); 
		}
		CloseLogFile(Port);
	}
}



int ProcessLine(char * buf, int Port)
{
	UCHAR * ptr,* p_cmd;
	char * p_ipad = 0;
	char * p_port = 0;
	unsigned short WINMORport = 0;
	int BPQport;
	int len=510;
	struct TNCINFO * TNC;
	char errbuf[256];

	strcpy(errbuf, buf);

	BPQport = Port;
	
	TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO));
	memset(TNC, 0, sizeof(struct TNCINFO));

	TNC->InitScript = malloc(1000);
	TNC->InitScript[0] = 0;
	
	goto ConfigLine;


	// Read Initialisation lines

	while(TRUE)
	{
		if (GetLine(buf) == 0)
			return TRUE;
ConfigLine:

		strcpy(errbuf, buf);

		if (memcmp(buf, "****", 4) == 0)
			return TRUE;

		ptr = strchr(buf, ';');
		if (ptr)
		{
			*ptr++ = 13;
			*ptr = 0;
		}
		
		if (_memicmp(buf, "DEBUGLOG", 8) == 0)	// Write Debug Log
			WRITELOG = atoi(&buf[9]);
	
		else if (_memicmp(buf, "APPL", 4) == 0)
		{
			p_cmd = strtok(&buf[5], " \t\n\r");

			if (p_cmd && p_cmd[0] != ';' && p_cmd[0] != '#')
				TNC->ApplCmd=_strdup(_strupr(p_cmd));
		}
	
		else if (_memicmp(buf, "OLDMODE", 7) == 0)
			TNC->OldMode = TRUE;

		else if (_memicmp(buf, "VERYOLDMODE", 7) == 0)
		{
			TNC->OldMode = TRUE;
			TNC->VeryOldMode = TRUE;
		}

		else if (_memicmp(buf, "BUSYWAIT", 8) == 0)		// Wait time beofre failing connect if busy
			TNC->BusyWait = atoi(&buf[8]);

		else if (_memicmp(buf, "WL2KREPORT", 10) == 0)
			TNC->WL2K = DecodeWL2KReportLine(buf);
			
		else
			strcat (TNC->InitScript, buf);
	}
	return (TRUE);
}

#define	FEND	0xC0	// KISS CONTROL CODES 
#define	FESC	0xDB
#define	TFEND	0xDC
#define	TFESC	0xDD

static int MaxStreams = 26;

struct TNCINFO * CreateTTYInfo(int port, int speed);
BOOL OpenConnection(int);
BOOL SetupConnection(int);
BOOL CloseConnection(struct TNCINFO * conn);
static BOOL WriteCommBlock(struct TNCINFO * TNC);
BOOL DestroyTTYInfo(int port);
void CheckRXKAM(struct TNCINFO * TNC);
VOID KAMPoll(int Port);
VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len);
static VOID ProcessTermModeResponse(struct TNCINFO * TNC);
static VOID DoTNCReinit(struct TNCINFO * TNC);
static VOID DoTermModeTimeout(struct TNCINFO * TNC);

VOID ProcessPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
VOID ProcessKPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer);
VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len);

//	Note that Kantronics host Mode uses KISS format Packets (without a KISS COntrol Byte)

VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len);
static int	KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
static int	KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len);

static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{
	int txlen;
	PMSGWITHLEN buffptr;

	struct TNCINFO * TNC = TNCInfo[port];
	struct STREAMINFO * STREAM;

	int Stream;

	size_t Param;
	struct ScanEntry * Scan;

	if (TNC == NULL)
		return 0;

	if (TNC->hDevice == 0)
	{
		// Clear anything from UI_Q

		while (TNC->PortRecord->UI_Q)
		{
			buffptr = Q_REM(&TNC->PortRecord->UI_Q);
			ReleaseBuffer(buffptr);
		}

		// Try to reopen every 30 secs

		if (fn > 3  && fn < 7)
			goto ok;

		TNC->ReopenTimer++;

		if (TNC->ReopenTimer < 300)
			return 0;

		TNC->ReopenTimer = 0;
		
		if (TNC->PortRecord->PORTCONTROL.PortStopped == 0)
			OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE);

		if (TNC->hDevice == 0)
			return 0;
	}

ok:

	switch (fn)
	{
	case 7:			

		// 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances

		CheckRXKAM(TNC);
		KAMPoll(port);

		return 0;

	case 1:				// poll

		while (TNC->PortRecord->UI_Q)			// Release anything accidentally put on UI_Q
		{
			buffptr = Q_REM(&TNC->PortRecord->UI_Q);
			ReleaseBuffer(buffptr);
		}

		for (Stream = 0; Stream <= MaxStreams; Stream++)
		{
			STREAM = &TNC->Streams[Stream];

			if (STREAM->ReportDISC)
			{
				STREAM->ReportDISC = FALSE;
				buff->PORT = Stream;

				return -1;
			}
		}

		for (Stream = 0; Stream <= MaxStreams; Stream++)
		{
			STREAM = &TNC->Streams[Stream];

			if (STREAM->PACTORtoBPQ_Q !=0)
			{
				int datalen;
			
				buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q);

				datalen = (int)buffptr->Len;

				buff->PORT = Stream;
				buff->PID= 0xf0;
				memcpy(buff->L2DATA, buffptr->Data, datalen);		// Data goes to +7, but we have an extra byte
				datalen += (MSGHDDRLEN + 1);

				PutLengthinBuffer(buff, datalen);

				ReleaseBuffer(buffptr);
	
				return (1);
			}
		}
			
		return 0;

	case 2:				// send

		buffptr = GetBuff();

		if (buffptr == 0) return (0);			// No buffers, so ignore

		// Find TNC Record

		Stream = buff->PORT;
		STREAM = &TNC->Streams[Stream];
		
		if (!TNC->TNCOK)
		{
			// Send Error Response

			buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r");

			C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
			
			return 0;
		}

		txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1);

		buffptr->Len = txlen;
		memcpy(buffptr->Data, buff->L2DATA, txlen);

		C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);

		if(STREAM->Connected)
		{
			STREAM->FramesOutstanding++;
			STREAM->FramesQueued++;

			STREAM->BytesOutstanding += txlen;
		}
		return (0);


	case 3:				// CHECK IF OK TO SEND. Also used to check if TNC is responding
		
		Stream = (int)(size_t)buff;
	
		STREAM = &TNC->Streams[Stream];

		if (Stream == 0)
		{
			if (TNC->HFPacket)
			{
				if (TNC->Mem1 < 2000 || TNC->Streams[0].FramesOutstanding  > 4)	
					return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);

			}
			else
			{
				if (TNC->Streams[0].FramesQueued > 4)
					return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);
			}
		}
		else
		{
			if (STREAM->FramesOutstanding > 3 || STREAM->BytesOutstanding > 500 || TNC->Mem1 < 500)	
				return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);
		}

		return TNC->HostMode << 8 | STREAM->Disconnecting << 15;		// OK, but lock attach if disconnecting

	case 4:				// reinit

		return (0);

	case 5:				// Close

		EncodeAndSend(TNC, "Q", 1);			// Exit Host Mode
		Sleep(50);

		CloseCOMPort(TNCInfo[port]->hDevice);		
		return (0);

	case 6:				// Scan Control

		// Use P0 to Disable Pactor, P1 to enable

		Param = (size_t)buff;

		if (Param == 2)		// Check  Permission (shouldn't happen)
		{
			return 1;		// OK to change
		}

		if (!TNC->HostMode)
			return 0;		// No connection so no interlock
	
		if (Param == 1)		// Request Permission
			return 0;		// OK to Change					// Dont want to mess with disabling it
	
		if (Param == 3)		// Release Permission
			return 0;

		// Param is Address of a struct ScanEntry

		Scan = (struct ScanEntry *)buff;

		if (Scan->PMaxLevel >= '0' && Scan->PMaxLevel < '5')		// 1 - 4 
		{
			if (TNC->Bandwidth != Scan->PMaxLevel)
			{
				// Enable or disable by setting mycall

				TNC->Bandwidth = Scan->PMaxLevel;
				
				if (Scan->PMaxLevel == '0')
				{
					EncodeAndSend(TNC, "X", 1);			// ??Return to packet mode??
					return 0;
				}
				if (TNC->OldMode)
					EncodeAndSend(TNC, "C20PACTOR", 9);			// Back to Listen
				else
					EncodeAndSend(TNC, "C20TOR", 6);			// Back to Listen
				
				TNC->InternalCmd = 'T';
			}
		}
	}
	return 0;
}

static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL)
{
	int Len = sprintf(Buff, "<html><meta http-equiv=expires content=0><meta http-equiv=refresh content=15>"
	"<head><title>KAM Pactor Status</title></head><body><h3>KAM Pactor Status</h3>");

	Len += sprintf(&Buff[Len], "<table style=\"text-align: left; width: 480px; font-family: monospace; align=center \" border=1 cellpadding=2 cellspacing=2>");

	Len += sprintf(&Buff[Len], "<tr><td width=90px>Comms State</td><td>%s</td></tr>", TNC->WEB_COMMSSTATE);
	Len += sprintf(&Buff[Len], "<tr><td>TNC State</td><td>%s</td></tr>", TNC->WEB_TNCSTATE);
	Len += sprintf(&Buff[Len], "<tr><td>Mode</td><td>%s</td></tr>", TNC->WEB_MODE);
	Len += sprintf(&Buff[Len], "<tr><td>Status</td><td>%s</td></tr>", TNC->WEB_STATE);
	Len += sprintf(&Buff[Len], "<tr><td>TX/RX State</td><td>%s</td></tr>", TNC->WEB_TXRX);
	Len += sprintf(&Buff[Len], "<tr><td>Free Space</td><td>%s</td></tr>", TNC->WEB_BUFFERS);
	Len += sprintf(&Buff[Len], "<tr><td>Traffic</td><td>%s</td></tr>", TNC->WEB_TRAFFIC);
	Len += sprintf(&Buff[Len], "</table>");

	Len += sprintf(&Buff[Len], "<textarea rows=10 style=\"width:500px; height:250px;\" id=textarea >%s</textarea>", TNC->WebBuffer);
	Len = DoScanLine(TNC, Buff, Len);

	return Len;
}


void * KAMExtInit(EXTPORTDATA * PortEntry)
{
	char msg[500];
	struct TNCINFO * TNC;
	int port;
	char * ptr;
	char * TempScript;

	port=PortEntry->PORTCONTROL.PORTNUMBER;

	sprintf(msg,"KAM Pactor %s", PortEntry->PORTCONTROL.SerialPortName);
	WritetoConsole(msg);

	ReadConfigFile(port, ProcessLine);

	TNC = TNCInfo[port];

	if (TNC == NULL)
	{
		// Not defined in Config file

		sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n");
		WritetoConsole(msg);

		return ExtProc;
	}
	TNC->Port = port;

	TNC->Hardware = H_KAM;

	if (TNC->BusyWait == 0)
		TNC->BusyWait = 10;

	PortEntry->MAXHOSTMODESESSIONS = 11;		// Default

	TNC->PortRecord = PortEntry;

	if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
		memcpy(TNC->NodeCall, MYNODECALL, 10);
	else
		ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);

	PortEntry->PORTCONTROL.PROTOCOL = 10;	// WINMOR/Pactor
	PortEntry->PORTCONTROL.PORTQUALITY = 0;
	PortEntry->SCANCAPABILITIES = NONE;		// No Scan Control 

	if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
		TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;

	if (PortEntry->PORTCONTROL.PORTPACLEN == 0)
		PortEntry->PORTCONTROL.PORTPACLEN = 100;

	PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort;
	PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort;

	ptr=strchr(TNC->NodeCall, ' ');
	if (ptr) *(ptr) = 0;					// Null Terminate

	// Set Essential Params and MYCALL

	TempScript = malloc(4000);

	strcpy(TempScript, "MARK 1400\r");
	strcat(TempScript, "SPACE 1600\r");
	strcat(TempScript, "SHIFT MODEM\r");
	strcat(TempScript, "INV ON\r");
	strcat(TempScript, "PTERRS 30\r");			// Default Retries
	strcat(TempScript, "MAXUSERS 1/10\r");
	strcat(TempScript, TNC->InitScript);

	free(TNC->InitScript);
	TNC->InitScript = TempScript;

	// Others go on end so they can't be overriden

	strcat(TNC->InitScript, "ECHO OFF\r");
	strcat(TNC->InitScript, "XMITECHO ON\r");
	strcat(TNC->InitScript, "TXFLOW OFF\r");
	strcat(TNC->InitScript, "XFLOW OFF\r");
	strcat(TNC->InitScript, "TRFLOW OFF\r");
	strcat(TNC->InitScript, "AUTOCR 0\r");
	strcat(TNC->InitScript, "AUTOLF OFF\r");
	strcat(TNC->InitScript, "CRADD OFF\r");
	strcat(TNC->InitScript, "CRSUP OFF\r");
	strcat(TNC->InitScript, "CRSUP OFF/OFF\r");
	strcat(TNC->InitScript, "LFADD OFF/OFF\r");
	strcat(TNC->InitScript, "LFADD OFF\r");
	strcat(TNC->InitScript, "LFSUP OFF/OFF\r");
	strcat(TNC->InitScript, "LFSUP OFF\r");
	strcat(TNC->InitScript, "RING OFF\r");
	strcat(TNC->InitScript, "ARQBBS OFF\r");

	// Set the ax.25 MYCALL


	sprintf(msg, "MYCALL %s/%s\r", TNC->NodeCall, TNC->NodeCall);
	strcat(TNC->InitScript, msg);

	// look for the MAXUSERS config line, and get the limits

	TNC->InitScript = _strupr(TNC->InitScript);

	ptr = strstr(TNC->InitScript, "MAXUSERS");
	
	if (ptr)
	{
		ptr = strchr(ptr,'/');  // to the separator
		if (ptr)
			PortEntry->MAXHOSTMODESESSIONS = atoi(++ptr) + 1;
	}

	if (PortEntry->MAXHOSTMODESESSIONS > 26)
		PortEntry->MAXHOSTMODESESSIONS = 26;

	PortEntry->PORTCONTROL.TNC = TNC;

	TNC->WebWindowProc = WebProc;
	TNC->WebWinX = 510;
	TNC->WebWinY = 280;

	TNC->WEB_COMMSSTATE = zalloc(100);
	TNC->WEB_TNCSTATE = zalloc(100);
	strcpy(TNC->WEB_TNCSTATE, "Free");
	TNC->WEB_MODE = zalloc(100);
	TNC->WEB_TRAFFIC = zalloc(100);
	TNC->WEB_BUFFERS = zalloc(100);
	TNC->WEB_STATE = zalloc(100);
	TNC->WEB_TXRX = zalloc(100);
	TNC->WebBuffer = zalloc(5000);

#ifndef LINBPQ

	CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 500, ForcedClose);

 
	CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL);
	
	CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL);

	CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL);
 
	CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL);

 	CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL);
 
	CreateWindowEx(0, "STATIC", "Free Space", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL);
	
	CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL);

	TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD |  WS_VISIBLE  | LBS_NOINTEGRALHEIGHT | 
            LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL,
			0,RigControlRow + 44,250,300, TNC->hDlg, NULL, hInstance, NULL);

	TNC->ClientHeight = 500;
	TNC->ClientWidth = 500;
	
	MoveWindows(TNC);
#endif
	OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE);

	WritetoConsole("\n");

	return ExtProc;
}



void CheckRXKAM(struct TNCINFO * TNC)
{
	int Length, Len;
	char debug[512] = "RX: ";

	// only try to read number of bytes in queue 

	if (TNC->RXLen == 500)
		TNC->RXLen = 0;

	Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen);

	if (Len == 0)
		return;

	TNC->RXLen += Len;

	memcpy(&debug[4], TNC->RXBuffer, TNC->RXLen);
	debug[TNC->RXLen + 4] = 0;		
	WriteLogLine(TNC->Port, debug, TNC->RXLen + 4);

	Length = TNC->RXLen;

	// If first char != FEND, then probably a Terminal Mode Frame. Wait for CR on end
			
	if (TNC->RXBuffer[0] != FEND)
	{
		// Char Mode Frame I think we need to see cmd: on end

		// If we think we are in host mode, then to could be noise - just discard.

		if (TNC->HostMode)
		{
			TNC->RXLen = 0;		// Ready for next frame
			return;
		}

		TNC->RXBuffer[TNC->RXLen] = 0;

//		if (TNC->RXBuffer[TNC->RXLen-2] != ':')
		if (strstr(TNC->RXBuffer, "cmd:") == 0)
			return;				// Wait for rest of frame

		// Complete Char Mode Frame

		TNC->RXLen = 0;		// Ready for next frame
					
		if (TNC->HostMode == 0)
		{
			// We think TNC is in Terminal Mode
			ProcessTermModeResponse(TNC);
			return;
		}
		// We thought it was in Host Mode, but are wrong.

		TNC->HostMode = FALSE;
		return;
	}

	// Receiving a Host Mode frame

	if (TNC->HostMode == 0)	// If we are in Term Mode, discard it. Probably in recovery
	{
		TNC->RXLen = 0;		// Ready for next frame
		return;
	}

	if (Length < 3)				// Minimum Frame Sise
		return;

	if (TNC->RXBuffer[Length-1] != FEND)
		return;					// Wait till we have a full frame

	ProcessHostFrame(TNC, TNC->RXBuffer, Length);	// Could have multiple packets in buffer

	TNC->RXLen = 0;		// Ready for next frame

		
	return;

}

VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len)
{
	UCHAR * FendPtr;
	int NewLen;

	//	Split into KISS Packets. By far the most likely is a single KISS frame
	//	so treat as special case

	if (rxbuffer[1] == FEND)			// Two FENDS - probably got out of sync
	{
		rxbuffer++;
		Len--;
	}
	
	FendPtr = memchr(&rxbuffer[1], FEND, Len-1);
	
	if (FendPtr == &rxbuffer[Len-1])
	{
		ProcessKHOSTPacket(TNC, &rxbuffer[1], Len - 2);
		return;
	}
		
	// Process the first Packet in the buffer

	NewLen = (int)(FendPtr - rxbuffer - 1);

	ProcessKHOSTPacket(TNC, &rxbuffer[1], NewLen);
	
	// Loop Back

	ProcessHostFrame(TNC, FendPtr+1, Len - NewLen - 2);
	return;

}



static BOOL WriteCommBlock(struct TNCINFO * TNC)
{
	char debug[512] = "TX: ";

	memcpy(&debug[4], TNC->TXBuffer, TNC->TXLen);
	debug[TNC->TXLen + 4] = 0;
			
	WriteLogLine(TNC->Port, debug, TNC->TXLen + 4);

	WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen);

	return TRUE;
}

VOID KAMPoll(int Port)
{
	struct TNCINFO * TNC = TNCInfo[Port];
	struct STREAMINFO * STREAM;

	UCHAR * Poll = TNC->TXBuffer;
	char Status[80];
	int Stream;

	if (TNC->PortRecord == 0)
		Stream = 0;


	// If Pactor Session has just been attached, drop back to cmd mode and set Pactor Call to 
	// the connecting user's callsign

	for (Stream = 0; Stream <= MaxStreams; Stream++)
	{
		STREAM = &TNC->Streams[Stream];

		if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0)
		{
			// New Attach

			STREAM->Attached = TRUE;

			if (Stream == 0)			// HF Port
			{
				int calllen;
				UCHAR TXMsg[1000] = "D20";
				int datalen;
				char Msg[80];
				
				TNC->HFPacket = FALSE;
				TNC->TimeInRX = 0;

				calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall);
				TNC->Streams[0].MyCall[calllen] = 0;
		
					EncodeAndSend(TNC, "X", 1);			// ??Return to packet mode??
					if (TNC->VeryOldMode)
						datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->Streams[0].MyCall);
					else
						datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall);
					EncodeAndSend(TNC, TXMsg, datalen);
					TNC->InternalCmd = 'M';
		
					TNC->NeedPACTOR = 0;		// Cancel enter Pactor

					sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall);
					SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

					// Stop Scanning

					sprintf(Msg, "%d SCANSTOP", TNC->Port);
		
					Rig_Command(-1, Msg);

			}
		}
	}
	
	if (TNC->Timeout)
	{
		TNC->Timeout--;
		
		if (TNC->Timeout)			// Still waiting
			return;

		// Timed Out

		if (TNC->HostMode == 0)
		{
			DoTermModeTimeout(TNC);
			return;
		}

		// Timed out in host mode - Clear any connection and reinit the TNC

		Debugprintf("KAM PACTOR - Link to TNC Lost");
		TNC->TNCOK = FALSE;
		TNC->HostMode = 0;
		TNC->ReinitState = 0;
				
		sprintf(TNC->WEB_COMMSSTATE, "%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName);
		SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

		for (Stream = 0; Stream <= MaxStreams; Stream++)
		{
			PMSGWITHLEN buffptr;
			
			STREAM = &TNC->Streams[Stream];

			if (TNC->PortRecord->ATTACHEDSESSIONS[Stream])		// Connected
			{
				STREAM->Connected = FALSE;		// Back to Command Mode
				STREAM->ReportDISC = TRUE;		// Tell Node
			}
			
			STREAM->FramesQueued = 0;

			while(STREAM->BPQtoPACTOR_Q)
			{
				buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
				ReleaseBuffer(buffptr);
			}

			while(STREAM->PACTORtoBPQ_Q)
			{
				buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q);
				ReleaseBuffer(buffptr);
			}
		}
	}

	for (Stream = 0; Stream <= MaxStreams; Stream++)
	{
		STREAM = &TNC->Streams[Stream];

		if (STREAM->Attached)
			CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);

	}

	// if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence

	if (!TNC->HostMode)
	{
		DoTNCReinit(TNC);
		return;
	}

	if (TNC->BusyDelay)		// Waiting to send connect
	{
		// Still Busy?

		if (InterlockedCheckBusy(TNC) == 0)
		{
			// No, so send

			EncodeAndSend(TNC, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd));
			free(TNC->ConnectCmd);

			TNC->Timeout = 50;
			TNC->InternalCmd = 'C';			// So we dont send the reply to the user.
			STREAM->Connecting = TRUE;

			TNC->Streams[0].Connecting = TRUE;

			sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
			SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

			TNC->BusyDelay = 0;
			return;
		}
		else
		{
			// Wait Longer

			TNC->BusyDelay--;

			if (TNC->BusyDelay == 0)
			{
				// Timed out - Send Error Response

				PMSGWITHLEN buffptr = GetBuff();

				if (buffptr == 0) return;			// No buffers, so ignore

				buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r");

				C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);

				free(TNC->ConnectCmd);

			}
		}
	}

	if (TNC->NeedPACTOR)
	{
		TNC->NeedPACTOR--;

		if (TNC->NeedPACTOR == 0)
		{
			int datalen;
			UCHAR TXMsg[80] = "D20";

			if (TNC->VeryOldMode)
				datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->NodeCall);
			else
				datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->NodeCall);
			EncodeAndSend(TNC, TXMsg, datalen);

			if (TNC->OldMode)
				EncodeAndSend(TNC, "C20PACTOR", 9);			// Back to Listen
			else
				EncodeAndSend(TNC, "C20TOR", 6);			// Back to Listen

			TNC->InternalCmd = 'T';
			TNC->Timeout = 50;
			TNC->IntCmdDelay--;

			// Restart Scanning

			sprintf(Status, "%d SCANSTART 15", TNC->Port);
		
			Rig_Command(-1, Status);

			return;
		}
	}

	for (Stream = 0; Stream <= MaxStreams; Stream++)
	{
		STREAM = &TNC->Streams[Stream];

		// If in HF Packet mode, normal flow control doesn't seem to work
		//	If more that 4 packets sent, send a status poll. and use response to 
		//	reset frames outstanding

		if ((Stream == 0) && (TNC->HFPacket) && (TNC->Streams[0].FramesOutstanding  > 4))
		{
			EncodeAndSend(TNC, "C10S", 4);
			TNC->InternalCmd = 'S';
			TNC->Timeout = 50;
			return;

		}

		if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q)
		{
			int datalen;
			UCHAR TXMsg[1000] = "D20";
			PMSGWITHLEN buffptr;
			UCHAR * MsgPtr;
			char Status[80];

			if (STREAM->Connected)
			{
				int Next;
				
				if (Stream > 0)
					sprintf(TXMsg, "D1%c", Stream + '@');
				else if (TNC->HFPacket)
					memcpy(TXMsg, "D2A", 3);
				else
				{
					// Pactor

					// Limit amount in TX, so we keep some on the TX Q and don't send turnround too early

					if (TNC->Streams[0].BytesTXed - TNC->Streams[0].BytesAcked > 200)
						continue;

					// Dont send if IRS State
					// If in IRS state for too long, force turnround

					if (TNC->TXRXState == 'R')
					{
						if (TNC->TimeInRX++ > 15)
							EncodeAndSend(TNC, "T", 1);			// Changeover to ISS 
						else
							goto Poll;
					}
					TNC->TimeInRX = 0;
				}

				buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
				STREAM->FramesQueued--;

				datalen = buffptr->Len;
				MsgPtr = buffptr->Data;

				if (TNC->SwallowSignon && Stream == 0)
				{
					TNC->SwallowSignon = FALSE;	
					if (strstr(MsgPtr, "Connected"))	// Discard *** connected
					{
						ReleaseBuffer(buffptr);
						return;
					}
				}

				Next = 0;
				STREAM->BytesTXed += datalen; 

				if (Stream == 0)
				{
					while (datalen > 100)		// Limit Pactor Sends
					{
						memcpy(&TXMsg[3], &MsgPtr[Next], 100);
						EncodeAndSend(TNC, TXMsg, 103);
						Next += 100;
						datalen -= 100;

						WritetoTrace(TNC, &TXMsg[3], 100);
					}
				}

				memcpy(&TXMsg[3], &MsgPtr[Next], datalen);
				EncodeAndSend(TNC, TXMsg, datalen + 3);

				WritetoTrace(TNC, &TXMsg[3], datalen);

				ReleaseBuffer(buffptr);

				if (Stream == 0)
				{
					sprintf(Status, "RX %d TX %d ACKED %d ",
						TNC->Streams[0].BytesRXed, TNC->Streams[0].BytesTXed, TNC->Streams[0].BytesAcked);
					SetWindowText(TNC->xIDC_TRAFFIC, Status);

					if ((TNC->HFPacket == 0) && (TNC->Streams[0].BPQtoPACTOR_Q == 0))		// Nothing following
					{
						EncodeAndSend(TNC, "E", 1);			// Changeover when all sent
					}
				}

				if (STREAM->Disconnecting)
				{
					Debugprintf("Send with Disc Pending, Q = %x", STREAM->BPQtoPACTOR_Q);
					if (STREAM->BPQtoPACTOR_Q == 0)		// All Sent

						// KAM doesnt have a tidy close!

						STREAM->DisconnectingTimeout = 100;							// Give 5 secs to get to other end
				}
				return;
			}
			else // Not Connected
			{
				buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q);
				datalen = buffptr->Len;
				MsgPtr = buffptr->Data;

				// Command. Do some sanity checking and look for things to process locally

				datalen--;				// Exclude CR
				MsgPtr[datalen] = 0;	// Null Terminate
				_strupr(MsgPtr);

				if ((Stream == 0) && memcmp(MsgPtr, "RADIO ", 6) == 0)
				{
					sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]);
					if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &MsgPtr[40]))
					{
						ReleaseBuffer(buffptr);
					}
					else
					{
						buffptr->Len = sprintf(buffptr->Data, "%s", &MsgPtr[40]);
						C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
					}
					return;
				}

				if (_memicmp(MsgPtr, "D\r", 2) == 0)
				{
					STREAM->ReportDISC = TRUE;		// Tell Node
					return;
				}

				if ((Stream == 0) && memcmp(MsgPtr, "HFPACKET", 8) == 0)
				{
					TNC->HFPacket = TRUE;
					buffptr->Len = sprintf(buffptr->Data, "KAM} OK\r");
					C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
					return;
				}

				if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2)	// Connect
				{
					memcpy(STREAM->RemoteCall, &MsgPtr[2], 9);
					STREAM->Connecting = TRUE;

					// If Stream 0, Convert C CALL to PACTOR CALL

					if (Stream == 0)
					{
						if (TNC->HFPacket)
							datalen = sprintf(TXMsg, "C2AC %s", TNC->Streams[0].RemoteCall);
						else
							datalen = sprintf(TXMsg, "C20PACTOR %s", TNC->Streams[0].RemoteCall);
						
						// If Pactor, check busy detecters on any interlocked ports

						if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0)
						{
							// Channel Busy. Wait

							TNC->ConnectCmd = _strdup(TXMsg);

							sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel");
							SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

							TNC->BusyDelay = TNC->BusyWait * 10;

							return;
						}

						TNC->OverrideBusy = FALSE;

						sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s",
							TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
						SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
					}
					else
						datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall);

					EncodeAndSend(TNC, TXMsg, datalen);
					TNC->Timeout = 50;
					TNC->InternalCmd = 'C';			// So we dont send the reply to the user.
					ReleaseBuffer(buffptr);
					STREAM->Connecting = TRUE;

					return;
				}

				if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0)	// Disconnect
				{
					if (Stream == 0)
					{
						if (TNC->HFPacket)
							EncodeAndSend(TNC, "C2AD", 4);		// ??Return to packet mode??
						else
							EncodeAndSend(TNC, "X", 1);			// ??Return to packet mode??
			
						TNC->NeedPACTOR = 50;
					}
					else
					{
						sprintf(TXMsg, "C1%cD", Stream + '@');
						EncodeAndSend(TNC, TXMsg, 4);
						TNC->CmdStream = Stream;
						TNC->Timeout = 50;
					}

					TNC->Timeout = 0;					//	Don't expect a response
					STREAM->Connecting = FALSE;
					STREAM->ReportDISC = TRUE;
					ReleaseBuffer(buffptr);

					return;
				}
	
				// Other Command ??

				if (Stream > 0)
					datalen = sprintf(TXMsg, "C1%c%s", Stream + '@', MsgPtr);
				else
					datalen = sprintf(TXMsg, "C20%s", MsgPtr);
				EncodeAndSend(TNC, TXMsg, datalen);
				ReleaseBuffer(buffptr);
				TNC->Timeout = 50;
				TNC->InternalCmd = 0;
				TNC->CmdStream = Stream;

			}
		}
	}

Poll:

	// Need to poll data and control channel (for responses to commands)

	// Also check status if we have data buffered (for flow control)

	if (TNC->TNCOK)
	{
		if (TNC->IntCmdDelay == 50)
		{
			EncodeAndSend(TNC, "C10S", 4);
			TNC->InternalCmd = 'S';
			TNC->Timeout = 50;
			TNC->IntCmdDelay--;
			return;
		}

		if (TNC->IntCmdDelay <=0)
		{
			if (TNC->VeryOldMode == FALSE)
			{
				EncodeAndSend(TNC, "?", 1);
				TNC->InternalCmd = '?';
				TNC->Timeout = 50;
			}
			TNC->IntCmdDelay = 100;	// Every 30
			return;
		}
		else
			TNC->IntCmdDelay--;
	}

	return;

}

static VOID DoTNCReinit(struct TNCINFO * TNC)
{
	UCHAR * Poll = TNC->TXBuffer;

	if (TNC->ReinitState == 1)		// Forcing back to Term
		TNC->ReinitState = 0;		// Got Response, so must be back in term mode

	if (TNC->ReinitState == 0)
	{
		// Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode
		
		sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName);
		SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

		Poll[0] = 13;
		TNC->TXLen = 1;

		WriteCommBlock(TNC);
		TNC->Timeout = 50;

		return;
	}

	if (TNC->ReinitState == 2)		// In Term State, Sending Initialisation Commands
	{
		char * start, * end;
		int len;

		start = TNC->InitPtr;
		
		if (*(start) == 0)			// End of Script
		{
			// Put into Host Mode

			memcpy(Poll, "INTFACE HOST\r", 13);

			TNC->TXLen = 13;
			WriteCommBlock(TNC);
			TNC->Timeout = 50;

			TNC->ReinitState = 4;	// Need Reset 

			return;
		}
		
		end = strchr(start, 13);
		len = (int)(++end - start);
		TNC->InitPtr = end;
		memcpy(Poll, start, len);

		TNC->TXLen = len;
		WriteCommBlock(TNC);
		TNC->Timeout = 50;

		return;

	}
}

static VOID DoTermModeTimeout(struct TNCINFO * TNC)
{
	UCHAR * Poll = TNC->TXBuffer;

	if (TNC->ReinitState == 0)
	{
		//Checking if in Terminal Mode - Try to set back to Term Mode

		TNC->ReinitState = 1;
		Poll[0] = 3;
		Poll[1] = 0x58;				// ?? Back to cmd: mode ??
		TNC->TXLen = 2;

		Poll[0] = 0xc0;
		Poll[1] = 'Q';				// ?? Back to cmd: mode ??
		Poll[2] = 0xc0;
		TNC->TXLen = 3;

		WriteCommBlock(TNC);

		return;
	}
	if (TNC->ReinitState == 1)
	{
		// Forcing back to Term Mode

		TNC->ReinitState = 0;
		DoTNCReinit(TNC);				// See if worked
		return;
	}

	if (TNC->ReinitState == 3)
	{
		// Entering Host Mode
	
		// Assume ok

		TNC->HostMode = TRUE;
		return;
	}
}

	

static VOID ProcessTermModeResponse(struct TNCINFO * TNC)
{
	UCHAR * Poll = TNC->TXBuffer;

	if (TNC->ReinitState == 0 || TNC->ReinitState == 1) 
	{
		// Testing if in Term Mode. It is, so can now send Init Commands

		TNC->InitPtr = TNC->InitScript;
		TNC->ReinitState = 2;
		DoTNCReinit(TNC);		// Send First Command
		return;
	}
	if (TNC->ReinitState == 2)
	{
		// Sending Init Commands

		DoTNCReinit(TNC);		// Send Next Command
		return;
	}

	if (TNC->ReinitState == 4)		// Send INTFACE, Need RESET
	{
		TNC->ReinitState = 5;
			
		memcpy(Poll, "RESET\r", 6);

		TNC->TXLen = 6;
		WriteCommBlock(TNC);
		TNC->Timeout = 50;
		TNC->HostMode = TRUE;		// Should now be in Host Mode
		TNC->NeedPACTOR = 50;		// Need to Send PACTOR command after 5 secs

		return;
	}

	if (TNC->ReinitState == 5)		// RESET sent
	{
		TNC->ReinitState = 5;

		return;
	}



}

VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len)
{
	PMSGWITHLEN buffptr;
	char * Buffer = &Msg[3];			// Data portion of frame
	char * Call;
	char Status[80];
	int Stream = 0;
	struct STREAMINFO * STREAM;

	// Any valid frame is an ACK

	TNC->TNCOK = TRUE;

	Len = KissDecode(Msg, Msg, Len);	// Remove KISS transparency

	if (Msg[1] == '0' && Msg[2] == '0')
		Stream = 0;
	else
		if (Msg[1] == '2') Stream = 0; else Stream = Msg[2] - '@';	

	STREAM = &TNC->Streams[Stream];

	//	See if Poll Reply or Data

	Msg[Len] = 0; // Terminate

	if (Msg[0] == 'M')					// Monitor
	{
		DoMonitor(TNC, Msg, Len);
		return;
	}


	if (Msg[0] == 'E')					// Data Echo
	{
		if (Msg[1] == '2')				// HF Port
		{
			if (TNC->Streams[0].BytesTXed)
				TNC->Streams[0].BytesAcked += Len - 3;	// We get an ack before the first send

			sprintf(Status, "RX %d TX %d ACKED %d ",
				TNC->Streams[0].BytesRXed, TNC->Streams[0].BytesTXed, TNC->Streams[0].BytesAcked);
			SetWindowText(TNC->xIDC_TRAFFIC, Status);

			if (TNC->Streams[0].BytesTXed - TNC->Streams[0].BytesAcked < 500)
				TNC->Streams[0].FramesOutstanding = 0;
		}
		return;
	}

	if (Msg[0] == 'D')					// Data
	{	
		// Pass to Appl

		buffptr = GetBuff();
		if (buffptr == NULL) return;			// No buffers, so ignore

		Len-=3;							// Remove Header

		buffptr->Len = Len;				// Length
		STREAM->BytesRXed += Len;
		memcpy(buffptr->Data, Buffer, Len);

		C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);

		if (Stream == 0)
		{
			sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", 
					TNC->Streams[0].BytesRXed, TNC->Streams[0].BytesTXed, TNC->Streams[0].BytesAcked);
			SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC);
		}

		WritetoTrace(TNC, Buffer, Len);

		return;
	}


	if (Msg[0] == 'C')					// Command Reponse
	{
		TNC->Timeout = 0;
	
		// See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc

		// See if a response to internal command

		if (TNC->InternalCmd)
		{
			// Process it

			if (TNC->InternalCmd == 'S')		// Status
			{
				char * Line;
				char * ptr;
					
				// Message is line giving free bytes, followed by a line for each active (packet) stream

				// FREE BYTES 1366/5094
				// A/2 #1145(12) CONNECTED to KE7XO-3
				// S/2 CONNECTED to NLV

				// each line is teminated by CR, and by the time it gets here it is null terminated

				//FREE BYTES 2628
				//A/H #80(1) CONNECTED to DK0MNL..

				if (TNC->HFPacket)
					TNC->Streams[0].FramesOutstanding = 0;

				Line = strchr(&Msg[3], 13);
				if (Line == 0) return;

				*(Line) = 0;

				ptr =  strchr(&Msg[13], '/');
				TNC->Mem1 = atoi(&Msg[13]);
				if (ptr) 
					TNC->Mem2 = atoi(++ptr);
				else
					TNC->Mem2 = 0;

				SetWindowText(TNC->xIDC_BUFFERS, &Msg[14]);
				strcpy(TNC->WEB_BUFFERS, &Msg[14]);

				while (Line[1] != 0)		// End of stream
				{
					Stream = Line[1] - '@';
					STREAM = &TNC->Streams[Stream];

					if (Line[5] == '#')
					{
						STREAM->BytesOutstanding = atoi(&Line[6]);
						ptr = strchr(&Line[6], '(');
						if (ptr)
							STREAM->FramesOutstanding = atoi(++ptr);
					}
					else
					{
						STREAM->BytesOutstanding = 0;
						STREAM->FramesOutstanding = 0;
					}
						
					Line = strchr(&Line[1], 13);
				}
				return;
			}
			return;
		}

		// Pass to Appl

		Stream = TNC->CmdStream;


		buffptr = GetBuff();

		if (buffptr == NULL) return;			// No buffers, so ignore

		buffptr->Len = sprintf(buffptr->Data,"KAM} %s", Buffer);

		C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);

		return;
	}

	if (Msg[0] == 'I')					// ISS/IRS State
	{
		if (Msg[2] == '1')
		{
			strcpy(TNC->WEB_TXRX, "Sender");
			SetWindowText(TNC->xIDC_TXRX, "Sender");
			TNC->TXRXState = 'S';
		}
		else
		{
			strcpy(TNC->WEB_TXRX, "Receiver");
			SetWindowText(TNC->xIDC_TXRX, "Receiver");
			TNC->TXRXState = 'R';
		}
		return;
	}

	if (Msg[0] == '?')					// Status
	{
		TNC->Timeout = 0;
		return;
	}

	if (Msg[0] == 'S')					// Status
	{
		if (Len < 4)
		{
			// Reset Response FEND FEND S00 FEND
					
			TNC->Timeout = 0;

			sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName);
			SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

			return;
		}
		
		// Pass to Appl

		if (strstr(Buffer, "STANDBY>") || strstr(Buffer, "*** DISCONNECTED"))
		{
			if ((STREAM->Connecting | STREAM->Connected) == 0)
			{
				// Not connected or Connecting. Probably response to going into Pactor Listen Mode

				return;
			}
	
			if (STREAM->Connecting && STREAM->Disconnecting == FALSE)
			{
				// Connect Failed
			
				buffptr = GetBuff();
				if (buffptr == 0) return;			// No buffers, so ignore

				buffptr->Len  = sprintf(buffptr->Data, "*** Failure with %s\r", STREAM->RemoteCall);

				C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
	
				STREAM->Connecting = FALSE;
				STREAM->Connected = FALSE;				// In case!
				STREAM->FramesOutstanding = 0;

				return;
			}

			// Must Have been connected or disconnecting - Release Session

			STREAM->Connecting = FALSE;
			STREAM->Connected = FALSE;		// Back to Command Mode
			STREAM->FramesOutstanding = 0;

			if (STREAM->Disconnecting == FALSE)
				STREAM->ReportDISC = TRUE;		// Tell Node

			STREAM->Disconnecting = FALSE;

			if (Stream == 0)
			{
				// Need to reset Pactor Call in case it was changed

				EncodeAndSend(TNC, "X", 1);			// ??Return to packet mode??
				TNC->NeedPACTOR = 20;
			}

			return;
		}

		if (Msg[2] == '0')
			Call = strstr(Buffer, "<LINKED TO");
		else
			Call = strstr(Buffer, "NNECTED to");

		if (Call)
		{	
			Call+=11;					// To Callsign
			
			if (Stream == 0 && TNC->HFPacket == 0)
			{
				Buffer[Len-4] = 0;
			}

			STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0;
			STREAM->ConnectTime = time(NULL); 

			if (Stream == 0)
			{
				// Stop Scanner

				char Msg[80];
				
				sprintf(Msg, "%d SCANSTOP", TNC->Port);
		
				Rig_Command(-1, Msg);

				sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", 
					TNC->Streams[0].BytesRXed, TNC->Streams[0].BytesTXed, TNC->Streams[0].BytesAcked);
				
				SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC);
			}

			if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0)
			{
				// Incoming Connect

				TRANSPORTENTRY * SESS;

				if (Msg[1] == '2' && Msg[2] == 'A')
					TNC->HFPacket = TRUE;

				ProcessIncommingConnect(TNC, Call, Stream, TRUE);

				SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream];

				if (Stream == 0)
				{
					struct WL2KInfo * WL2K = TNC->WL2K;
					char FreqAppl[10] = "";				// Frequecy-specific application
	
					if (TNC->RIG && TNC->RIG != &TNC->DummyRig)
					{
						sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->NodeCall, TNC->RIG->Valchar);
						SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500;		// Convert to Centre Freq
			
						// If Scan Entry has a Appl, save it

						if (TNC->RIG->FreqPtr && TNC->RIG->FreqPtr[0]->APPL[0])
							strcpy(FreqAppl, &TNC->RIG->FreqPtr[0]->APPL[0]);
					}
					else
					{
						sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->NodeCall);
				
						if (WL2K)
						{
							SESS->Frequency = WL2K->Freq;
						}
					}

					SESS->Mode = 11;			// P1
					
					if (WL2K)
						strcpy(SESS->RMSCall, WL2K->RMSCall);		
				
					SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

					EncodeAndSend(TNC, "T", 1);			// Changeover to ISS 

					// If an autoconnect APPL is defined, send it	

					if (FreqAppl[0])			// Frequency spcific APPL overrides TNC APPL
					{
						buffptr = GetBuff();
						if (buffptr == 0) return;			// No buffers, so ignore

						buffptr->Len = sprintf(buffptr->Data, "%s\r", FreqAppl);
						C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
						TNC->SwallowSignon = TRUE;
						return;
					}

					if (TNC->ApplCmd)
					{
						buffptr = GetBuff();
						if (buffptr == 0) return;			// No buffers, so ignore

						buffptr->Len = sprintf(buffptr->Data, "%s\r", TNC->ApplCmd);
						C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);

						TNC->SwallowSignon = TRUE;
						return;
					}
				}

				if (FULL_CTEXT && HFCTEXTLEN == 0)
				{
					char CTBuff[300] = "D20";
					int Len = CTEXTLEN, CTPaclen = 50;
					int Next = 0;

					if (Stream > 0)
						sprintf(CTBuff, "D1%c", Stream + '@');

					while (Len > CTPaclen)		// CTEXT Paclen
					{
						memcpy(&CTBuff[3], &CTEXTMSG[Next], CTPaclen);
						EncodeAndSend(TNC, CTBuff, CTPaclen + 3);
						Next += CTPaclen;
						Len -= CTPaclen;
					}

					memcpy(&CTBuff[3], &CTEXTMSG[Next], Len);
					EncodeAndSend(TNC, CTBuff, Len + 3);
					EncodeAndSend(TNC, "E", 1);			// Changeover when all sent
					TNC->Streams[0].BytesTXed += CTEXTLEN;
				}
				return;

			}
			else
			{
				// Connect Complete
			
				buffptr = GetBuff();
				if (buffptr == 0) return;			// No buffers, so ignore

				buffptr->Len  = sprintf(buffptr->Data, "*** Connected to %s\r", Call);;

				C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
	
				STREAM->Connecting = FALSE;
				STREAM->Connected = TRUE;			// Subsequent data to data channel

				
				if (Stream == 0)
				{
					if (TNC->RIG)
						sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar);
					else
						sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);

					SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

					UpdateMH(TNC, Call, '+', 'O');

				}

				return;
			}
		}
	}
}


static int	KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
	int i,txptr=0;
	UCHAR c;

	for (i=0;i<len;i++)
	{
		c=inbuff[i];

		if (c == FESC)
		{
			c=inbuff[++i];
			{
				if (c == TFESC)
					c=FESC;
				else
				if (c == TFEND)
					c=FEND;
			}
		}

		outbuff[txptr++]=c;
	}

	return txptr;

}

VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len)
{
	// Send A Packet With KISS Encoding

	TNC->TXLen = KissEncode(txbuffer, TNC->TXBuffer, Len);
	WriteCommBlock(TNC);
}

static int	KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
	int i,txptr=0;
	UCHAR c;

	outbuff[0]=FEND;
	txptr=1;

	for (i=0;i<len;i++)
	{
		c=inbuff[i];
		
		switch (c)
		{
		case FEND:
			outbuff[txptr++]=FESC;
			outbuff[txptr++]=TFEND;
			break;

		case FESC:

			outbuff[txptr++]=FESC;
			outbuff[txptr++]=TFESC;
			break;

		default:

			outbuff[txptr++]=c;
		}
	}

	outbuff[txptr++]=FEND;

	return txptr;

}

static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len)
{
	// Update HM list and maube pass to monitor somehow

	UCHAR * ptr;

	ptr = strchr(&Msg[3], '>');

	if (ptr) *(ptr) = 0;

	UpdateMH(TNC, &Msg[3], ' ', 0);

}
VOID TidyClose(struct TNCINFO * TNC, int Stream)
{
	if (Stream == 0)					// Pactor Stream
	{
		TNC->TimeInRX = 0;
		if (TNC->HFPacket)
			EncodeAndSend(TNC, "C2AD", 4);		// Disconnect
		else
			EncodeAndSend(TNC, "X", 1);			// ??Return to packet mode??
//			EncodeAndSend(TNC, "C20TOR", 6);		// Disconnect

		TNC->HFPacket = FALSE;
	}
	else
	{
		UCHAR TXMsg[10];

		sprintf(TXMsg, "C1%cD", Stream + '@');
		EncodeAndSend(TNC, TXMsg, 4);
		TNC->Timeout = 50;
	}
}

VOID ForcedClose(struct TNCINFO * TNC, int Stream)
{
	if (Stream == 0)					// Pactor Stream
	{
		TNC->TimeInRX = 0;

		if (TNC->HFPacket)
			EncodeAndSend(TNC, "C2AD", 4);		// Disconnect
		else
			EncodeAndSend(TNC, "X", 1);			// ??Return to packet mode??

		TNC->HFPacket = FALSE;
	}
	else
	{
		UCHAR TXMsg[10];

		sprintf(TXMsg, "C1%cD", Stream + '@');
		EncodeAndSend(TNC, TXMsg, 4);		// Send twice - must force a disconnect
		TNC->Timeout = 50;
	}}

VOID CloseComplete(struct TNCINFO * TNC, int Stream)
{
		TNC->NeedPACTOR = 50;	
}



/*


MARK 1400
SPACE 1600
SHIFT MODEM
INV ON
MAXUSERS 1/10
PTERRS 30

	// Others go on end so they can't be overriden

ECHO OFF
XMITECHO ON
TXFLOW OFF
XFLOW OFF
TRFLOW OFF
AUTOCR 0
AUTOLF OFF
CRADD OFF
CRSUP OFF
CRSUP OFF/OFF
LFADD OFF/OFF
LFADD OFF
LFSUP OFF/OFF
LFSUP OFF
RING OFF
ARQBBS OFF

MYCALL 

*/
