#include "dialup.h"

extern const UBYTE 			*prgname;
extern struct IOExtSer		*serialIOReq;
extern struct timerequest	*timereq;
extern BPTR					repfile;
extern UBYTE				*rxbuffer;
extern LONG					arg[];

BOOL	isend( UBYTE *send );			/* real send with \r, \n and \t remapping, string must be in a bigger buffer */

UBYTE *
expectFromSer( UBYTE *expect, USHORT waittime )
	{
	UBYTE *result = NULL;
	ULONG WaitMask, timebit, serbit, wres;

	WaitMask = 0;

	if ( waittime )
		{
		if ( arg[A_VERBOSE] && !expect ) msg("waiting %ld seconds\n", expect, waittime);
		timebit = 1L << timereq->tr_node.io_Message.mn_ReplyPort->mp_SigBit;
		SetSignal(0L, timebit);
		WaitMask |= SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F | timebit;
		timereq->tr_node.io_Command = TR_ADDREQUEST;
		timereq->tr_time.tv_secs	= waittime;
		timereq->tr_time.tv_micro	= 0;
		SendIO((struct IORequest *)timereq);
		if ( !expect )
			{
			while ( ! ( (wres = Wait(WaitMask)) & WaitMask ) ); /* Wait for the timeout, no string expected! */
			if ( wres & timebit ) { *rxbuffer = '\0'; result = rxbuffer; };
			};
		};

	if ( expect )
		{
		UBYTE *wptrb;
		UBYTE *repptr;

		UBYTE mbuf[MAXMATCHSTRING * 25];
		UBYTE *msa[MAXMATCHSTRING];

		if ( strlen(expect) > sizeof(mbuf) )
			{
			msg("expectFromSer: expectstring to long!");
			_FAIL_;
			};

		if ( arg[A_VERBOSE] ) msg("expecting %s for %ld seconds\n", expect, waittime);

		buildMatchStringArray(expect, mbuf, msa);

		serbit =  1L << serialIOReq->IOSer.io_Message.mn_ReplyPort->mp_SigBit;
		SetSignal(0L, serbit);
		WaitMask |= SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F | serbit;
		wptrb = repptr = rxbuffer;
		wres = 0;
		do	{
			UBYTE *spt;	

			serialIOReq->IOSer.io_Command  = SDCMD_QUERY;
			DoIO((struct IORequest *)serialIOReq);
			serialIOReq->IOSer.io_Command  = CMD_READ;
			serialIOReq->IOSer.io_Data	   = (APTR)wptrb;
			/* while there are chars on the serial, set and get them (upto bufsize): */
			if ( ( serialIOReq->IOSer.io_Length = min(serialIOReq->IOSer.io_Actual, RXBUFSIZE - (wptrb - rxbuffer)) ) > 0 )
				{
				DoIO((struct IORequest *)serialIOReq);
				wptrb += serialIOReq->IOSer.io_Actual;
				continue;
				}
			*wptrb = '\0'; /* Terminate received string and replace any received \0's with spaces, to make a stringsearch possible: */
			while ( spt = memchr(repptr, '\0', wptrb - repptr ) ) memmove(spt, spt + 1, wptrb-- - spt);

			/* Report the conversation. The modem echos commands sent to it: */
			if ( repfile && wptrb > repptr ) { Write( repfile, repptr, wptrb - repptr); repptr = wptrb; };

			/* check whether one of the matchstrings is in the receive buffer. Stop and return it if so. */
			if ( result = matchString(rxbuffer, msa) ) break;

			/* wait for one char, break or timeout: */
			serialIOReq->IOSer.io_Length = 1;
			SendIO((struct IORequest *)serialIOReq);
			while ( ! ( (wres = Wait(WaitMask)) & WaitMask ) );
			if ( wres & serbit || wres & SIGBREAKF_CTRL_F )
				{
				if(CheckIO((struct IORequest *)serialIOReq) )	/* If request is complete... */
					{
					WaitIO((struct IORequest *)serialIOReq);	/* clean up and remove reply */
					wptrb += serialIOReq->IOSer.io_Actual;
					*wptrb = '\0';								/* Terminate received string */
					}
				}
			else if ( (wres & timebit) )
				{
				UBYTE *cbptr;
				UBYTE *rbptr;
				UBYTE ccnt;
				for(rbptr = rxbuffer, cbptr = wptrb + 1; ( rbptr < wptrb ) && ((cbptr + 20) < (rxbuffer + RXBUFSIZE)); rbptr += 8)
					{
					cbptr += sprintf(cbptr, "%08lX %08lX  ", *(ULONG *)rbptr, *(ULONG *)(rbptr + 4) );
					for(ccnt = 0; rbptr[ccnt] && ccnt < 8; ccnt++)
						if ( strchr("\n\r\t", rbptr[ccnt] ) ) rbptr[ccnt] = 1;
					cbptr += sprintf(cbptr, "%-.4s ", rbptr);
					if (rbptr + 4 < wptrb) cbptr += sprintf(cbptr, "%-.4s\n", rbptr + 4 );
					};
				msg("TIMEOUT, after waiting %ld secs for:\n'%s'\nReceived:\n%s", waittime, expect, wptrb + 1);
				}
			}
		while ( ! ( wres & ( SIGBREAKF_CTRL_C | timebit ) ) );

		if ( wres & SIGBREAKF_CTRL_C ) { strcpy(wptrb, BREAKSTR); wptrb += sizeof(BREAKSTR); };

		if( !CheckIO((struct IORequest *)serialIOReq) ) /* If request is complete... */
			{
			AbortIO((struct IORequest *)serialIOReq);  /* Ask device to abort request, if pending */
			WaitIO((struct IORequest *)serialIOReq);   /* clean up and remove reply */
			if ( SetSignal(0, 0) & serbit ) SetSignal(0, serbit );
			}

		if ( repfile && (wptrb > repptr) )
			{
			Write( repfile, repptr, wptrb - repptr); /* Report the conversation, cause normaly modem echos commands sent to it. */
			repptr = wptrb;
			};
		};

	if( waittime && !CheckIO((struct IORequest *)timereq) ) /* Abort any pending timer requests */
		{
		AbortIO((struct IORequest *)timereq);
		WaitIO((struct IORequest *)timereq);
		if ( SetSignal(0, 0) & timebit ) SetSignal(0, timebit );
		}

	return( result );
	}

