#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;
			SetSignal(0L, serbit);
			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'; /* overwrite it insitu */
		    else if ( *mptr == 'n' )
			     *mptr = '\n'; /* ditto */
		    else if ( *mptr == 't' )
			     *mptr = '\t'; /* ditto */
        /* then get rid of the '\\' by left shifting the string */
		    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);
}


/* new function to handle WAKEUPSTRING -GH- */
BOOL
sendWUSSer( UBYTE *send )
{
	UBYTE buf[SCRATCHBUFSIZE];

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


BOOL
wuSend( UBYTE *send )	/* this gets only called by sendWUSSer() -GH- */
{
	UBYTE *mptr = send;
        UBYTE kick;

	while ( *mptr != '\0' )
	{
		if ( *mptr++ == '\\' )
		{ /* added to implement a delay mechanism by Gene Heskett */
                    if ( *mptr == '*' )
                        Delay(15); /* 1/4 second delay */
                    else if ( *mptr == 'r' )
		    {
                        kick = '\r';
                        kickport (& kick );
                    }
		    else if ( *mptr == 'n' )
                    {
                        kick = '\n';
                        kickport ( & kick );
                    }
		    else if ( *mptr == 't' )
                    {
                        kick = '\t';
                        kickport ( & kick );
                    }
		    memmove(mptr - 1, mptr, strlen(mptr) + 1);
		};
	};
        return TRUE;
} /* this is the new end of this routine -GH- */


BOOL
kickport ( UBYTE *send ) /* called *only* by wuSend */
{
	if ( arg[A_VERBOSE] )
		msg("Sending %s\n", send);

	serialIOReq->IOSer.io_Command   = CMD_WRITE;
	serialIOReq->IOSer.io_Data	= (APTR)send;
	serialIOReq->IOSer.io_Length    = 1;
	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? */

	if (!serialIOReq)
		return(FALSE);

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

