/*
Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO

This file is part of QtSoundModem

QtSoundModem 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.

QtSoundModem 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 QtSoundModem.  If not, see http://www.gnu.org/licenses

*/

// UZ7HO Soundmodem Port by John Wiseman G8BPQ

#include "UZ7HOStuff.h"

extern char modes_name[modes_count][20];
extern int RSID_SABM[4];
extern int RSID_UI[4];
extern int RSID_SetModem[4];
extern int needRSID[4];

/*


unit ax25_agw;

interface

uses sysutils,classes;

  void AGW_init;
  void AGW_free;
  void AGW_add_socket(socket integer);
  void AGW_del_socket(socket integer);
  void AGW_send_to_app(socket integer; data string);
  void AGW_AX25_frame_analiz(snd_ch byte; RX boolean; frame string);
  void AGW_frame_analiz(Socket Integer; frame string);
  void AGW_explode_frame(Socket Integer; data string);
  void AGW_AX25_conn(socket integer; snd_ch, mode byte; path string);
  void AGW_AX25_disc(socket integer; snd_ch, mode byte; path string);
  void AGW_AX25_data_in(socket integer; snd_ch,PID byte; path,data string);
  void AGW_Raw_monitor(snd_ch byte; data string);
  void AGW_frame_header(AGWPort,DataKind,PID,CallFrom,CallTo string; Len word) string;
  void AGW_C_Frame(port char; CallFrom,CallTo,Conn_MSG string) string;
  void erase_zero_ssid(call string) string;
  void AGW_get_socket(socket integer) integer;
  void clr_zero(data string) string;

type TAGWUser = record
  socket TStringList;
  AGW_frame_buf TStringList;
  Monitor TStringList;
  Monitor_raw TStringList;
};
*/

void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame);

void AGW_frame_analiz(AGWUser *  AGW);
void AGW_send_to_app(void * socket, string * data);
string * make_frame(string * data, Byte * path, Byte  pid, Byte nr, Byte ns, Byte f_type, Byte f_id, boolean rpt, boolean pf, boolean cr);
void del_incoming_mycalls_by_sock(void * socket);
void del_incoming_mycalls(char * src_call);
void send_data_buf(TAX25Port * AX25Sess, int  nr);
void get_monitor_path(Byte * path, char * mycall, char * corrcall, char * digi);
void decode_frame(Byte * frame, int len, Byte * path, string * data,
	Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id,
	Byte *  rpt, Byte * pf, Byte * cr);


int AGWVersion[2] = {2019, 'B'};		// QTSM Signature

  //ports_info1='1;Port1 with LoopBack Port;

#define    LSB 29
#define    MSB 30
#define    MON_ON '1'
#define    MON_OFF '0'
#define    MODE_OUR 0
#define    MODE_OTHER 1
#define    MODE_RETRY 2


AGWUser ** AGWUsers = NULL;				// List of currently connected clients
int AGWConCount = 0;


struct AGWHeader
{
	int Port;
	unsigned char DataKind;
	unsigned char filler2;
	unsigned char PID;
	unsigned char filler3;
	char callfrom[10];
	char callto[10];
	int DataLength;
	int reserved;
};

#define AGWHDDRRLEN sizeof(struct AGWHeader)


struct AGWSocketConnectionInfo
{
	int Number;					// Number of record - for AGWConnections display
	void *socket;
//	SOCKADDR_IN sin;
	BOOL SocketActive;
	BOOL RawFlag;
	BOOL MonFlag;
	unsigned char CallSign1[10];
	unsigned char CallSign2[10];
	BOOL GotHeader;
	int MsgDataLength;
	struct AGWHeader AGWRXHeader;
};


AGWUser * AGW_get_socket(void * socket)
{
	int i;
	AGWUser * AGW = NULL;

	if (AGWConCount == 0)
		return NULL;

	for (i = 0; i < AGWConCount; i++)
	{
		if (AGWUsers[i]->socket == socket)
		{
			AGW = AGWUsers[i];
			break;
		}
	}
	return AGW;
}


void AGW_del_socket(void * socket)
{
	AGWUser * AGW = AGW_get_socket(socket);
	TAX25Port * AX25Sess = NULL;

	int i = 0;
	int port = 0;

	// Close any connections

	for (port = 0; port < 4; port++)
	{
		for (i = 0; i < port_num; i++)
		{
			AX25Sess = &AX25Port[port][i];

			if (AX25Sess->status != STAT_NO_LINK && AX25Sess->socket == socket)
			{
				rst_timer(AX25Sess);
				set_unlink(AX25Sess, AX25Sess->Path);
			}
		}
	}

	// Clear registrations

	del_incoming_mycalls_by_sock(socket);

	if (AGW == NULL)
		return;

	Clear(&AGW->AGW_frame_buf);
	freeString(AGW->data_in);
	AGW->Monitor = 0;
	AGW->Monitor_raw = 0;
	AGW->reportFreqAndModem = 0;
}