#define SCRATCHBUFSIZE 1000

BOOL
sendLine( UBYTE *send )
	{
	UBYTE buf[SCRATCHBUFSIZE];

	if ( strlen(send) >= SCRATCHBUFSIZE - 1 ) _FAIL_
	sprintf(buf, "%s\r", send);
	return(isend(buf));
	}

UBYTE *
sendEx( UBYTE *send , UBYTE *expect, USHORT waittime )
	{
	UBYTE buf[SCRATCHBUFSIZE];

	if ( strlen(send) >= SCRATCHBUFSIZE - 1 ) _FAIL_
	sprintf(buf, "%s\r", send);
	if ( !isend(buf) ) return(FALSE);
	return( expectFromSer(expect, waittime) );
	}

UBYTE *
exSend( UBYTE *expect, USHORT waittime, UBYTE *send )
	{
	UBYTE *rxbufp;

	if ( strlen(send) >= SCRATCHBUFSIZE - 1 ) _FAIL_
	if (rxbufp = expectFromSer(expect, waittime) )
		{
		UBYTE buf[SCRATCHBUFSIZE];
		sprintf(buf, "%s\r", send);
		if ( !isend(buf) ) return(NULL);
		}
	return(rxbufp);
	}

BOOL
sendToSer( UBYTE *send )
	{
	UBYTE buf[SCRATCHBUFSIZE];

	if ( strlen(send) >= SCRATCHBUFSIZE ) _FAIL_
	strcpy(buf, send);	/* copying string to be able modify it */
	return(isend(send));
	}

BOOL
isend( UBYTE *send )		/* this gets only called by sendEx(), exSend() and sendToSer() */
	{
	UBYTE *mptr = send;

	while ( *mptr != '\0' )
		{
		if ( *mptr++ == '\\' )
			{
			if      ( *mptr == 'r' ) *mptr = '\r';
			else if ( *mptr == 'n' ) *mptr = '\n';
			else if ( *mptr == 't' ) *mptr = '\t';
			memmove(mptr - 1, mptr, strlen(mptr) + 1);
			};
		};

	if ( arg[A_VERBOSE] ) msg("Sending %s\n", send);

	Delay(2);
	serialIOReq->IOSer.io_Command  = CMD_WRITE;
	serialIOReq->IOSer.io_Data	   = (APTR)send;
	serialIOReq->IOSer.io_Length   = strlen(send);
	if ( DoIO( (struct IORequest *)serialIOReq ) )
		{
		msg("failed to write to serial line\n");
		return(FALSE);
		}
	return(TRUE);
	}


