/*
 * Phone dialing Module (from XMDM)
 *
 *
 *		Jwahar Bammi
 *			usenet: mandrill!bammi@{decvax,sun}.UUCP
 *			csnet:  bammi@mandrill.ces.CWRU.edu
 *			arpa:   bammi@mandrill.ces.CWRU.edu
 *			CompuServe: 71515,155
 */

#include "config.h"

#ifndef STANDALONE
#ifdef PHONES

#include "zmdm.h"

typedef int WORD;
typedef long LONG;


#define sendchar(c)	Bconout(1, c);
#define clear_screen()  Bconws("\033H\033J")
#define inv()		EscSeq('p')
#define normal()	EscSeq('q')
#define mvto(r,c)	EscSeq('Y');Bconout(2, r+040);Bconout(2, c+040)
#define ceol()		EscSeq('K')
#define show_cursor()	EscSeq('e')
#define hide_cursor()	EscSeq('f')

typedef struct _dir {		/* The Directory type */
	char	*name;		/* Name (11 chars max) */
	char 	*number;	/* Phone # (24 chars max) */
	WORD	baud;		/* baud Rate		 */
	struct _dir *next;	/* Ptr to next entry */
} *DIR;


 	/* External Variables */
extern WORD speed;		/* Current Baud Rate */
extern WORD Baudrate;
extern BAUDS vbauds[];

WORD dchanged = 0;			/* Has the directory been updated */

 	/* Globals */
static WORD ndir = 0;			/* # of entries in phone directory   */
static DIR directory = (DIR)NULL;	/* The phone directory  	     */
static DIR lastdir   = (DIR)NULL; 	/* Pointer to last entry	     */
static char *dirfile = (char *)NULL;	/* Name of file conatining directory */
static WORD rflag = 0;			/* Read directory as yet?? 	     */


/*
 * Read the Telephone directory
 *  returns -2 on read error
 *	    -1 if cancelled
 *	    -3 if file not found
 *	     n # of entries  otherwise
 */
WORD readdir()
{
	register char *filename;
	register WORD handle;
	register WORD nentries;
	register DIR last;
	register DIR present;
	extern char *preadl();
	extern char *malloc();
	
	Bconws("Enter Filename of Phone Directory or <CR> to Cancel: ");

	if((filename = preadl()) == (char *)NULL)
	    /* Cancelled */
	    return -1;

	if((dirfile = malloc(strlen(filename)+1)) == (char *)NULL)
	{
		/* Out of memory */
		Bconws("Out of Memory\r\n");
		return 0;
	}
	strcpy(dirfile,filename);
	

	if((handle = Fopen(filename,0)) < 0)
	    /* File does not exist */
	    return -3;
	
	/* Read in the file */
	if(Fread(handle, 2L, &ndir) != 2L)
	{
		Bconws("Error Reading ");
		Bconws(filename);
		Bconws("\r\n");
		Fclose(handle);
		return -2;
	}

	/* Read in the directory */
	last = (DIR)NULL;
	directory = (DIR)NULL;
	lastdir = (DIR)NULL;
	rflag = 1;
	
	for(nentries = 0; nentries < ndir; nentries++)
	{
		/* Allocate an entry */
		if((present = (DIR)malloc(sizeof(struct _dir))) == (DIR)NULL)
		{
			/* Out of memory */
			Bconws("Out of Memory\r\n");
			Fclose(handle);
			return nentries;
		}
		
		if((present->name = malloc(12)) == (char *)NULL)
		{
			/* Out of memory */
			Bconws("Out of Memory\r\n");
			Fclose(handle);
			return nentries;
		}

		if((present->number = malloc(25)) == (char *)NULL)
		{
			/* Out of memory */
			Bconws("Out of Memory\r\n");
			Fclose(handle);
			return nentries;
		}

		present->next = (DIR)NULL;
		
		/* Read in the entry */
		if(Fread(handle,11L,present->name) != 11L)
		{
			Bconws("Error Reading ");
			Bconws(filename);
			Bconws("\r\n");
			Fclose(handle);
			rflag = 0;
			freedir(directory);
			return -2;

		}

		if(Fread(handle,24L,present->number) != 24L)
		{
			Bconws("Error Reading ");
			Bconws(filename);
			Bconws("\r\n");
			Fclose(handle);
			rflag = 0;
			freedir(directory);
			return -2;
		}

		if(Fread(handle,2L,&(present->baud)) != 2L)
		{
			Bconws("Error Reading ");
			Bconws(filename);
			Bconws("\r\n");
			Fclose(handle);
			rflag = 0;
			freedir(directory);
			return -2;
		}
		

		present->name[11] = '\0';
		present->number[24] = '\0';

		/* Link it on with the directory */
		if(last == (DIR)NULL)
			/* first entry */
			directory = present;
		else
			last->next = present;
		
		last = present;
	}
	lastdir = last;
	
	return nentries;	
}