void AGW_add_socket(void * socket)
{
	AGWUser * User = (struct AGWUser_t *)malloc(sizeof(struct AGWUser_t));			// One Client
	memset(User, 0, sizeof(struct AGWUser_t));

	AGWUsers = realloc(AGWUsers, (AGWConCount + 1) * sizeof(void *));
	
	AGWUsers[AGWConCount++] = User;

	User->data_in = newString();
	User->socket = socket;

	User->Monitor = 0;
	User->Monitor_raw = 0;
	User->reportFreqAndModem = 0;
};




void agw_free()
{
 // AGWUser.AGW_frame_buf.Free;
 // AGWUser.Monitor.Free;
 // AGWUser.Monitor_raw.Free;
 // AGWUser.socket.Free;
};

/*
void erase_zero_ssid(call string) string;
var
  p byte;
{
  p = pos('-0',call);
  if (p>0 ) delete(call,p,2);
  result = call;
};
*/

string * AGW_frame_header(char AGWPort, char DataKind, UCHAR PID, char * CallFrom, char * CallTo, int Len )
{
	string * Msg = newString();

	struct AGWHeader * Hddr = (struct AGWHeader *)Msg->Data;
	memset(Hddr, 0, sizeof(struct AGWHeader));

	Hddr->Port = AGWPort;
	Hddr->DataLength = Len;
	Hddr->DataKind = DataKind;
	Hddr->PID = PID;
	strcpy(Hddr->callfrom, CallFrom);
	strcpy(Hddr->callto, CallTo);

	Msg->Length = sizeof(struct AGWHeader);
	return Msg;
};



// AGW to APP frames

string * AGW_R_Frame()
{
	string * Msg = AGW_frame_header(0, 'R', 0, "", "", 8);
	
	stringAdd(Msg, (UCHAR *)AGWVersion, 8);

	return Msg;
};


string * AGW_X_Frame(char * CallFrom,  UCHAR reg_call)
{
	string * Msg = AGW_frame_header(0, 'x', 0, CallFrom, "", 1);

	stringAdd(Msg, (UCHAR *)&reg_call, 1);

	return Msg;

};

string * AGW_G_Frame()
{
	char Ports[256] = "0;";
	char portMsg[64];

	string * Msg;
	
	for (int i = 0; i < 4; i++)
	{
		Ports[0]++;
		if (soundChannel[i])
			sprintf(portMsg, "Port%c with SoundCard Ch %c;", Ports[0], 'A' + i);
		else
			sprintf(portMsg, "Port%c Disabled;", Ports[0]);

		strcat(Ports, portMsg);
	}


	Msg = AGW_frame_header(0, 'G', 0, "", "", strlen(Ports) + 1);

	stringAdd(Msg, (UCHAR *)Ports, strlen(Ports) + 1);

	return Msg;
};



string * AGW_Gs_Frame(int port, Byte * port_info, int Len)
{
	string * Msg;

	Msg = AGW_frame_header(port, 'g', 0, "", "", Len);
	stringAdd(Msg, port_info, Len);
	return Msg;
};


/*
void AGW_Ys_Frame(port char; frame_outstanding string) string;
var
  DataLen word;
{
  DataLen = 4;
  result = AGW_frame_header(port,'y','','','',DataLen)+frame_outstanding;
};

/
void AGW_Y_Frame(port char; CallFrom,CallTo,frame_outstanding string) string;
var
  DataLen word;
{
  DataLen = 4;
  result = AGW_frame_header(port,'Y','',CallFrom,CallTo,DataLen)+frame_outstanding;
};

*/


string * AGW_Y_Frame(int port, char * CallFrom, char *CallTo, int frame_outstanding)
{
	string * Msg;
	
	Msg = AGW_frame_header(port, 'Y', 0, CallFrom, CallTo, 4);

	stringAdd(Msg, (UCHAR *)&frame_outstanding, 4);
	return Msg;
}

/*

void AGW_H_Frame(port char; heard string) string;
var
  DataLen word;
{
  DataLen = length(heard);
  result = AGW_frame_header(port,'H','','','',DataLen)+heard;
};
*/


string *  AGW_C_Frame(int port, char * CallFrom, char * CallTo, string * Conn_MSG)
{
	string * Msg;
	int DataLen;

	DataLen = Conn_MSG->Length;

	Msg = AGW_frame_header(port, 'C', 240, CallFrom, CallTo, DataLen);

	stringAdd(Msg, Conn_MSG->Data, Conn_MSG->Length);

	freeString(Conn_MSG);

	return Msg;
}

