#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <proto/dos.h>
#include <libraries/dos.h>

#include "os.h"
#include "capi-usr.h"

#include "MyCAPI.h"


/*
** einige in ETSI definierte Konstanten
*/
#define D_ALERTING					0x01
#define D_CALL_PROCEEDING			0x02
#define D_SETUP_ACKNOWLEDGE		0x0D
#define I_Cause						0x08
#define I_ChannelIdentification	0x18
#define I_Facility					0x1C
#define I_DateTime					0x29


/*
** ist das richtig ???
*/
#define I_Charging					0x4001


/*
** Text-Highlighting
*/
#define HI_ON	"\x1b[32m"
#define HI_OFF	"\x1b[31m"
#define NAMELEN 50
#define MAXLINELEN 512

/*
** Externals
*/
extern BOOL debugcapi;
extern U8 *actioncmd;
extern U8 *phonebook;
extern U8   outputbuff[];
extern struct Task *maintask;
extern VOID myoutput(U8 *, ...);
extern FILE *phonebookfile;


/*
** Locals
*/
static U16 ApplID;
static U8  manufacturer[64];
static U32 version[4];
static U8  serialnumber[8];


/*
** Code
*/

/****************************************************************************
**	Ausgaben aus dem CAPI-Callback heraus sollten wir tunlichst
** vermeiden (Device-Context !). Daher der Weg ueber eine globale
** Variable und ein Signal.
*/
static VOID out(BOOL flag, U8 *fmt, ...)
{
	va_list arg;

	if (flag) {
		va_start(arg, fmt);
		vsprintf(outputbuff, fmt, arg);
		va_end(arg);
		Signal( maintask, SIGF_SINGLE );
	}
}


/****************************************************************************
**	Aus einem Array von CAPI-`struct's einen Zeiger auf die <num>te
** struct (d.h. auf das Laengenbyte) zurueckgeben
*/

static U8 *GetStruct( U8 *ptr, int num )
{
	while (num > 0) {
		if (*ptr == 0xFF)
			ptr += (GET_U16(ptr+1) + 3);
		else
			ptr += *ptr + 1;
		num--;
	}
	return ptr;
}


/****************************************************************************
**	Eine CAPI-Message an den CAPI Treiber schicken
*/
static U32 SendCAPIMessage( U16 applID, CAPI_MESSAGE *msg, U8 *end )
{
	msg->TotalLength = end - (U8 *)msg;
	return (U32)U_CAPI_PUT_MESSAGE( applID, msg );
}


/*****************************************************************************
** Ermittelt eine evtl. uebertragene CallingNumber
*/
static VOID GetCallingPartyName(U8 *name, U8 *number)
{
	U8 rline[MAXLINELEN];
	U8 number1[NAMELEN+2];
	U8 number2[NAMELEN+4];
	U8 index;
	U8 index2;
	U8 anfang;
	U8 ende;
	
	BOOL status;
	
	if(number[0]=='\0')
	{
		name[0]='\0';
		return;
	}
	
	number1[0]='P';
	number1[1]='=';
	number1[2]='\0';
	
	number2[0]='P';
	number2[1]='=';
	number2[2]='"';
	number2[3]='\0';
	
	strcat(number1,number);
	strcat(number2,number);
	
	strcat(number2,"\"");

	name[0]='\0';
	
	if (phonebook != NULL)
	{
		if (phonebookfile = fopen(phonebook,"r"))
		{
			while ((fgets(rline, MAXLINELEN, phonebookfile) != NULL) && (name[0] == '\0'))
			{
				if ((strstr(rline,number1) != NULL) || (strstr(rline,number2) != NULL))
				{
					status=FALSE;
					anfang=0;
					ende=0;
					for(index=0;index<=strlen(rline);index++)
					{
						switch(rline[index])
						{
						case '"' :
							status = (status==TRUE) ? FALSE : TRUE;
							break;
						case '=' :
							if ((rline[index-1] == 'N') && (status==FALSE))
							{
								index++;
								anfang = (rline[index] == '"') ? index+1 : index;
								while(!((rline[index] == ' ') && (status == FALSE)) && !((rline[index] == '"') && (status == TRUE)) && !(rline[index] == '\0'))
								{
									if(rline[index]=='"')
									{
										status = (status==TRUE) ? FALSE : TRUE;
									}
									index++;
								}
								ende = (rline[index-1]=='"') ? index-1 : index;
								ende = ((ende-anfang)>=(NAMELEN-1)) ? (anfang+NAMELEN-1) : ende;
								for(index2=anfang;index2<ende;index2++)
								{
									name[index2-anfang]=rline[index2];
								}
								name[index2-anfang]='\0';
							}
							break;
						}
					}
				}
			}
			fclose(phonebookfile);
		}
	}
	return;
}


