//
//  quick and dirty:
//     dialing with the ZyXEL modem 
//     by: Olaf Mueller <olaf@orest.escape.de>
//

#import "DialOut.h"


#include "device.h"



#define DIALOUT			"cuf"



#define TSLICE			100
#define INTERVAL		((double)TSLICE/1000.0)


static int readRX (int line,RX *rp)
{
	char	buffer [rp->untillength] ;
	int		buflen , ii ;

	/* read bytes immediately */
	ii = fcntl (line,F_GETFL,0);
	fcntl (line,F_SETFL,ii|O_NDELAY) ;
	buflen = read (line,buffer,rp->untillength-rp->untilpos) ;
	fcntl (line,F_SETFL,ii) ;
//printf ("readRX: buflen = %d  space = %d\n",buflen,rp->untillength-rp->untilpos) ;
	if (buflen > 0)
	{
		for (ii = 0 ; ii < buflen ; ii++)
		{
//printf ("readRX: %c (%02x)\n",buffer[ii]<32?' ':buffer[ii],buffer[ii]) ;
			if (rp->answer  &&  rp->answerpos < rp->answerlength)
				*(rp->answer+rp->answerpos++) = buffer[ii] ;
			if (*(rp->until+rp->untilpos) == buffer[ii])
				rp->untilpos++ ;
			else if (*(rp->until) == buffer[ii])
				rp->untilpos = 1 ;
			else
				rp->untilpos = 0 ;
		}
	}
	rp->tcnt += rp->tslice ;
	if (rp->untilpos == rp->untillength)
	{
		if (rp->answer)
			rp->answerpos -= rp->untillength ;
		return  0 ;
	}
	else if (rp->tcnt >= rp->tlimit)
		return  -1 ;
	else
		return  1 ;
}


static void initRX (char *until,long msec,char *answer,int answerlength,RX *rp)
{
	rp->until = until ;
	rp->untillength = strlen (until) ;
	rp->tlimit = msec ;
	rp->tslice = TSLICE ;
	rp->untilpos = rp->answerpos = 0 ;
	rp->tcnt = 0L ;
	if (answer)
		rp->answer = answer ;
	else
		rp->answer = NULL ;
	rp->answerlength = answerlength ;
}


static void dialog (int line,long msec,RX *rp,char *until,char *question,...)
{
	va_list		ap ;
	char		buffer [1000] ;

	va_start (ap,question) ;
	vsprintf (buffer,question,ap) ;
	va_end (ap) ;

	initRX (until,msec,NULL,0,rp) ;
	//printf ("dialog write bytes = %d\n",write(line,buffer,strlen(buffer))) ;
	write (line,buffer,strlen(buffer)) ;
}


static void dialogAnswer (int line,long msec,RX *rp,char *until,char *answer,int answerlength,char *question,...)
{
	va_list		ap ;
	char		buffer [1000] ;

	va_start (ap,question) ;
	vsprintf (buffer,question,ap) ;
	va_end (ap) ;

	initRX (until,msec,answer,answerlength,rp) ;
	//printf ("dialog write bytes = %d\n",write(line,buffer,strlen(buffer))) ;
	write (line,buffer,strlen(buffer)) ;
}


static void clearLine (int line)
{
	int ii ;
	char cc ;

	ii = fcntl (line,F_GETFL,0);
	fcntl (line,F_SETFL,ii|O_NDELAY) ;
	while (read(line,&cc,1) > 0) 
//		printf ("* %c(%02x)\n",cc<32?' ':cc,cc)
		;
	fcntl (line,F_SETFL,ii) ;
	usleep (1000000L) ;
}


@interface DialOut (PrivateDialOutMethods)	

- nextData ;
- processModem: (int) lastrc ;
- closeDev ;
- (BOOL) convertNumber: (const char*)number ;

@end


@implementation DialOut

//	init

- init
{
	[super init] ;
	
	[NXApp loadNibSection:"DialOut.nib" owner:self] ;
	[myWindow setFrameAutosaveName: "DialOut"] ;
	return  self ;
}


