/*
**
**	$Id: am.c,v 1.2 1995/10/07 02:52:18 chris Exp $
**	$Revision: 1.2 $
**
**	$Filename: developer/am.c $
**	$Author: chris $
**	$Date: 1995/10/07 02:52:18 $ 
**	$Portability: ANSI $
**
**	Ein kleiner Anrufbeantworter, setzt auf CAPI 2.0 auf.
**
**	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF RELOG AG.
**
**	COPYRIGHT (C) 1992-1996 BY RELOG AG, ZUERICH. ALL RIGHTS RESERVED.
**	NO PART OF THIS SOFTWARE MAY BE COPIED, REPRODUCED, OR TRANSMITTED
**	IN ANY FORM OR BY ANY MEANS,  WITHOUT THE PRIOR WRITTEN PERMISSION
**	OF RELOG AG.
**
*/

#include <exec/types.h>
#include <proto/exec.h>
#include <dos/dos.h>

#include <stdio.h>
#include <string.h>

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

#define  _AM
#include "am.h"
#include "arexx.h"
#include "bchanneljobs.h"
#include "process.h"


static struct AnsweringMachine	am =
{
	1,						/* Controller */
	"8",					/* MSN */
	"limmathotline.amrx",	/* REXX-Script */
};


/*
**	Locals
*/
static U16	applID;
static U32	capiproc;
static BOOL	running = TRUE;


/****************************************************************************
**	Klein- nach Grossbuchstaben wandeln. $$$ Kann keine Umlaute
*/

static U8 ToUpper( U8 c )
{
	return (U8)(((c >= 'a') && (c <= 'z')) ? c + 'A' - 'a' : c);
}


/****************************************************************************
**	Send a CAPI message to the CAPI driver
*/

U32
SendCAPIMessage( CAPI_MESSAGE *msg, U8 *end )
{
	msg->TotalLength = end - (U8 *)msg;
	return U_CAPI_PUT_MESSAGE( applID, msg );
}


/****************************************************************************
**	Aus einem Array von CAPI-`struct's einen Zeiger auf die <num>te
**	struct (d.h. auf das Längenbyte) zurückgeben
*/

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;
}


/****************************************************************************
**	Add a null-terminated string as a struct to a CAPI message
*/

static VOID AddStructString( U8 **paraptr, U8 *string )
{
	U8	*para = *paraptr;
	int	len = strlen( string );

	if (len < 0xFF)
	{
		ADD_PARA( para, U8, len );
	}
	else
	{
		ADD_PARA( para, U8, 0xFF );	/* Escape-Zeichen für 16Bit-Länge */
		ADD_PARA( para, U16, len );
	}

	strcpy( para, string );

	*paraptr = para + len;
}


/****************************************************************************
**	Testen, ob eine MSN auf ein am-Entity passt. `msn' ist dabei ein
**	CAPI struct, mit Längenbyte im ersten Byte.
*/

static BOOL CheckMSN( struct AnsweringMachine *am, U8 *ist )
{
	if (ToUpper( am->MSN[0] ) != 'X')	/* `Alle MSNs akzeptieren' ? */
	{
		U8 *p1, *p2, len = (U8)strlen( am->MSN );

		/* Ende der Called Party Number im CAPI struct */
		p1 = ist + ist[0] + 1;

		/* Ende der MSN in unserer Konfiguration */
		p2 = am->MSN + len;

		/*
		**	Es werden nur soviele Zeichen verglichen,
		**	wie die kürzere der beiden Nummern enthält.
		**	Dadurch kann der Benutzer beispielsweise
		**	statt seiner EAZ die ganze Nummer eingeben,
		**	und ein Anruf wird trotzdem richtig erkannt.
		*/
		if (len > ist[0])
			len = ist[0];

		while (len)
		{
			if (*--p1 != *--p2)
				return FALSE;	/* Falsche MSN */
			len--;
		}
	}

	return TRUE;	/* Nummer stimmt überein */
}


/****************************************************************************
**	Ein Zeichen aus dem Keybuffer lesen und zurückgeben
*/

static U8 GetKey( struct AnsweringMachine *am )
{
	USES_DISABLE
	U8 key = '-';		/* "-" heisst "keine Taste gedrückt" */

	DISABLE;
	if (am->KeyBufferSize > 0)
	{
		key = am->KeyBuffer[0];
		memmove( am->KeyBuffer, am->KeyBuffer+1, am->KeyBufferSize );
		am->KeyBufferSize--;
	}
	ENABLE;

	return key;
}