/*****************************************************************************
** Ermittelt eine evtl. uebertragene CallingNumber
*/
static VOID GetCallingPartyNumber(U8 *p, U8 *callingparty)
{
	U8 len, type, plan, presentation;

	if ((len = callingparty[0]) > 1) {

		/*
		** Number Type ermitteln
		*/
		type = (callingparty[1] & 0x70) >> 4;

		/*
		** Numbering plan ermitteln
		*/
		plan = (callingparty[1] & 0x0F);

		/*
		** Presentation and Screening Indicator ermitteln
		*/
		presentation = callingparty[2] & 0x7F;

		switch (type) {
			case 0x00:			 /* unknown type */
				break;
			case 0x01:			 /* international number */
				break;
			case 0x02:			 /* national number */
				*p++ = '0';
				break;
			case 0x03:			 /* network specific number */
				break;
			case 0x04:			 /* subsciber number */
				break;
			case 0x06:			 /* abbreviated number */
				break;
			default:			 /* reserved */
				break;
		}
		strncpy(p, &callingparty[3], len-2);
		*(p + len - 2) = '\0';
	} else
		strcpy(p, "<Analog>");
}


/*****************************************************************************
** Ermittelt eine evtl. uebertragene CalledNumber
*/
static VOID GetCalledPartyNumber(U8 *p, U8 *calledparty)
{
	strncpy(p, &calledparty[2], calledparty[0]-1);
	p[calledparty[0]-1] = '\0';
}