/*
 * Free space allocated to a phone directory
 *
 */
freedir(dir)
register DIR dir;
{
	register DIR next;
	register DIR present;

	for(present = dir; present != (DIR)NULL; present = next)
	{
		next = present->next;
		free(present->name);
		free(present->number);
		free(present);
	}
	
	if(dirfile != (char *)NULL)
	{
		free(dirfile);
		dirfile   = (char *)NULL;
	}
	
	directory = (DIR)NULL;
	lastdir	  = (DIR)NULL;
	ndir	  = 0;
	rflag	  = 0;
}

/*
 * Write out the phone directory
 *  -returns 0 on success 1 otherwise
 *
 */
WORD writedir()
{
	register DIR dir;
	register WORD fd;
	
	if((rflag == 0) || (dirfile == (char *)NULL) || (dchanged == 0))
	    /* Nothing to Save */
	    return 0;

	/* Create/Open file for write - overwrite if it exists */
	if ((fd = Fcreate(dirfile,0)) < 0) /* Will fail if file is present */
	{
		/* Overwrite existing file */
		if((fd = Fopen(dirfile,1)) < 0)
		{
			Bconws("Cannot Open ");
			Bconws(dirfile);
			Bconws("\r\n");
			return 1;
		}
	}

	if(Fwrite(fd,2L,&ndir) != 2L)
	{
		Bconws("Error Writing ");
		Bconws(dirfile);
		Bconws("\r\n");
		Fclose(fd);
		return 1;
	}
	
	for(dir = directory; dir != (DIR)NULL; dir = dir->next)
	{
		if(Fwrite(fd,11L,dir->name) != 11L)
		{
			Bconws("Error Writing ");
			Bconws(dirfile);
			Bconws("\r\n");
			Fclose(fd);
			return 1;
		}

		if(Fwrite(fd,24L,dir->number) != 24L)
		{
			Bconws("Error Writing ");
			Bconws(dirfile);
			Bconws("\r\n");
			Fclose(fd);
			return 1;
		}

		if(Fwrite(fd,2L,&(dir->baud)) != 2L)
		{
			Bconws("Error Writing ");
			Bconws(dirfile);
			Bconws("\r\n");
			Fclose(fd);
			return 1;
		}
	}
	
	Fclose(fd);
	return 0;
}

/*
 * Show the phone directory
 * return the entry # or -1 if cancelled
 *
 */
WORD showdir()
{
	register WORD first, last;
	register WORD n;
	register char *line;
	extern WORD atoi();
	extern char *preadl();
	
	first = 0;

	while(1)
	{
	    again:
		clear_screen();

		mvto(0,19);
		Bconws("Phone Directory: ");
		Bconws(dirfile);
		Bconws("  ");
		printf("%d",ndir); fflush(stdout);
		Bconws(" Entry(s)");

		last = (ndir < (first + 44)) ? ndir : first + 44;
		putdir(first,last);
		
		/* mvto(25,0); */
		Bconws("\r\n");
		inv();
		Bconws("Enter a # or <SPACE><RETURN> for Next Page or <RETURN> to Cancel:");
		normal();
		Bconout(2, ' ');
		if((line = preadl()) == (char *)NULL)
		{
			return -1;
		}
		
		if(isdigit(*line))
		{
			if((n = atoi(line)) >= ndir)
			{
				Bconws("Invalid Number ");
				hit_key();
				goto again;
			}
		    
			return n;
		}

		if(last == ndir)
		    first = 0;
		else
		    first += 44;
	}
}

/*
 * Put up directory entries on the screen
 *
 */
putdir(first,last)
register WORD first;
register WORD last;
{
	register DIR dir;
	register WORD row;
	extern DIR nth();
	
	/* Find the first entry */
	dir = nth(first);
	row = (first % 44) + 1;

	hide_cursor();
	inv();
	for(; first < last; first++)
	{
		mvto(row,((first & 1)?41:0));
		if(first < 10)
			Bconout(2, ' ');
		printf("%d",first); fflush(stdout);
		Bconout(2, '|');
		putstr(dir->name,11);
		Bconout(2, '|');
		putstr(dir->number,24);
		dir = dir->next;
		if(first & 1)
			row++;
	}

	if(first & 1)
	{
		mvto(row,41);
		Bconws("  |           |                        ");
		row++;
	}

	for(; row < 23; row++)
	{
		Bconws("  |           |                        ");
		mvto(row,41);
		Bconws("  |           |                        ");
	}

	normal();
	show_cursor();
}

/*
 * Put a string padding to len on screen
 */