/****************************************************************************
**	Alle Zeichen aus dem Keybuffer entfernen
*/

static VOID FlushKeyBuffer( struct AnsweringMachine *am )
{
	am->KeyBufferSize = 0;
}


/****************************************************************************
**	Auf ein Zeichen einer Auswahl warten. Falls keys ein leerer Strig
**	ist, wartet die Funktion auf eine beliebige Taste. Falls keys das
**	Zeichen '-' enthält, kehrt diese Funktion zurück, sobald ein SIGF_
**	SINGLE empfangen wurde, sonst wartet sie.
**	timeout ist in Sekunden. 0 = kein Timeout.
*/

static U8 WaitKeys( struct AnsweringMachine *am, U8 *keys, S32 timeout )
{
	S32 doomsday;

	for (doomsday = 0;; doomsday++)
	{
		U32	signals = CheckSignal( SIGBREAKF_CTRL_C | SIGF_SINGLE );
		U8	key;

		if (timeout != 0)
		{
			if (doomsday >= (timeout * 4))
			{
				printf( "WaitKeys(): timeout reached.\n" );
				return '-';
			}
		}

		if (signals & SIGBREAKF_CTRL_C)
			return '-';		/* $$$ Macht das Sinn ? */

		if (signals & SIGF_SINGLE)
			if (strchr( keys, '-' ))
				return '-';

		if ((key = GetKey( am )) != '-')
		{
			if ((keys[0] == '\0') || (strchr( keys, key ) != NULL))
				return key;
		}
		else Delay( TICKS_PER_SECOND / 4 );
	}
}


/****************************************************************************
**	REXX-Kommando: Background <file> <volume> [LOOP]
*/

S32 RXFunc_Background( char *args, struct AnsweringMachine *am )
{
	char filename[PATHNAMESIZE];

	if (ARexx_GetString( &args, filename, sizeof( filename ) ))
	{
		S32 volume;

		if (ARexx_GetInteger( &args, &volume ))
		{
			char	loop[16];
			U16		jobflags = JOBF_WRITE | JOBF_AUTOFREE;

			loop[0] = '\0';
			if (ARexx_GetString( &args, loop, sizeof( loop ) ))
			{
				if (!stricmp( loop, "LOOP" ))
					jobflags |= JOBF_LOOP;
			}

			printf( "Background: file='%s', volume=%ld, loop='%s'\n", filename, volume, loop );

			if (volume > 100)
				volume = 100;
			if (volume < 0)
				volume = 0;

			volume = (volume * B_VOLUME_MAX) / 100;

			if (am->BGJob != NULL)
			{
				BChannel_FreeJob( am->BGJob );
/*				am->BGJob = NULL; */
			}

			if (am->BGJob = BChannel_AddJob( am->BChannel, filename, jobflags, (U16)volume, am->Process ))
				return RC_OK;

			return RC_WARN;
		}
	}

	return RC_FATAL;
}


/****************************************************************************
**	REXX-Kommando: Fade <endvolume>
*/

S32 RXFunc_Fade( char *args, struct AnsweringMachine *am )
{
	S32 volume;

	if (ARexx_GetInteger( &args, &volume ))
	{
		BOOL waiting = TRUE;
		USES_DISABLE

		printf( "Fade: volume=%ld\n", volume );

		if (volume > 100)
			volume = 100;
		if (volume < 0)
			volume = 0;

		volume = (volume * B_VOLUME_MAX) / 100;

		DISABLE;
		if (am->BGJob != NULL)
			am->BGJob->EndVolume = (U16)volume;
		ENABLE;

		while (waiting)
		{
			DISABLE;
			if (am->BGJob != NULL)
			{
				if (am->BGJob->ActVolume == am->BGJob->EndVolume)
					waiting = FALSE;
			}
			else
			{
				waiting = FALSE;
			}
			ENABLE;
			Delay( TICKS_PER_SECOND );
		}

		return RC_OK;
	}
	return RC_FATAL;
}


/****************************************************************************
**	REXX-Kommando: Hangup [cause]
*/