string * AGW_Ds_Frame(int port, char * CallFrom, char * CallTo, string * Disc_MSG)
{
	string * Msg;
	int DataLen;
	
	DataLen = Disc_MSG->Length;

	Msg = AGW_frame_header(port, 'd', 240, CallFrom, CallTo, DataLen);

	stringAdd(Msg, Disc_MSG->Data, Disc_MSG->Length);

	freeString(Disc_MSG);

	return Msg;
};


string * AGW_D_Frame(int port, int PID, char * CallFrom, char * CallTo, string * Data)
{
	string * Msg;
	int DataLen;

	DataLen = Data->Length;

	Msg = AGW_frame_header(port, 'D', PID, CallFrom, CallTo, DataLen);

	stringAdd(Msg, Data->Data, Data->Length);

	freeString(Data);

	return Msg;
}



string *  AGW_I_Frame(int port, char * CallFrom, char * CallTo, char * Monitor)
{
	string * Msg;
	int DataLen;

	DataLen = strlen(Monitor);
	Msg = AGW_frame_header(port, 'I', 0, CallFrom, CallTo, DataLen);

	stringAdd(Msg, (Byte *)Monitor, DataLen);
	return Msg;
}

string *  AGW_S_Frame(int port, char * CallFrom, char * CallTo, char * Monitor)
{
	string * Msg;
	int DataLen;

	DataLen = strlen(Monitor);
	Msg = AGW_frame_header(port, 'S', 0, CallFrom, CallTo, DataLen);

	stringAdd(Msg, (Byte *)Monitor, DataLen);
	return Msg;
};

string *  AGW_U_Frame(int port, char * CallFrom, char * CallTo, char * Monitor)
{
	string * Msg;
	int DataLen;

	DataLen = strlen(Monitor);
	Msg = AGW_frame_header(port, 'U', 0, CallFrom, CallTo, DataLen);

	stringAdd(Msg, (Byte *)Monitor, DataLen);
	return Msg;
}


string * AGW_T_Frame(int port, char * CallFrom, char * CallTo, char * Data)
{
	string * Msg;
	int DataLen;

	DataLen = strlen(Data);
	Msg = AGW_frame_header(port, 'T', 0, CallFrom, CallTo, DataLen);

	stringAdd(Msg, (Byte *)Data, DataLen);

	return Msg;
}

// Raw Monitor 
string * AGW_K_Frame(int port, int PID, char * CallFrom, char * CallTo, string * Data)
{
	string * Msg;
	int DataLen;

	DataLen = Data->Length;

	Msg = AGW_frame_header(port, 'K', PID, CallFrom, CallTo, DataLen);

	stringAdd(Msg, Data->Data, Data->Length);

	freeString(Data);

	return Msg;
}

// APP to AGW frames

void on_AGW_P_frame(AGWUser * AGW)
{
	UNUSED(AGW);
}

void on_AGW_X_frame(AGWUser * AGW, char * CallFrom)
{
	Byte reg_call;

	if (add_incoming_mycalls(AGW->socket, CallFrom))
		reg_call = 1;
	else
		reg_call = 0;
 
	AGW_send_to_app(AGW->socket, AGW_X_Frame(CallFrom, reg_call));
}


void on_AGW_Xs_frame(char * CallFrom)
{
	del_incoming_mycalls(CallFrom);
};

void on_AGW_G_frame(AGWUser * AGW)
{
  AGW_send_to_app(AGW->socket, AGW_G_Frame());
};


void on_AGW_Ms_frame(AGWUser * AGW)
{
	AGW->Monitor ^= 1;				// Flip State
}


void on_AGW_R_frame(AGWUser * AGW)
{
  AGW_send_to_app(AGW->socket, AGW_R_Frame());
}

int refreshModems = 0;


void on_AGW_Gs_frame(AGWUser * AGW, struct AGWHeader * Frame, Byte * Data)
{
	// QTSM with a data field is used by QtSM to set/read Modem Params

	Byte info[44] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature
	int Len = 12;

	if (Frame->DataLength == 32)
	{
		// BPQ to QTSM private Format. 

		int Freq;
		Byte versionBytes[4] = VersionBytes;

		AGW->reportFreqAndModem = 1;			// Can report frequency and Modem

		memcpy(&Freq, Data, 4);

		if (Freq)
		{
			// Set Frequency

			memcpy(&rx_freq[Frame->Port], Data, 2);
			refreshModems = 1;
		}

		if (Data[4])
		{
			// New Modem Name. Need to convert to index unless numeric

			int n;
			
			if (strlen(&Data[4]) < 3)
			{
				n = atoi(&Data[4]);
				if (n < modes_count)
				{
					speed[Frame->Port] = n;
					refreshModems = 1;
				}
			}
			else
			{
				for (n = 0; n < modes_count; n++)
				{
					if (strcmp(modes_name[n], &Data[4]) == 0)
					{
						// Found it

						speed[Frame->Port] = n;
						refreshModems = 1;
						break;
					}
				}

			}
		}

		// Return Freq and Modem

		memcpy(&info[12], &rx_freq[Frame->Port], 2);
		memcpy(&info[16], modes_name[speed[Frame->Port]], 20);
		info[37] = speed[Frame->Port];			// Index
		memcpy(&info[38], versionBytes, 4);

		Len = 44;
		AGW_send_to_app(AGW->socket, AGW_Gs_Frame(Frame->Port, info, Len));
		return;
	}
	AGW_send_to_app(AGW->socket, AGW_Gs_Frame(Frame->Port, info, Len));
};
/*
void on_AGW_H_Frame(socket integer; port char);
{
};

void on_AGW_Ys_Frame(socket integer; port char);
var
  snd_ch,i byte;
  info string;
  frames word;
{
  frames = 0;
  //for i = 0 to port_num-1 do frames = frames+AX25port[i].frame_buf.Count;
  snd_ch = ord(Port)+1;
  for i = 0 to port_num-1 do frames = frames+AX25port[snd_ch][i].in_data_buf.Count+AX25port[snd_ch][i].I_frame_buf.Count;
  info = chr(lo(frames))+chr(hi(frames))+#0#0;
  AGW_send_to_app(socket,AGW_Ys_Frame(port,info));
};

*/