/*****************************************************************************
** Unser (zugegeben einfacher) CAPI-Signalhandler
** (komplexere Applikationen muessen natuerlich auch andere Messages
** auswerten und darauf antworten; wir haben es hier einfacher, da
** nur LISTEN_CONF, CONNECT_IND, DISCONNECT_IND und INFO_IND kommen
** koennen)
** data ist der bei U_CAPI_SET_SIGNAL uebergebene Parameter (bei
** uns hier NULL)
*/
static VOID __saveds SignalHandler( U16 applIDdummy, U32 data )
{

	CAPI_MESSAGE *rmsg;
	U32 error;
	BPTR nullfh1, nullfh2;
	nullfh1 = nullfh2 = NULL;

	for (;;) {
		U8 *para;

		/*
		** CAPI-Messages vom Treiber abholen und bearbeiten
		*/
		error = U_CAPI_GET_MESSAGE( ApplID, &rmsg );
		if (error == 0) {
			if (rmsg) {

				/*
				** Auf Parameterblock setzen
				*/
				para = INIT_PARA( rmsg );
				switch (rmsg->Subcommand) {

					case CAPISUBCMD_CONF:
						switch (rmsg->Command) {

							case CAPICMD_LISTEN:
								out(debugcapi, "LISTEN_CONF");
								break;

							default:
								out(debugcapi, "Unknown CONF <0x%04x>", rmsg->Command);
								break;
						}
						break;

					case CAPISUBCMD_IND:
						switch (rmsg->Command) {

							case CAPICMD_CONNECT:
								{
									U32 plci;
									U16 cip;
									U16 resp = CAPI_REJECT_IGNORE;
									U8  *calledparty;
									U8  *callingparty;
									U8  number[CAPI_MAX_NUMBER_SIZE];
									U8  cnumber[CAPI_MAX_NUMBER_SIZE];
									U8	name[NAMELEN];

									GET_PARA( para, U32, plci );
									GET_PARA( para, U16, cip );
									out(debugcapi, "CONNECT_IND 0x%08lx 0x%04x", plci, cip);
									calledparty = GetStruct( para, 0 );
									callingparty = GetStruct( para, 1 );
									GetCallingPartyNumber(number, callingparty);
									GetCalledPartyNumber(cnumber, calledparty);
									GetCallingPartyName(name, number);
									
									out(TRUE, "%s %s %s", cnumber, number, name);
									if (actioncmd != NULL)
									{
										U8 cnta;
										U8 tmp;
										U8 cntc=0;
										U8 cmd[MAXLINELEN];
										
										for(cnta=0;cnta<strlen(actioncmd);cnta++)
										{
											if((actioncmd[cnta]=='%') && (cnta<(strlen(actioncmd)-1)))
											{
												switch(actioncmd[cnta+1])
												{
												case 's':
													cmd[cntc]='\0';
													tmp=48+(cip/10000)%10;
													strcat(cmd,&tmp);
													tmp=48+(cip/ 1000)%10;
													strcat(cmd,&tmp);
													tmp=48+(cip/  100)%10;
													strcat(cmd,&tmp);
													tmp=48+(cip/   10)%10;
													strcat(cmd,&tmp);
													tmp=48+(cip/    1)%10;
													strcat(cmd,&tmp);
													cntc=cntc+5;
													break;
												case 'c':
													cmd[cntc]='\0';
													strcat(cmd,number);
													cntc=cntc+strlen(number);
													break;
												case 'm':
													cmd[cntc]='\0';
													strcat(cmd,cnumber);
													cntc=cntc+strlen(cnumber);
													break;
												case 'n':
													cmd[cntc]='\0';
													strcat(cmd,name);
													cntc=cntc+strlen(name);
													break;
												default:
													cmd[cntc]='%';
													cntc++;
													cmd[cntc]=actioncmd[cnta+1];
													cntc++;
													break;
												}
												cnta++;
											}
											else
											{
												cmd[cntc]=actioncmd[cnta];
												cntc++;
											}
										}
										cmd[cntc]='\0';
										(void)Execute(cmd,nullfh1,nullfh2);
									} 

									/*
									** Antwort-Message zusammenfuegen
									*/
									para = INIT_PARA( rmsg ) + 4;	/* PLCI uebernehmen */
									ADD_PARA( para, U16, resp );
									ADD_PARA( para, U8, 0 );		/* Struct BProtocol */
									ADD_PARA( para, U8, 0 );		/* Struct ConnectedNumber */
									ADD_PARA( para, U8, 0 );		/* Struct ConnectedSubaddress */
									ADD_PARA( para, U8, 0 );		/* Struct LLC */
									ADD_PARA( para, U8, 0 );		/* Struct AdditionalInfo */
									rmsg->Subcommand = CAPISUBCMD_RESP;
									out(debugcapi, "CONNECT_RESP(0x%04x) --> 0x%04lx", resp, error);
									error = SendCAPIMessage( ApplID, rmsg, para );
								}
								break;

							case CAPICMD_DISCONNECT:
								{
									U32 plci;
									U16 reason;

									GET_PARA( para, U32, plci );
									GET_PARA( para, U16, reason );
									out(debugcapi, "DISCONNECT_IND");

									/*
									** Antwort-Message zusammenfuegen
									*/
									para = INIT_PARA( rmsg ) + 4;				/* PLCI uebernehmen */
									rmsg->Subcommand = CAPISUBCMD_RESP;
									out(debugcapi, "DISCONNECT_RESP --> 0x%04lx", error);
									error = SendCAPIMessage( ApplID, rmsg, para );
								}
								break;

							case CAPICMD_INFO:
								{
									U32 plci;
									U16 infonumber;
									U8  infosize;

									GET_PARA( para, U32, plci );
									GET_PARA( para, U16, infonumber );
									GET_PARA( para, U8, infosize );			/* Grösse der Info-Struct */
									if (infonumber & 0x8000) {			/* Bits 7..0 sind Message-Type */
										switch (infonumber & 0xFF) {
											case D_ALERTING:
												out(debugcapi, "INFO_IND D_Alerting");
												break;
											case D_CALL_PROCEEDING:
												out(debugcapi, "INFO_IND D_CallProceeding");
												break;
											case D_SETUP_ACKNOWLEDGE:
												out(debugcapi, "INFO_IND D_SetupAcknowledge");
												break;
											default:
												out(debugcapi, "INFO_IND UNKNOWN MESSAGE");
												break;
										}
									} else {									/* Bits 7..0 sind Info-Element-Type */
										if (infonumber == I_Charging) {
											U32 cost;

											GET_PARA( para, U32, cost);
											out(debugcapi, "INFO_IND I_Charging %ld", cost);
										} else {
											switch (infonumber & 0xFF) {
												case I_ChannelIdentification:
													{
														U8 channelinfo;

														GET_PARA( para, U8, channelinfo );
														out(debugcapi, "INFO_IND I_ChannelIdentification 0x%02x", channelinfo);
													}
													break;

												case I_Cause:
													{
														U8 cause;

														GET_PARA( para, U8, cause );
														out(debugcapi, "INFO_IND I_Cause 0x%02x", cause);
													}
													break;

												case I_DateTime:
													{
														U8 year, month, day;
														U8 hour, minute;

														GET_PARA( para, U8, year );
														GET_PARA( para, U8, month );
														GET_PARA( para, U8, day );
														GET_PARA( para, U8, hour );
														GET_PARA( para, U8, minute );
														out(debugcapi, "INFO_IND I_DateTime %02d.%02d.%4d %02d:%02d", day, month, year, hour, minute);
													}
													break;

												case I_Facility:
													out(debugcapi, "INFO_IND I_Facility");
													break;

												default:
													out(debugcapi, "INFO_IND Unknown INFOELEMENT");
													break;
											}
										}
									}

									/*
									** Antwort-Message zusammenfuegen
									*/
									para = INIT_PARA( rmsg ) + 4;   /* INFO_RESP: Nur PLCI */
									rmsg->Subcommand = CAPISUBCMD_RESP;
									out(debugcapi, "INFO_RESP : 0x%04lx", error);
									error = SendCAPIMessage( ApplID, rmsg, para );
								}
								break;

							default:
								out(debugcapi, "Unknown IND <0x%04x>", rmsg->Command);
								break;
						}
						break;

					default:
						out(debugcapi, "Unknown SUBCMD <0x%04x>", rmsg->Subcommand);
						break;
				}
			} else {
				out(debugcapi, "Empty Message !!!");
				break;
			}
		} else {
			if (error != CAPI_11_MESSAGE_QUEUE_EMPTY)
				out(debugcapi, "U_CAPI_GET_MESSAGE --> %04lx", error);
			break;
		}
	}
}