/*
** The next lines are stolen from the slip.device (Rhialto Seibert, Comodore)
**
** ReadConfig
**
** Attempt to read in and parse the driver's configuration file.
**
** The files are named by ENV:SANA2/slip0.config where X is the decimal
** representation of the device's unit number.
**
*/

#define LINEBUFFSIZE 1024
#define NUMARGS 10

BOOL ReadConfig(STRPTR sanacfgfile, USHORT sanaUnit, struct SP *sp)
	{
	UBYTE *linebuff;
	UBYTE buff[80] = "ENV:SANA2/";
	UBYTE *termchar;
	struct RDArgs *rdargs;
	BPTR ConfigFile;
	LONG arg[NUMARGS];
	BOOL status = FALSE;
	ULONG linenum=0;
	UWORD i;

	sprintf(buff + ( strchr(sanacfgfile, ':') ? 0 : strlen(buff) ), sanacfgfile, (ULONG)sanaUnit);
	if(ConfigFile = Open(buff,MODE_OLDFILE))
		{
		if(linebuff = AllocMem(LINEBUFFSIZE, MEMF_CLEAR|MEMF_PUBLIC))
			{
			if(rdargs = AllocDosObject(DOS_RDARGS, NULL))
				{
				while( FGets( ConfigFile, linebuff, LINEBUFFSIZE - 1) )
					{
					linenum++;
					if(linebuff[0] == '#') /* Skip comment lines */ continue;

					rdargs->RDA_Source.CS_Buffer = linebuff;
					rdargs->RDA_Source.CS_Length = LINEBUFFSIZE;
					rdargs->RDA_Source.CS_CurChr = 0;

					/* ReadArgs() requires that the line be null-terminated or funny things happen. */
					termchar = (UBYTE *) linebuff + strlen(linebuff);
					*termchar = '\n';
					termchar++;
					*termchar = 0;

					for(i = 0; i < NUMARGS ; i++) arg[i] = NULL;
					if( ReadArgs( "SERNAME/A,SERUNIT/A/N,SERBAUD/A/N,IPSTR/A,CD=CARRIERDETECT/S,7WIRE/S,EOFMODE/S,MTU/K/N,THEREST/F", arg, rdargs ) )
						{
						status = TRUE;
						strcpy( sp->serDevName,	(UBYTE *)arg[0] );
						sp->serUnit			= *((ULONG *)arg[1]);
						sp->serBaudRate		= *((ULONG *)arg[2]);
						sp->listen2CD		=			 arg[4] ? TRUE : FALSE;
						sp->serHWHS			=			 arg[5] ? TRUE : FALSE;
						FreeArgs(rdargs);
						break;
						}
					else
						{
						msg("Error parsing sana arguments in %s", buff);
						break;
						}
					}
				FreeDosObject(DOS_RDARGS, rdargs);
				}
			FreeMem(linebuff, LINEBUFFSIZE);
			}
		Close(ConfigFile);
		}
	return(status);
	}


VOID
msg(UBYTE *msg, ...)
	{
	extern BPTR	mystderr;
	va_list args;

	va_start(args, msg);
 	if(mystderr)
		{
		VFPrintf(mystderr, msg, args);
		VFPrintf(mystderr, "\n", NULL);
		}
	else
		{
		struct Library *IntuitionBase;
		static struct EasyStruct es;

		if(IntuitionBase = OpenLibrary("intuition.library", 37))
			{
			es.es_StructSize=sizeof(struct EasyStruct);
			es.es_Flags = 0;
			es.es_Title = prgname;
			es.es_TextFormat = msg;
			es.es_GadgetFormat = "OK";
			EasyRequestArgs(NULL, &es, 0, args);
			CloseLibrary(IntuitionBase);
			};
		}; 
	va_end(args);
	}

BOOL
carrier()
	{
	/* modem online? */
	serialIOReq->IOSer.io_Command  = SDCMD_QUERY;
	if ( DoIO((struct IORequest *)serialIOReq) ) msg("Error: Query serial line.");
	return( (BOOL)( (serialIOReq->io_Status & ( 1 << 5 ) ) == 0 ) );
	}