void on_AGW_Y_frame(void * socket, int snd_ch, char * CallFrom, char * CallTo)
{
	int  frames;
	TAX25Port * AX25Sess;

	AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo);

	if (AX25Sess)
	{
		//frames = AX25port[snd_ch][user_port].in_data_buf.Count;
		frames = AX25Sess->in_data_buf.Count + AX25Sess->I_frame_buf.Count;
;
		AGW_send_to_app(socket, AGW_Y_Frame(snd_ch, CallFrom, CallTo, frames));
	}
}

// UI Transmit

void on_AGW_M_frame(int port, Byte PID, char * CallFrom, char *CallTo, Byte *  Msg, int MsgLen)
{
	Byte path[80];
	char Calls[80];
	string * Data = newString();

	stringAdd(Data, Msg, MsgLen);

	sprintf(Calls, "%s,%s", CallTo, CallFrom);

	get_addr(Calls, path);

	Add(&all_frame_buf[port],
		make_frame(Data, path, PID, 0, 0, U_FRM, U_UI, FALSE, SET_F, SET_C));

}


void on_AGW_C_frame(AGWUser * AGW, struct AGWHeader * Frame)
{
	int snd_ch = Frame->Port;
	char * CallFrom = Frame->callfrom;
	char * CallTo = Frame->callto;

	char path[128];
	Byte axpath[80];

	TAX25Port * AX25Sess;

	// Also used for 'v' - connect via digis

	AX25Sess = get_free_port(snd_ch);

	if (AX25Sess)
	{
		AX25Sess->snd_ch = snd_ch;

		strcpy(AX25Sess->mycall, CallFrom);
		strcpy(AX25Sess->corrcall, CallTo);

		sprintf(path, "%s,%s", CallTo, CallFrom);


		if (Frame->DataLength)
		{
			// Have digis

			char * Digis = (char *)Frame + 36;
			int nDigis = Digis[0];

			Digis++;

			while(nDigis--)
			{
				sprintf(path, "%s,%s", path, Digis);
				Digis += 10;
			}
		}

		AX25Sess->digi[0] = 0;

//		rst_timer(snd_ch, free_port);

		strcpy(AX25Sess->kind, "Outgoing");
		AX25Sess->socket = AGW->socket;

		AX25Sess->pathLen = get_addr(path, axpath);

		if (AX25Sess->pathLen == 0)
			return;						// Invalid Path

		strcpy((char *)AX25Sess->Path, (char *)axpath);
		reverse_addr(axpath, AX25Sess->ReversePath, AX25Sess->pathLen);

		if (RSID_SABM[snd_ch])			// Send RSID before SABM/UA
			needRSID[snd_ch] = 1;

		set_link(AX25Sess, AX25Sess->Path);
	};
};





void on_AGW_D_frame(int snd_ch, char * CallFrom, char * CallTo, Byte * Msg, int Len)
{
	TAX25Port * AX25Sess;

	AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo);

	if (AX25Sess)
	{
		string * data = newString();

		stringAdd(data, Msg, Len);

		Add(&AX25Sess->in_data_buf, data);

		send_data_buf(AX25Sess, AX25Sess->vs);
	}
}

void on_AGW_Ds_frame(void * socket, int snd_ch, char * CallFrom, char * CallTo)
{
	TAX25Port * AX25Sess;

	AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo);

	if (AX25Sess)
	{
		rst_timer(AX25Sess);

		set_unlink(AX25Sess, AX25Sess->Path);
	}
	else
	{
		string * Msg = newString();

		Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED From Station %s\r", CallTo);
		Msg->Length++;					// Include the terminating NULL

		//del_outgoing_mycalls(CallTo);

		AGW_send_to_app(socket, AGW_Ds_Frame(snd_ch, CallTo, CallFrom, Msg));
	}
}