putstr(s,l)
register char *s;
register WORD l;
{
	register WORD pad;

	Bconws(s);
	if((pad = l - strlen(s)) <= 0)
		return;
	for(; pad > 0; pad--)
		Bconout(2, ' ');
}


/*
 * Return the nth entry in the phone directory
 *
 */
DIR nth(n)
register WORD n;
{
	register WORD i;
	register DIR dir;
	
	for(i = 0, dir = directory; (dir != (DIR)NULL) & (i < n);
	    i++, dir = dir->next)
	    /* Skip */;
	return(dir);
}

/*
 * Add a entry in the phonebook
 */
addir()
{
	register DIR dir;
	register char *s;
	extern char *malloc(), *preadl();
	
	/* If a directory file already exists add, else read or create */
	if(rflag == 0)
	{
		switch(readdir())
		{
		    case -3:
			/* Doesnt exist, but we will create it when we
			   write out the directory */
			rflag = 1;
			break;
			
		    case 0:
		    case -2:
		    case -1:
			hit_key();
			return;
			
		    default:
			rflag = 1;
			break;
		}
	}
	
	/* Allocate space for the new entry */
	if((dir = (DIR)malloc(sizeof(struct _dir))) == (DIR)NULL)
	{
		Bconws("Out of Memory\r\n");
		hit_key();
		return;
	}
	
	if((dir->name = malloc(12)) == (char *)NULL)
	{
		/* Out of memory */
		Bconws("Out of Memory\r\n");
		hit_key();
		return;
	}

	if((dir->number = malloc(25)) == (char *)NULL)
	{
		/* Out of memory */
		Bconws("Out of Memory\r\n");
		hit_key();
		return;
	}

		
	/* Get the entry */
	
	do {
		Bconws("Name: ");
		s = preadl();
	} while(s == (char *)NULL);
	
	strncpy(dir->name,s,11);
	
	do {
		Bconws("Number: ");
		s = preadl();
	} while(s == (char *)NULL);
	
	strncpy(dir->number,s,24);
	
	do {
		Bconws("Baud Rate: ");
		s = preadl();
	} while((s == (char *)NULL) || ((dir->baud = tobaud(s)) == -1));
	
	dir->next = (DIR)NULL;
	
	if(directory == (DIR)NULL)
	    directory = dir;
	else
	    lastdir->next = dir;

	lastdir = dir;
	dchanged = 1;
	ndir++;
}



/*
 * Convert a string to a baud rate
 * return int or -1 if invalid
 */
WORD tobaud(s)
register char *s;
{
	register WORD i;
	
	for(i = 0; vbauds[i].sbaud != (char *)NULL; i++)
	{
		if(strcmp(vbauds[i].sbaud,s) == 0)
		    return vbauds[i].ibaud;
	}

	Bconws(s);
	Bconws(": Invalid Baud Rate\r\nValid Baud Rates are:\r\n");
	for(i = 0; vbauds[i].sbaud != (char *)NULL; i++)
	{
		Bconout(2, '\t');
		if((i != 0) && (vbauds[i].ibaud != vbauds[i-1].ibaud))
		{
			Bconws(vbauds[i].sbaud);
			Bconws("\r\n");
		}
	}
		
	return -1;
}

/*
 * Dial a number 
 */
dial()
{
	register WORD n;
	register DIR dir;
	extern DIR nth();
	
	/* Has the directory been read so far */
	if(rflag == 0)
	{
		/* Go read it */
		switch(readdir())
		{
		    case -1:
		    case -2:
		    case -3:
		    case 0:
			rflag = 0;
			hit_key();
			his_screen();
			return;
		    default:
			break;
		}
	}
	
	if((n = showdir()) == -1)
	{
		/* Cancelled */
		hit_key();
		his_screen();
		return;
	}
	
	his_screen();
	dir = nth(n);
	if(dir->baud != speed)
	{
		speed = dir->baud;
		Baudrate = jbaud(speed);
		Rsconf(speed, -1, -1, -1, -1, -1);
		sendchar('\r');
		flushinput();
	}
	write_modem(PREDIAL, strlen(PREDIAL));
	write_modem(dir->number,strlen(dir->number));
	sendchar('\r');
}


/*
 * Re-dial the previous number
 *
 *	Does it cheaply, by sending the modem its Re-dial
 *	sequence, instead of remembering the last number dialed etc.
 *
 */
redial()
{
	his_screen();
	write_modem(REDIAL ,strlen(REDIAL));
}


/*
 * Open a phone directory 
 *
 */
opendir()
{
	/* if one is open, save it if changed, then deallocate memory
 	 * and then open a new directory
	 */
	if(rflag)
	{
		if(dchanged)
		{
			if(writedir() == 1)
			{
				hit_key();
				return;
			}
			dchanged = 0;
		}
		freedir(directory);
	}
	
	if(readdir() <= 0)
	{
		hit_key();
		return;
	}
}