S32 RXFunc_HangUp( char *args, struct AnsweringMachine *am )
{
	struct
	{
		CAPI_MESSAGE	Msg;
		U8				Para[8];
	} disconnect_b3_req;

	U8 *para = disconnect_b3_req.Para;

	S32 cause = 16;		/* NORMAL CALL CLEARING */

	ARexx_GetInteger( &args, &cause );

	FlushKeyBuffer( am );
	printf( "Hangup: cause=%ld\n", cause );

	disconnect_b3_req.Msg.Command		= CAPICMD_DISCONNECT_B3;
	disconnect_b3_req.Msg.Subcommand	= CAPISUBCMD_REQ;

	ADD_PARA( para, U32, am->NCCI );		/* NCCI des aktuellen Calls */
	ADD_PARA( para, U8, 0 );				/* NCPI brauchen wir nicht */

	SendCAPIMessage( &disconnect_b3_req.Msg, para );

	return RC_OK;
}


/****************************************************************************
**	REXX-Kommando: Play <file> <volume> [keys]
*/

S32 RXFunc_Play( char *args, struct AnsweringMachine *am )
{
	char filename[PATHNAMESIZE];

	if (ARexx_GetString( &args, filename, sizeof( filename ) ))
	{
		S32 volume;

		if (ARexx_GetInteger( &args, &volume ))
		{
			struct BChannel_Job	*job;
			char				keys[20];

			keys[0] = '\0';
			if (ARexx_GetString( &args, keys, sizeof( keys ) ))
			{
			}

			printf( "Play: file='%s', volume=%ld, keys='%s'\n", filename, volume, keys );

			if (volume > 100)
				volume = 100;
			if (volume < 0)
				volume = 0;

			volume = (volume * B_VOLUME_MAX) / 100;

			FlushKeyBuffer( am );
			if (job = BChannel_AddJob( am->BChannel, filename, JOBF_WRITE, (U16)volume, am->Process ))
			{
				rexx_resultstring[0] = WaitKeys( am, keys, 0 );
				rexx_resultstring[1] = '\0';

				BChannel_FreeJob( job );
				return RC_OK;
			}

			return RC_WARN;
		}
	}

	return RC_FATAL;
}


/****************************************************************************
**	REXX-Kommando: Record <file> <maxtime> [keys]
*/

S32 RXFunc_Record( char *args, struct AnsweringMachine *am )
{
	char filename[PATHNAMESIZE];

	if (ARexx_GetString( &args, filename, sizeof( filename ) ))
	{
		S32 maxtime;

		if (ARexx_GetInteger( &args, &maxtime ))
		{
			struct BChannel_Job	*job;
			char				keys[20];

			keys[0] = '\0';
			if (ARexx_GetString( &args, keys, sizeof( keys ) ))
			{
			}

			printf( "Record: file='%s', maxtime=%ld, keys='%s'\n", filename, maxtime, keys );

			FlushKeyBuffer( am );
			if (job = BChannel_AddJob( am->BChannel, filename, JOBF_READ, B_VOLUME_MAX, am->Process ))
			{
				rexx_resultstring[0] = WaitKeys( am, keys, maxtime );
				rexx_resultstring[1] = '\0';

				BChannel_FreeJob( job );
				return RC_OK;
			}

			return RC_WARN;
		}
	}

	return RC_FATAL;
}


/****************************************************************************
**	REXX-Kommando: WaitKey
*/

S32 RXFunc_WaitKey( char *args, struct AnsweringMachine *am )
{
	printf( "WaitKey\n" );

	FlushKeyBuffer( am );

	rexx_resultstring[0] = WaitKeys( am, "", 0 );
	rexx_resultstring[1] = '\0';

	return RC_OK;
}


/****************************************************************************
**	LISTEN_REQ an CAPI abschicken
*/

static VOID ListenReq( U8 controller )
{
	struct
	{
		CAPI_MESSAGE	Msg;
		U8				Para[32];					/* $$$ Rough guess */
	} listen_req;

	U8 *para = listen_req.Para;

	listen_req.Msg.Command		= CAPICMD_LISTEN;
	listen_req.Msg.Subcommand	= CAPISUBCMD_REQ;

	ADD_PARA( para, U32, controller );				/* Controller */

	ADD_PARA( para, U32, 0xFFFFFFFF );				/* Info mask: alles */

	ADD_PARA( para, U32,							/* CIP mask: Nur Gespräche */
			  (1UL << CAPI_CIP_SPEECH)
			| (1UL << CAPI_CIP_AUDIO31)
			| (1UL << CAPI_CIP_TELEPHONY) );

	ADD_PARA( para, U32, 0 );						/* CIP mask 2: reserviert */
	ADD_PARA( para, U8, 0 );						/* Calling party number */
	ADD_PARA( para, U8, 0 );						/* Calling party subaddress */

	SendCAPIMessage( &listen_req.Msg, para );
}


/****************************************************************************
**	Touchtone-Dekodierung einschalten (79)
*/