/*

void on_AGW_Vs_Frame(socket integer; port char; CallFrom,CallTo,Data string);
var
  snd_ch,num_digi,free_port byte;
  need_free_port boolean;
  digi,call,call1 string;
{
  snd_ch = ord(Port)+1;
  num_digi = 0;
  need_free_port = get_free_port(snd_ch,free_port);
  if (need_free_port )
  {
    digi = '';
    get_call(CallFrom,AX25Port[snd_ch][free_port].mycall);
    get_call(CallTo,AX25Port[snd_ch][free_port].corrcall);
    if (length(data)>0 ) { num_digi = ord(data[1]); delete(data,1,1); };
    if ((num_digi in [1..7]) and (length(data)>=num_digi*10) )
    {
      repeat
        call = clr_zero(copy(data,1,10)); delete(data,1,10);
        if (call<>'' )
        {
          get_call(call,call1);
          if (digi='' ) digi = call1
          else digi = digi+','+call1;
        };
      until data='';
      AX25Port[snd_ch][free_port].digi = reverse_digi(digi);
      rst_timer(snd_ch,free_port);
      AX25Port[snd_ch][free_port].kind = 'Outgoing';
      AX25Port[snd_ch][free_port].socket = socket;
      set_link(snd_ch,free_port,CallTo+','+CallFrom);
    };
  };
};

void on_AGW_V_Frame(socket integer; port char; PID,CallFrom,CallTo,Data string);
var
  call,call1,digi,path string;
  snd_ch byte;
  num_digi byte;
  i byte;
{
  digi = '';
  snd_ch = ord(port)+1;
  num_digi = 0;
  if (length(data)>0 ) { num_digi = ord(data[1]); delete(data,1,1); };
  if ((num_digi in [1..7]) and (length(data)>=num_digi*10) )
  {
    for i = 1 to num_digi do
    {
      call = clr_zero(copy(data,1,10)); delete(data,1,10);
      if (call<>'' )
      {
        get_call(call,call1);
        if (digi='' ) digi = call1
        else digi = digi+','+call1;
      };
    };
  };
  path = CallTo+','+CallFrom+','+digi;
  all_frame_buf[snd_ch].Add(make_frame(Data,path,ord(PID[1]),0,0,U_FRM,U_UI,FALSE,SET_F,SET_C));
};

void on_AGW_Cs_Frame(socket integer; port char; PID,CallFrom,CallTo string);
{
};
*/

void on_AGW_K_frame(struct AGWHeader * Frame)
{
	// KISS frame

	unsigned short CRC;
	UCHAR CRCString[2];
	string * TXMSG;

	UCHAR * Data = (UCHAR * )Frame;
	int Len = Frame->DataLength;

	Data = &Data[AGWHDDRRLEN];

	TXMSG = newString();

	stringAdd(TXMSG, Data, Len);		// include Control

	CRC = get_fcs(&Data[1], Len - 1);

	CRCString[0] = CRC & 0xff;
	CRCString[1] = CRC >> 8;

	stringAdd(TXMSG, CRCString, 2);

	Add(&all_frame_buf[Frame->Port], TXMSG);

	// for now assume only used for sending UI

	if (RSID_UI[Frame->Port])			// Send RSID before UI
		needRSID[Frame->Port] = 1;

}

void on_AGW_Ks_frame(AGWUser * AGW)
{
	AGW->Monitor_raw ^= 1;			// Flip State
}

// Analiz incoming frames

void AGW_explode_frame(void * socket, UCHAR * data, int length)
{
	int AGWHeaderLen = sizeof(struct AGWHeader);
	int i;

	AGWUser *  AGW = NULL;

	if (AGWConCount == 0)
		return;

	for (i = 0; i < AGWConCount; i++)
	{
		if (AGWUsers[i]->socket == socket)
		{
			AGW = AGWUsers[i];
			break;
		}
	}

	if (AGW == NULL)
		return;

	stringAdd(AGW->data_in, data, length);

	while (AGW->data_in->Length >= AGWHeaderLen)		// Make sure have at least header
	{
		struct AGWHeader * Hddr = (struct AGWHeader *)AGW->data_in->Data;

		int AgwLen = Hddr->DataLength + AGWHeaderLen;

		if (AgwLen < AGWHeaderLen)						// Corrupt
		{
			AGW->data_in->Length = 0;
			return;
		}

		if (AGW->data_in->Length >= AgwLen)
		{
			// Have frame as well

			if (AGW->data_in->Data[0] == 0xC0)			// Getting KISS Data on AGW Port
			{
				AGW->data_in->Length = 0;				// Delete data
				return;
			}
		
			AGW_frame_analiz(AGW);
			mydelete(AGW->data_in, 0, AgwLen);
		}
		else
			return;					// Wait for the data
	}




	/*
	idx = AGW_get_socket(socket);
	 if (idx>=0 )
	 {
	   AGWUser.AGW_frame_buf.Strings[idx] = AGWUser.AGW_frame_buf.Strings[idx]+data;
	   str_buf = AGWUser.AGW_frame_buf.Strings[idx];
	   repeat
		 done = FALSE;
		 BufLen = length(str_buf);
		 if (BufLen>=HEADER_SIZE )
		 {
		   DataLen = ord(str_buf[LSB])+ord(str_buf[MSB])*256;
		   FrameLen = HEADER_SIZE+DataLen;
		   if (length(str_buf)>=FrameLen )
		   {
			 frame = copy(str_buf,1,FrameLen);
			 delete(str_buf,1,FrameLen);
			 done = TRUE;
			 AGW_frame_analiz(socket,frame);
		   };
		 };
	   until not done;
	   // Check if (socket is still present and has same index
	   if ((AGWUser.socket.Count>idx) and (AGWUser.socket.Strings[idx]=inttostr(socket)) )
		 AGWUser.AGW_frame_buf.Strings[idx] = str_buf;
	 };
	 */
};