- newNumber: (const char*)number
{
	if (number)
		[myNumberField setStringValue: number] ;
	[myNumberField selectText: self] ;
	[myWindow makeKeyAndOrderFront: self] ;
	return  self ;
}


static void ReadIt (DPSTimedEntry te,double timeNow,void *data)
{
	/* we set data to self so we can call this method from the timed entry */
	[(id)data nextData] ;
}


- doDial: sender
{
	[myNumberField selectText: self] ;
	if (line > 0)
	{
		if (readTimedEntry)
		{
			DPSRemoveTimedEntry (readTimedEntry) ;
			readTimedEntry = NULL ;
		}
		if (pstate == 100)
		{
			dialog (line,4000L,&rx,"OK\r\n","ATH\r") ;
			readTimedEntry = DPSAddTimedEntry (INTERVAL,&ReadIt,self,NX_BASETHRESHOLD) ;
		}
		else
		{
			[myReportField setStringValue: "interrupted"] ;
//			clearLine (line) ;
			[self closeDev] ;
		}
	}
	else
	{
		if (![self convertNumber: [myNumberField stringValue]])
		{
			//NXRunAlertPanel ("Aufgemerkt","Schlechte Nummer",NULL,NULL,NULL) ;
			NXRunAlertPanel ("Alert","Bad number",NULL,NULL,NULL) ;
			return  self ;
		}
		if (lockDevice(DIALOUT,'b') != 0)
		{
			[myReportField setStringValue: "Port b cannot be locked"] ;
			return  self ;
		}
		if ((line = openDevice(DIALOUT,'b')) <= 0)
		{
			unlockDevice (DIALOUT,'b') ;
			[myReportField setStringValue: "Port b cannot be opened"] ;
			return  self ;
		}
		[myDialButton setTitle: "Hangup"] ;
		initDevice (line,B38400) ;
		pstate = 1 ; ringcount = 1 ;
		[myReportField setStringValue: "preparing modem"] ;
		dialog (line,2000L,&rx,"OK\r\n","AT\r") ;
		readTimedEntry = DPSAddTimedEntry (INTERVAL,&ReadIt,self,NX_BASETHRESHOLD) ;
	}
	return  self ;
}



// ------------------- private methods -------------------------

- nextData
{
	int		rc = readRX (line,&rx) ;
	
	if (rc < 1)
	{
		DPSRemoveTimedEntry (readTimedEntry) ;
		readTimedEntry = NULL ;
		[self processModem: rc] ;
	}
	return  self ;
}


- closeDev
{
	if (line > 0)
	{
		closeDevice (line) ;
		unlockDevice (DIALOUT,'b') ;
		line = 0 ;
		[myDialButton setTitle: "Dial"] ;
	}
	return  self ;
}