static VOID TouchtoneDecoder( U32 ncci, BOOL onoff )
{
	struct
	{
		CAPI_MESSAGE	Msg;
		U8				Para[32];					/* $$$ Rough guess */
	} facility_req;

	U8 *para = facility_req.Para;

	facility_req.Msg.Command	= CAPICMD_FACILITY;
	facility_req.Msg.Subcommand	= CAPISUBCMD_REQ;

	ADD_PARA( para, U32, ncci );
	ADD_PARA( para, U16, 1 );				/* Facility Selector: DTMF */

	ADD_PARA( para, U8,	7 );				/* Struct length */
	ADD_PARA( para, U16, onoff ? 1 : 2 );	/*  Function: Start/Stop DTMF listen */
	ADD_PARA( para, U16, 40 );				/*  Time in ms for one digit (ignored) */
	ADD_PARA( para, U16, 40 );				/*  Time in ms between digits (ignored) */
	ADD_PARA( para, U8, 0 );				/*  Struct digits (ignored) */

	SendCAPIMessage( &facility_req.Msg, para );
}


/****************************************************************************
**	Eine CAPI-Message behandeln
*/

static BOOL HandleCAPIMessage( struct AnsweringMachine *am, CAPI_MESSAGE *msg )
{
	UBYTE 	*para = (UBYTE *)(msg+1);
	BOOL	running = TRUE;

	switch (msg->Subcommand)
	{
		case CAPISUBCMD_CONF:
			switch (msg->Command)
			{
				/*
				**	DATA_B3_CONF (Seite 33)
				*/
				case CAPICMD_DATA_B3:
				{
					U32 ncci;
					U16 handle;

					GET_PARA( para, U32, ncci );
					GET_PARA( para, U16, handle );

					BChannel_DataB3Conf( ncci );
				}
					break;


				/*
				**	LISTEN_CONF (Seite 54)
				*/
				case CAPICMD_LISTEN:
				{
					U32 controller;
					U16 info;

					GET_PARA( para, U32, controller );
					GET_PARA( para, U16, info );

					if (info != CAPI_00_REQUEST_ACCEPTED)
					{
						printf( "LISTEN_REQ failed (error 0x%04x)\n", info );
						running = FALSE;
					}
				}
					break;


				/*
				**	FACILITY_CONF (Seite 45)
				*/
				case CAPICMD_FACILITY:
				{
					U32 ncci;
					U16 info;

					GET_PARA( para, U32, ncci );
					GET_PARA( para, U16, info );

					if (info != CAPI_00_REQUEST_ACCEPTED)
					{
						printf( "FACILITY_REQ for ncci 0x%08lx failed (error 0x%04x)\n", ncci, info );
						running = FALSE;
					}
				}
					break;


				default:
					INTERNAL_ERROR;
					break;

			}
			break;




		case CAPISUBCMD_IND:
			msg->Subcommand = CAPISUBCMD_RESP;		/* Für Antwort-Messages */

			switch (msg->Command)
			{
				/*
				**	CONNECT_IND (Seite 19)
				*/
				case CAPICMD_CONNECT:
				{
					U32	plci;
					U16	cip;
					U16	reject = CAPI_REJECT_IGNORE;

					GET_PARA( para, U32, plci );
					GET_PARA( para, U16, cip );

					if (CheckMSN( am,  GetStruct( para, 0 )))
					{
						if (am->PState == P0_IDLE)
						{
							U8 *str;

							am->PLCI	= plci;
							am->PState	= P2_CONNECT_INDICATION;


							/*
							**	Calling Party Number übernehmen. Die ersten
							**	zwei Bytes der struct enthalten Type of Number
							**	und Presentation Indicator, und werden ignoriert.
							*/
							str = GetStruct( para, 1 );
							if (str[0] >= 2)	/* Falls Nummer vorhanden */
							{
								memcpy( am->CallingNumber, str + 1 + 2, str[0] - 2 );
								am->CallingNumber[str[0] - 2] = '\0';
							}
							else
								am->CallingNumber[0] = '\0';

							printf( "Got a call from %s\n", am->CallingNumber );

							reject = CAPI_REJECT_ACCEPT;
							am->PState = P4_CALL_ACCEPTED;
						}
					}


					/*
					**	Entsprechende CONNECT_RESP-Message vorbereiten
					*/
					para = (U8 *)(msg+1) + 4;		/* PLCI übernehmen */
					ADD_PARA( para, U16, reject );

					ADD_PARA( para, U8, 9 );						/* B Protocol: Struct-Länge */
					ADD_PARA( para, U16, CAPI_B1PROT_TRANSP64 );	/* B1 protocol */
					ADD_PARA( para, U16, CAPI_B2PROT_TRANSP );		/* B2 protocol */
					ADD_PARA( para, U16, CAPI_B3PROT_TRANSP );		/* B3 protocol */
					ADD_PARA( para, U8, 0 );						/* B1 configuration: leer */
					ADD_PARA( para, U8, 0 );						/* B2 configuration: leer */
					ADD_PARA( para, U8, 0 );						/* B3 configuration: leer */

					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 */
				}
					break;


				/*
				**	CONNECT_ACTIVE_IND (Seite 22)
				*/
				case CAPICMD_CONNECT_ACTIVE:
				{
					U32	plci;

					GET_PARA( para, U32, plci );
					am->PState = P_ACTIVE;
				}
					break;


				/*
				**	CONNECT_B3_IND (Seite 28)
				*/
				case CAPICMD_CONNECT_B3:
				{
					U32	ncci;

					GET_PARA( para, U32, ncci );
/*					GET_PARA( para, U32, ncpi ); */

					am->NCCI = ncci;

					if ((am->BChannel = BChannel_New( ncci )) != NULL)
					{
					}

					ADD_PARA( para, U16, CAPI_REJECT_ACCEPT );
					ADD_PARA( para, U8, 0);		/* Kein NCPI */

				}
					break;


				/*
				**	CONNECT_B3_ACTIVE_IND (Seite 24)
				*/
				case CAPICMD_CONNECT_B3_ACTIVE:
				{
					char buf[256];
					U32	ncci;

					GET_PARA( para, U32, ncci );
					printf( "B-channel activated.\n" );

					am->Process = Process_Create( NULL, am, "AM-REXX", 2 );
					Process_WakeUp( am->Process );

					TouchtoneDecoder( ncci, TRUE );


					/*
					**	PARSE arg callednumber callingnumber
					*/
					sprintf( buf, "limmathotline.amrx %s %s", "?", am->CallingNumber );

					printf( "Launching Rexx: %s\n", buf );
					ARexx_LaunchScript( Process_GetRexxPort( am->Process ), buf );
				}
					break;


				/*
				**	DATA_B3_IND (Seite 34)
				*/
				case CAPICMD_DATA_B3:
				{
					U32	ncci;
					U8	*data;
					U16	size, handle, flags;

					GET_PARA( para, U32,  ncci );
					GET_PARA( para, U8 *, data );
					GET_PARA( para, U16,  size );
					GET_PARA( para, U16,  handle );
					GET_PARA( para, U16,  flags );

					BChannel_DataB3Ind( ncci, data, size );

					para = (U8 *)(msg+1) + 4;		/* Nach NCCI */
					ADD_PARA( para, U16, handle );
				}
					break;


				/*
				**	DISCONNECT_IND (Seite 42)
				*/
				case CAPICMD_DISCONNECT:
				{
					U32	plci;
					U16	reason;

					GET_PARA( para, U32, plci );
					GET_PARA( para, U16, reason );

					if (am->BChannel != NULL)
					{
						BChannel_Free( am->BChannel );
						am->BChannel = NULL;
					}

					am->NCCI		= 0;

					am->PState		= P0_IDLE;
					am->PLCI		= 0;

					Process_Delete( am->Process );

					para = (U8 *)(msg+1) + 4;	/* DISCONNECT_RESP hat nur PLCI */
					printf( "Connection terminated.\n" );
				}
					break;


				/*
				**	DISCONNECT_B3_IND (Seite 38)
				*/
				case CAPICMD_DISCONNECT_B3:
				{
					U32	ncci;
					U16	reason_b3;

					GET_PARA( para, U32, ncci );
					GET_PARA( para, U16, reason_b3 );

					am->NCCI = 0;

					Process_Delete( am->Process );

					if (am->BChannel != NULL)
					{
						BChannel_Free( am->BChannel );
						am->BChannel = NULL;
					}

					para = (U8 *)(msg+1) + 4;	/* DISCONNECT_B3_RESP hat nur NCCI */
					printf( "B-channel deactivated.\n" );
				}
					break;


				/*
				**	FACILITY_IND
				*/
				case CAPICMD_FACILITY:
				{
					U32	ncci;
					U16 type;

					GET_PARA( para, U32, ncci );
					GET_PARA( para, U16, type );

					switch (type)
					{
						case 1:		/* DTMF */
						{
							U8 numkeys, key;

							GET_PARA( para, U8, numkeys );

							while (numkeys)
							{
								USES_DISABLE
								char buf[PATHNAMESIZE];

								GET_PARA( para, U8, key );

								/*
								**	Key an Keybuffer anhängen
								*/
								DISABLE;
								if (am->KeyBufferSize < sizeof( am->KeyBuffer ))
								{
									am->KeyBuffer[am->KeyBufferSize] = key;
									am->KeyBufferSize++;
								}
								ENABLE;

								if ((key >= '0') && (key <= '9'))
								{
									sprintf( buf, "Ansagen/Keys/KEY-%lc", (U32)key );
									BChannel_AddJob( am->BChannel, buf, JOBF_WRITE, B_VOLUME_MAX, 0 );
								}
								numkeys--;
							}
						}
							break;

						default:
							printf( "Unknown facility %ld\n", (U32)type );
							break;
					}
				}
					break;


				/*
				**	INFO_IND
				*/
				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 */

					para = (U8 *)(msg+1) + 4;	/* INFO_RESP: Nur PLCI */
				}
					break;


				default:
					INTERNAL_ERROR;
					break;
			}


			/*
			**	Vorbereitete Message (normalerweise Antwortmessage auf _IND) senden.
			**	Para zeigt hier auf das Ende der zu sendenden Message.
			*/
			SendCAPIMessage( msg, para );
			break;
	}

	return running;
}