/*
void clr_zero(data string) string;
var
  p,i word;
  s string;
{
  s = '';
  p = pos(#0,data);
  if (p>1 ) data = copy(data,1,p-1);
  if (length(data)>0 ) for i = 1 to length(data) do if (data[i]>#31 ) s = s+data[i];
  result = s;
};

void AGW_parse_frame(frame string; var DataKind,PID,AGWPort char; var Pass,CallFrom,CallTo,DataLen,Data string);
{
  DataKind = frame[5];
  PID = frame[7];
  AGWPort = frame[1];
  Pass = '';
  CallFrom = clr_zero(copy(frame,9,10));
  CallTo = clr_zero(copy(frame,19,10));
  get_call(CallFrom,CallFrom);
  get_call(CallTo,CallTo);
  DataLen = inttostr(ord(frame[LSB])+ord(frame[MSB])*256);
  if (length(frame)>HEADER_SIZE ) Data = copy(frame,37,strtoint(DataLen)) else Data = '';
};

*/

void AGW_send_to_app(void * socket, string * data)
{
	char * Msg = malloc(data->Length);
	memcpy(Msg, data->Data, data->Length);
	// can use KISS proc as it just sends to the supplied socket but need copy of message
	KISSSendtoServer(socket, (Byte *)Msg, data->Length);
	freeString(data);
};


void AGW_AX25_data_in(void  * socket, int snd_ch, int PID, Byte * path, string * data)
{
	int len = 250, sendlen;

	char CallFrom[10];
	char CallTo[10];

	string * pkt;

	CallTo[ConvFromAX25(&path[7], CallTo)] = 0;
	CallFrom[ConvFromAX25(path, CallFrom)] = 0;

	while (data->Length)
	{
		if (data->Length > len)
			sendlen = len;
		else
			sendlen = data->Length;

		pkt = copy(data, 0, sendlen);
		mydelete(data, 0, sendlen);

		AGW_send_to_app(socket, AGW_D_Frame(snd_ch, PID, CallFrom, CallTo, pkt));
	}

}

void AGW_AX25_conn(TAX25Port * AX25Sess, int snd_ch, Byte mode)
{
	string * Msg = newString();

	switch (mode)
	{
	case MODE_OTHER:

		Msg->Length = sprintf((char *)Msg->Data, "*** CONNECTED To Station  %s\r", AX25Sess->corrcall);
		break;

	case MODE_OUR:

		Msg->Length = sprintf((char *)Msg->Data, "*** CONNECTED With Station %s\r", AX25Sess->corrcall);
		break;

	};

	Msg->Length++;					// Include the terminating NULL

	AGW_send_to_app(AX25Sess->socket, AGW_C_Frame(snd_ch, AX25Sess->corrcall, AX25Sess->mycall, Msg));
};



void AGW_AX25_disc(TAX25Port * AX25Sess, Byte mode)
{
	string * Msg = newString();

	switch (mode)
	{

	case MODE_OTHER:
	case MODE_OUR:

		Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED From Station %s\r", AX25Sess->corrcall);
		break;

	case MODE_RETRY:

		Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED RETRYOUT With Station %s\r", AX25Sess->corrcall);
		break;

	};

	Msg->Length++;					// Include the terminating NULL

	//del_outgoing_mycalls(CallTo);

	AGW_send_to_app(AX25Sess->socket, AGW_Ds_Frame(AX25Sess->snd_ch, AX25Sess->corrcall, AX25Sess->mycall, Msg));
};