/*****************************************************************************
** CAPI initialisieren
*/
BOOL MYCAPI_Init(U32 ctrlnum)
{
	CAPI_PROFILE	profile;
	U32				error;
	U32				infomask = 0x0000007F;	/* alles was definiert ist */
	U32				cipmask =  0xFFFFFFFF;  /* alle Anruftypen anzeigen
	/*(1L<<CAPI_CIP_SPEECH) | (1L<<CAPI_CIP_AUDIO31);*/

	BOOL				ret = FALSE;
	/*U8  number[CAPI_MAX_NUMBER_SIZE];
	U8 name[NAMELEN]; */

	/*
	** Erst mal schauen, ob ctrlnum innerhalb des legalen Bereichs liegt,
	** und mindestens ein Controller vorhanden ist.
	*/

	/*number[0]='0';
	number[1]='1';
	number[2]='9';
	number[3]='9';
	number[4]='4';
	number[5]='1';
	number[6]='8';
	number[7]='3';
	number[8]='1';
	number[9]='\0';
	
	GetCallingPartyName(name, number);
	myoutput("%s",name);*/
	error = (U32)U_CAPI_GET_PROFILE( &profile, 0 );
	if (!error) {
		if ((profile.NumControllers != 0) && (ctrlnum > 0) && (ctrlnum <= profile.NumControllers)) {

			/*
			** beim CAPI als Applikation registrieren
			*/
			error = (U32)U_CAPI_REGISTER( CAPICONNECTIONS, CAPIBUFFERS, CAPIBLOCKSIZE, &ApplID);
			if (!error) {
				struct {
					CAPI_MESSAGE Msg;
					U8 Para[32];
				} listen_req;
				U8 *para = listen_req.Para;

				/*
				** CAPI-Signalhandler einbinden
				*/
				error = (U32)U_CAPI_SET_SIGNAL( ApplID, (U32)SignalHandler, (U32)NULL );
				if (!error) {

					U_CAPI_GET_MANUFACTURER( manufacturer );
					U_CAPI_GET_VERSION( version );
					U_CAPI_GET_SERIAL_NUMBER( serialnumber );

					/* myoutput("Display CallerID  (C) 1996 by ith GmbH");
					myoutput("%s", manufacturer); */

					if (debugcapi)
						myoutput("started logging (ApplID:0x%04x)", ApplID);

					/*
					** LISTEN_REQ fuer den entsprechenden Controller abschicken.
					*/
					listen_req.Msg.Command		= CAPICMD_LISTEN;
					listen_req.Msg.Subcommand	= CAPISUBCMD_REQ;
					ADD_PARA( para, U32, ctrlnum );		/* Controller */
					ADD_PARA( para, U32, infomask );		/* Info mask  */
					ADD_PARA( para, U32, cipmask );		/* CIP mask   */
					ADD_PARA( para, U32, 0 );				/* CIP mask 2: reserviert		*/
					*para++ = 0;								/* Calling party number			*/
		   	   *para++ = 0;								/* Calling party subaddress	*/
		   	   if (debugcapi)
						myoutput("LISTEN_REQ(0x%08lx, 0x%08lx, 0x%08lx) --> 0x%04lx", ctrlnum, infomask, cipmask, error);
					error = SendCAPIMessage( ApplID, &listen_req.Msg, para );
					ret = TRUE;

				} else
					if (debugcapi)
						myoutput("U_CAPI_SET_SIGNAL() --> 0x%04lx", error);
			} else
				if (debugcapi)
					myoutput("U_CAPI_REGISTER() --> 0x%04lx", error);
		} else
			if (debugcapi)
				myoutput("Controllernummer nicht im gültigen Bereich.");
	} else
		if (debugcapi)
			myoutput("U_CAPI_GET_PROFILE --> 0x%04lx", error);

	return ret;
}


/*****************************************************************************
** CAPI-Resourcen freigeben
*/
VOID MYCAPI_Exit(VOID)
{
	U32 error;

	error = (U32)U_CAPI_RELEASE( ApplID );
	if (debugcapi)
		myoutput("U_CAPI_RELEASE --> 0x%04lx", error);
}