/****************************************************************************
**	Diese Funktion wird vom CAPI-Treiber aufgerufen, wenn eine neue
**	CAPI-Message bereitliegt. Sie weckt einfach unseren CAPI-Prozess.
*/

static void __saveds MySignalHandler( unsigned short applID, unsigned long para )
{
	Process_WakeUp( para );
}


/****************************************************************************
**	Diese Funktion wird vom CAPI-Prozess aufgerufen, wenn er aufgeweckt
**	wurde.
*/

static BOOL __saveds CAPIProcFunc( VOID *para )
{
	CAPI_MESSAGE *msg;

	while (U_CAPI_GET_MESSAGE( applID, &msg ) == 0)
	{
		running = HandleCAPIMessage( para, msg );
	}

	return TRUE;
}


/****************************************************************************
**	Das Hauptprogramm
*/

int main( int argc, char **argv )
{
	U16	error;


	if (!((argc == 1) || ((argc == 2) && strcmp( argv[1], "?")) || (argc > 2)))
	{
		printf( "Usage: %s [number]\n", argv[0] );
		return 10;
	}


	/*
	**	Als erstes finden wir raus, ob in diesem Rechner überhaupt ein
	**	CAPI-Treiber installiert ist.
	*/
	if (U_CAPI_INSTALLED())
	{
		printf( "No CAPI driver installed!\n" );
		return 10;
	}


	/*
	**	CAPI initialisieren. Dies muss ganz am Anfang geschehen, bevor
	**	irgendwelche CAPI-Funktionen (ausser U_CAPI_INSTALLED) aufgerufen
	**	werden.
	*/
	if ((error = U_CAPI_REGISTER( 2, 2, B_BLOCKSIZE, &applID )))
	{
		printf( "Can't initialize CAPI, error 0x%lx\n", error );
		return 10;
	}

	ARexx_Init();
	ALAW_Init();


	/*
	**	Infos über den CAPI-Treiber ausgeben
	*/
	{
		U32	ver[4];
		U8	manu[64];

		U_CAPI_GET_VERSION( ver );
		U_CAPI_GET_MANUFACTURER( manu );

		printf( "Driver: %s\nVersion %ld.%ld (internal revision %ld.%ld)\n\n",
			manu, ver[0], ver[1], ver[2], ver[3] );
	}


	/*
	**	CAPI-Handler-Prozess und Callback-Funktion initialisieren.
	*/
	if (capiproc = Process_Create( CAPIProcFunc, &am, "AM-CAPI", 15 ))
	{
		U_CAPI_SET_SIGNAL( applID, (ULONG)MySignalHandler, capiproc );

		ListenReq( am.Controller );

		Wait( SIGBREAKF_CTRL_C );

		Process_Delete( capiproc );
		capiproc = (U32)NULL;
	}
	else
	{
		printf( "Can't create CAPI handler process\n" );
	}


	ALAW_Exit();
	ARexx_Exit();

	/*
	**	CAPI schliessen.
	*/
	U_CAPI_RELEASE( applID );

	return 0;
}