- processModem: (int) lastrc
{
//	printf ("state = %d  lastrc = %d\n",pstate,lastrc) ;
	if (lastrc < 0)
	{
//printf ("state = %d  lastrc = %d\n",pstate,lastrc) ;
		[myReportField setStringValue: "Error: timeout"] ;
		[self closeDev] ;
		return  self ;
	}
	switch (pstate)
	{
	case 1:
		dialog (line,4000L,&rx,"OK\r\n","ATZ\r") ;
		readTimedEntry = DPSAddTimedEntry (INTERVAL,&ReadIt,self,NX_BASETHRESHOLD) ;
		break ;
	case 2:
		dialog (line,2000L,&rx,"OK\r\n","ATE1M2L5&D3S0=0\r") ;
		readTimedEntry = DPSAddTimedEntry (INTERVAL,&ReadIt,self,NX_BASETHRESHOLD) ;
		break ;
	case 3:
		dialog (line,2000L,&rx,"OK\r\n","AT+FCLASS=8\r") ;
		readTimedEntry = DPSAddTimedEntry (INTERVAL,&ReadIt,self,NX_BASETHRESHOLD) ;
		break ;
	case 4:
		[myReportField setStringValue: "dialing..."] ;
		dialogAnswer (line,40000L,&rx,"\r\n",readbuf,sizeof(readbuf),"ATDT%s\r",realNumber) ;
		readTimedEntry = DPSAddTimedEntry (INTERVAL,&ReadIt,self,NX_BASETHRESHOLD) ;
		break ;
	default:
		if (pstate == 100)
		{
			[myReportField setStringValue: "hung up"] ;
			[self closeDev] ;
		}
		else if (pstate % 2)
		{
			initRX ("\r\n",2000L,readbuf,sizeof(readbuf),&rx) ;
			readTimedEntry = DPSAddTimedEntry (INTERVAL,&ReadIt,self,NX_BASETHRESHOLD) ;
		}
		else
		{
			*(rx.answer+rx.answerpos) = '\0' ;
			if (!strcmp(rx.answer,"BUSY"))
			{
				[myReportField setStringValue: "Sorry: line busy"] ;
				[self closeDev] ;
			}
			else if (!strcmp(rx.answer,"NO DIALTONE"))
			{
				[myReportField setStringValue: "Sorry: no dial tone"] ;
				[self closeDev] ;
			}
			else if (!strcmp(rx.answer,"NO CARRIER"))
			{
				[myReportField setStringValue: "Sorry: no carrier"] ;
				[self closeDev] ;
			}
			else if (!strcmp(rx.answer,"NO ANSWER"))
			{
				[myReportField setStringValue: "Sorry: no answer"] ;
				[self closeDev] ;
			}
			else if (!strcmp(rx.answer,"VCON"))
			{
				[myReportField setStringValue: "voice connection"] ;
				pstate = 99 ;
			}
			else if (!strcmp(rx.answer,"RINGING"))
			{
				char	buf [40] ;

				sprintf (buf,"ringing %d",++ringcount) ;
				[myReportField setStringValue: buf] ;
				initRX ("\r\n",7000L,readbuf,sizeof(readbuf),&rx) ;
				readTimedEntry = DPSAddTimedEntry (INTERVAL,&ReadIt,self,NX_BASETHRESHOLD) ;
			}
		}
		break ;
	}
	pstate++ ;
	return  self ;
}


- (BOOL) convertNumber: (const char*)number
{
	const char	*ignore = NXGetDefaultValue([NXApp appName],"charsToIgnore") ;
	const char	*idcountry = NXGetDefaultValue([NXApp appName],"countryCode") ;
	const char	*country = NXGetDefaultValue([NXApp appName],"thisCountry") ;
	const char	*idcity = NXGetDefaultValue([NXApp appName],"cityCode") ;
	const char	*city = NXGetDefaultValue([NXApp appName],"thisCity") ;
	char		*icp , *rcp ;
	BOOL		firstValid = NO ;
	char		buffer [20] ;
	int			buflen ;

	rcp = realNumber ;
	while (*number)
	{
		for (icp = (char*)ignore ; *icp ; icp++)
			if (*icp == *number)
				break ;
		if (*icp == '\0')
		{
			if (!firstValid  &&  *number == '+')
			{
				strcpy (rcp,idcountry) ;
				rcp += strlen (idcountry) ;
			}
			else
				*rcp++ = *number ;
			firstValid = YES ;
		}
		number++ ;
	}
	*rcp = '\0' ;

	for (rcp = realNumber ; *rcp ; rcp++)
		if (*rcp < '0'  ||  *rcp > '9')
			return  NO ;

	/* check this country */
	strcpy (buffer,idcountry) ; strcat (buffer,country) ; buflen = strlen (buffer) ;
	if (!strncmp(realNumber,buffer,buflen))
	{
		memcpy (realNumber,idcity,strlen(idcity)) ;
		memmove (realNumber+strlen(idcity),realNumber+buflen,strlen(realNumber)-buflen+1) ;
	}

	/* check this city */
	strcpy (buffer,idcity) ; strcat (buffer,city) ; buflen = strlen (buffer) ;
	if (!strncmp(realNumber,buffer,buflen))
		memmove (realNumber,realNumber+buflen,strlen(realNumber)-buflen+1) ;

	return  YES ;
}

@end