/*
 * Delete an entry.
 */
delentry()
{
	register DIR dir, del;
	register WORD n;
	extern DIR nth();
	
	if(rflag == 0)
	{
		Bconws("Nothing to delete\r\n");
		hit_key();
		return;
	}
	
	if((n = showdir()) == -1)
	{
		/* Cancelled */
		hit_key();
		return;
	}
	
	if(ndir == 1)
	{
		del = directory;
		directory = (DIR)NULL;
		lastdir = (DIR)NULL;
	}
	else
	{
		if(n == 0)
		{
			del = directory;
			dir = directory->next;
			directory = dir;
		}
		else
		{
			dir = nth(n-1);
			del = dir->next;
			dir->next = del->next;
			if(lastdir == del)
			    lastdir = dir;
		}
	}
	
	free(del->name);
	free(del->number);
	free(del);
	ndir--;
	dchanged = 1;
	
}

	
/*
 * Phone services - top level
 *
 */
phone()
{
	register LONG conin;
	
	while(1)
	{
		clear_screen();
		mvto(2,32);
		inv();
		Bconws("Phone  Services");
		normal();

		mvto(5,0);
		
		if(rflag)
		{
			Bconws("\tThe Phone Directory \"");
			Bconws(dirfile);
			Bconws("\" containing ");
			printf("%d", ndir); fflush(stdout);
			Bconws(" Entry(s) is Currently Open.\r\n\n");
		}
		else
		    Bconws("\tNo Phone Directory is Currently Open.\r\n\n");
		
		
		/* Put up menu */
		Bconws("\r\n\t");
		EscSeq('p');		/* reverse video */
		Bconws("Undo");
		EscSeq('q');		/* quit reverse video */
		Bconws(" to exit the emulator.\r\n");
		
		Bconws("\t");
		EscSeq('p');		/* reverse video */
		Bconws("d");
		EscSeq('q');		/* quit reverse video */
		Bconws(" to dial a number.\r\n");
		
		Bconws("\t");
		EscSeq('p');		/* reverse video */
		Bconws("a");
		EscSeq('q');		/* quit reverse video */
		Bconws(" to add an entry to the phone directory.\r\n");
		
		Bconws("\t");
		EscSeq('p');		/* reverse video */
		Bconws("D");
		EscSeq('q');		/* quit reverse video */
		Bconws(" to delete an entry from the phone directory.\r\n");
		
		Bconws("\t");
		EscSeq('p');		/* reverse video */
		Bconws("o");
		EscSeq('q');		/* quit reverse video */
		Bconws(" to open another phone directory.\r\n");

		Bconws("\t");
		EscSeq('p');		/* reverse video */
		Bconws("r");
		EscSeq('q');		/* quit reverse video */
		Bconws(" to re-dial last number.\r\n");
		
		Bconws("\t");
		EscSeq('p');		/* reverse video */
		Bconws("Return");
		EscSeq('q');		/* quit reverse video */
		Bconws(" to return to the emulator.\r\n\n\n\n");
		
		/* get response */
		conin = Bconin(2);
		
		if ((conin & 0x00FF0000L) == 0x610000L)
		{
			/* He hit <UNDO> */
			his_screen();
			ResetIoBuf();
			finish();
		}
		
		switch((WORD)(conin & 0x7f))
		{
		    case 'd':
			/* Dial a number */
			dial();
			return;
			
		    case 'D':
			/* Delete an entry */
			delentry();
			break;
			
		    case 'a':
			/* Add an entry */
			addir();
			break;
			
		    case 'o':
			/* Open another phone directory */
			opendir();
			break;
			

		    case 'r':
			/* Re-Dial # */
			redial();
			return;
			
		    case '\r':
			his_screen();
			return;
			
		    default:
			break;
		}
		
	}
	
}

/*
 * Convert a baud rate to its int
 * return int or -1 if invalid
 */
WORD jbaud(bd)
register WORD bd;
{
	register WORD i;
	
	for(i = 0; vbauds[i].sbaud != (char *)NULL; i++)
	{
		if(vbauds[i].ibaud == bd)
		    return vbauds[i].jbaud;
	}
	return -1;
}

static char scrth[80];
/*
 * Read a line from Standard Input and return a 
 * NULL terminated pointer to it
 */
char *preadl()
{
	/* Use the scrth bufr for storage */
	scrth[0] = 80;
	Cconrs(scrth);
	Bconws("\r\n");
	if(scrth[1] == 0)
	{
		/* User Cancelled */
		Bconws("Cancelled\r\n");
		return((char *)NULL);
	}
	/* Terminate string that starts at scrth[2] */
	scrth[scrth[1]+2] = '\0';
	return(&scrth[2]);
}	

#endif /* PHONES */
#endif /* STANDALONE */

/* -eof - */