void AGW_frame_monitor(Byte snd_ch, Byte * path, string * data, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, Byte  rpt, Byte pf, Byte cr, Byte RX)
{
	char mon_frm[512];
	char AGW_path[256];
	string * AGW_data = NULL;

	const char * frm;
	Byte * datap = data->Data;
	Byte _data[512];
	Byte * p_data = _data;
	int _datalen;

	char  agw_port;
	char  CallFrom[10], CallTo[10], Digi[80];

	integer i;
	const char * ctrl;
	int len;

	AGWUser * AGW;

	UNUSED(rpt);

	len = data->Length;

	//	if (pid == 0xCF)
	//		data = parse_NETROM(data, f_id);
		// IP parsing
	//	else if (pid == 0xCC)
	//		data = parse_IP(data);
		// ARP parsing
	//	else if (pid == 0xCD)
	//		data = parse_ARP(data);
		//

	if (len > 0)
	{
		for (i = 0; i < len; i++)
		{
			if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9)
				*(p_data++) = datap[i];
		}
	}

	_datalen = p_data - _data;

	if (_datalen)
	{
		Byte * ptr = _data;
		i = 0;

		// remove successive cr or cr on end		while (i < _datalen)

		while (i < _datalen)
		{
			if ((_data[i] == 13) && (_data[i + 1] == 13))
				i++;
			else
				*(ptr++) = _data[i++];
		}

		if (*(ptr - 1) == 13)
			ptr--;

		*ptr = 0;

		_datalen = ptr - _data;
	}

	agw_port = snd_ch;

	get_monitor_path(path, CallTo, CallFrom, Digi);

	ctrl = "";
	frm = "";

	switch (f_id)
	{
	case I_I:

		frm = "I";
		if (cr == SET_C && pf == SET_P)
			ctrl = " P";

		break;

	case S_RR:

		frm = "RR";
		if (pf == SET_P)
			ctrl = " P/F";

		break;

	case S_RNR:

		frm = "RNR";
		if (pf == SET_P)
			ctrl = " P/F";

		break;

	case S_REJ:

		frm = "REJ";
		if (pf == SET_P)
			ctrl = " P/F";

		break;


	case S_SREJ:

		frm = "SREJ";
		if (pf == SET_P)
			ctrl = " P/F";

		break;

	case U_SABM:

		frm = "SABM";
		if (cr == SET_C && pf == SET_P)
			ctrl = " P";

		break;

	case U_DISC:

		frm = "DISC";
		if (cr == SET_C && pf == SET_P)
			ctrl = " P";
		break;

	case U_DM:

		frm = "DM";
		if ((cr == SET_R) && (pf == SET_P))
			ctrl = " F ";
		break;

	case U_UA:

		frm = "UA";
		if ((cr == SET_R) && (pf == SET_P))
			ctrl = " F ";

		break;

	case U_FRMR:

		frm = "FRMR";
		if ((cr == SET_R) && (pf == SET_P))
			ctrl = " F ";
		break;

	case U_UI:

		frm = "UI";
		if ((pf == SET_P))
			ctrl = " P/F";
	}
	
	if (Digi[0])
		sprintf(AGW_path, " %d:Fm %s To %s Via %s <%s", snd_ch + 1, CallFrom, CallTo, Digi, frm);
	else
		sprintf(AGW_path, " %d:Fm %s To %s <%s", snd_ch + 1, CallFrom, CallTo, frm);
	

	switch (f_type)
	{
	case I_FRM:

		//mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13;
		sprintf(mon_frm, "%s%s R%d S%d pid=%X Len=%d >[%s]\r%s\r", AGW_path, ctrl, nr, ns, pid, len, ShortDateTime(), _data);

		break;

	case U_FRM:

		if (f_id == U_UI)
		{
			sprintf(mon_frm, "%s pid=%X Len=%d >[%s]\r%s\r", AGW_path, pid, len, ShortDateTime(), _data); // "= AGW_path + ctrl + '>' + time_now + #13;
		}
		else if (f_id == U_FRMR)
		{
			sprintf(mon_frm, "%s%s>%02x %02x %02x[%s]\r", AGW_path, ctrl, datap[0], datap[1], datap[2], ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13;
		}
		else
			sprintf(mon_frm, "%s%s>[%s]\r", AGW_path, ctrl, ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13;

		break;

	case S_FRM:

		//		mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13;
		sprintf(mon_frm, "%s%s R%d>[%s]\r", AGW_path, ctrl, nr, ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13;

		break;

	}

//	stringAdd(mon_frm, "", 1); //  Add 0 at the end of each frame

	// I think we send to all AGW sockets

	for (i = 0; i < AGWConCount; i++)
	{
		AGW = AGWUsers[i];

		if (AGW->Monitor)
		{
			if (RX)
			{
				switch (f_id)
				{

				case I_I:
					AGW_data = AGW_I_Frame(agw_port, CallFrom, CallTo, mon_frm);
					break;

				case S_RR:
				case S_RNR:
				case S_REJ:
				case S_SREJ:

					AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
					break;

				case U_SABM:
					AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
					break;

				case U_DISC:
					AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
					break;

				case U_DM:
					AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
					break;

				case U_UA:
					AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
					break;

				case U_FRMR:
					AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
					break;

				case U_UI:
					AGW_data = AGW_U_Frame(agw_port, CallFrom, CallTo, mon_frm);
				}
				if (AGW_data)
					AGW_send_to_app(AGW->socket, AGW_data);
			}

			else
			{
				AGW_data = AGW_T_Frame(agw_port, CallFrom, CallTo, mon_frm);
				AGW_send_to_app(AGW->socket, AGW_data);
			}
		}
	}
}

void AGW_Report_Modem_Change(int port)
{
	// Send modem change report to all sockets that support it

	int i;
	AGWUser * AGW;
	string * pkt;

	// I think we send to all AGW sockets

	for (i = 0; i < AGWConCount; i++)
	{
		AGW = AGWUsers[i];

		if (AGW->reportFreqAndModem)
		{
			// QTSM 's' Message with a data field is used by QtSM to set/read Modem Params

			Byte info[44] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature

			// Return Freq and Modem

			memcpy(&info[12], &rx_freq[port], 2);
			memcpy(&info[16], modes_name[speed[port]], 20);
			info[37] = speed[port];			// Index
			AGW_send_to_app(AGW->socket, AGW_Gs_Frame(port, info, 44));
		}
	}
}


void AGW_Raw_monitor(int snd_ch, string * data)
{
	int i;
	AGWUser * AGW;
	string * pkt;

	// I think we send to all AGW sockets

	for (i = 0; i < AGWConCount; i++)
	{
		AGW = AGWUsers[i];

		if (AGW->Monitor_raw)
		{
			pkt = newString();

			pkt->Data[0] = snd_ch << 4;		// KISS Address
			pkt->Length++;

			stringAdd(pkt, data->Data, data->Length - 2);		// Exclude CRC

			AGW_send_to_app(AGW->socket, AGW_K_Frame(snd_ch, 0, "", "", pkt));
		}
	}
}

void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame)
{
	//  path,data string;
	Byte pid, nr, ns, f_type, f_id;
	Byte  rpt, cr, pf;
	Byte path[80];
	string * data = newString();

	decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr);

	AGW_frame_monitor(snd_ch, path, data, pid, nr, ns, f_type, f_id, rpt, pf, cr, RX);
	
//	if (RX)
//		AGW_Raw_monitor(snd_ch, frame);
};


void AGW_frame_analiz(AGWUser *  AGW)
{
	struct AGWHeader * Frame = (struct AGWHeader *)AGW->data_in->Data;
	Byte * Data = &AGW->data_in->Data[36];

	if (Frame->Port < 0 || Frame->Port > 3)
		return;

	if (soundChannel[Frame->Port] == 0)
		return;

	if (Frame->Port > 3)
		return;

	switch (Frame->DataKind)
	{
	case 'P':
		
		on_AGW_P_frame(AGW);
		return;

	case 'X':

		on_AGW_X_frame(AGW, Frame->callfrom);
		return;
		

	case 'x':
		
		on_AGW_Xs_frame(Frame->callfrom);
		return;

	case 'G':
		
		on_AGW_G_frame(AGW);
		return;
		
	case 'm':
		
		on_AGW_Ms_frame(AGW);
		return;

	case 'R':
		
		on_AGW_R_frame(AGW);
		return;
	
	case 'g':
	
		on_AGW_Gs_frame(AGW, Frame, Data);
		return;
//	'H': on_AGW_H_frame(AGW,Frame->Port);
//	'y': on_AGW_Ys_frame(AGW,Frame->Port);

	case 'Y':
		on_AGW_Y_frame(AGW->socket, Frame->Port, Frame->callfrom, Frame->callto);
		break;

	case 'M':
		
		on_AGW_M_frame(Frame->Port,Frame->PID, Frame->callfrom, Frame->callto, Data, Frame->DataLength);
		break;

	case 'C':
	case 'v':				// Call with digis

		on_AGW_C_frame(AGW, Frame);
		return;

	case 'D':
		
		on_AGW_D_frame(Frame->Port, Frame->callfrom, Frame->callto, Data, Frame->DataLength);
		return;
	
	case 'd':
		on_AGW_Ds_frame(AGW->socket, Frame->Port, Frame->callfrom, Frame->callto);
		return;

//	'V': on_AGW_V_frame(AGW,Frame->Port,PID,CallFrom,CallTo,Data);
//	'c': on_AGW_Cs_frame(sAGWocket,Frame->Port,PID,CallFrom,CallTo);


	case 'K':
		
		on_AGW_K_frame(Frame);
		return;

	case 'k':
		on_AGW_Ks_frame(AGW);
		return;

	default:
		Debugprintf("AGW %c", Frame->DataKind);
	}
}