/*
 * $Source: $
 * $Revision: $
 * $Date: $
 * $State: $
 * $Author: $
 *
 *
 * $Log: $
 *
 */

/*  
 *	Copyright (c) 1991 by the Massachusetts Institute of Technology,
 *	For copying and distribution information, see the file
 *	"mit-copyright.h".
 *
 *   networkMgr.c  - WADE 8/23/89
 *	
 *	net_end
 *	net_check
 *	net_open
 *	net_error
 *	net_banner
 *	net_talk
 *	mpanic
 *	user_abort
 *
 */
 
#include <types.h>
#include <ctype.h>
#include <string.h>
#include "mtypes.h"
#include "net_stuff.h"
#include <fcntl.h>
#include <dialogs.h>
#include <ToolUtils.h>
#include <Events.h>
#include <Traps.h>
#include <SegLoad.h>
#include <Dialogs.h>
#include <stdio.h>
#include "pips.h"
#include "mit-copyright.h"
#include <Osutils.h>

#define	  CONNECTION_CLOSED "connection is closed"
#define	  LF '\012'
#define	  CR '\015'

static stream 	f_stream = NULL;
static char   	banner[BANNER_SIZE] = {'\0'};
extern char   	gMessage[];
extern Handle	gTextStr;
extern Boolean	gBitBucket;

/*     
 *
 * net_end: 
 *	Close socket connection.  Change static values for banner and f_stream.
 * 
 *
 */
#pragma segment Network
net_end()
{
	int  result;
	
	if ( f_stream != NULL ) {
		/* strcpy(banner,CONNECTION_CLOSED); */
		result =  tcp_close_stream(f_stream);
		f_stream = NULL;
		banner[0] = '\0';
	}
}

/*     
 *
 * net_check: 
 *	If we expect the an open socket stream, true is returned, else false is returned.
 *	Called from network dialog routine.
 *
 */
#pragma segment Network
int
net_check()
{
	if ( f_stream != NULL ) 
		return true;
	else
		return false;
}

/*     
 *
 * net_open: 
 *	Open connection to specified host and port.  Read banner from server.
 * 	Banner is terminated by a LF (\012).
 *
 */
#pragma segment Network
int
net_open( debug )
Boolean  debug;
{
     char 		msg[50];
     int  		num_read, result;
     ip_addr   	host_addr;
     char      	real_name[256], buf[256];
     Boolean	done;
     int		end;
	 char		port[30];
	 char		host[30];
	 short		watch_cursor_index = -1;
     short		watch_cursor_offset = 5;
	 int		msg_rc;
	 char		*indexPtr;

	result = -1;				/* return -1 if f_stream is already open */
	
	data_host(retrieve,host);
	data_port(retrieve,port);
	
	if ( f_stream == NULL ) {
		/* resolve host name into IP address */
		sprintf( msg, "Looking for TechInfo Server" );
		if (debug) 
			OpenMsgDialog( msg );
			
		wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset);
//		result = resolve_name (host, real_name, &host_addr);
// mec - use the name resolver from the bsd library
		result = ns_resolve_name (host, real_name, &host_addr);
		if ( result ) {
			net_error( result, msg );
			if (debug)
				CloseMsgDialog();
			return ( result );
		}
		
		/* open TCP/IP socket connection to host server */
		sprintf(msg, "Connecting to TechInfo Server"); 
		if (debug) 
			OpenMsgDialog( msg );
			
		wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset);
		result = tcp_open_stream( host_addr, atoi(port), &f_stream );
		if ( result ) {
			net_error( result, msg );
			if (debug)
				CloseMsgDialog();
			return( result );
		}

		/* get banner */
		sprintf(msg, "%s", "Connected to TechInfo Server" ); 
		if (debug) 
			OpenMsgDialog( msg );
			
		banner[0] = '\0';
		done = false;
		for (; !done ;) {
			wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset);
			result = tcp_get_data(f_stream, buf, sizeof(buf), &num_read);

			if (result != 0) {
				if (result == TIMED_OUT) 
					continue;
				else {
					net_error( result, msg );
					if (debug)
						CloseMsgDialog();
					result = net_end();
					return (result);
				}
			}
			else {
				wind_set_cursor(0); 			/* turn cursor into arrow */
				strncat(banner,buf,num_read);
				end = strlen( banner );
				if (( banner[end-1] == LF) && (banner[end-3] == '.')) {
					done = true;			/* terminate the loop   */
					banner[end-4] = '\0';
					
					/* determine if we can continue based on banner msg */
					if (msg_rc = atoi(banner)) {
						
						/* close message box */
						if ( debug )   
							CloseMsgDialog();
							
						indexPtr = strstr(banner,":"); 
						if (indexPtr == NULL)
							indexPtr = banner;
						else
							indexPtr++;
						OpenAlertDialog( TECHINFO_MSG, indexPtr); 

						return(msg_rc);
					}
				}
			}

		}  	/* end of for loop  */

		/* close message box */
		if ( debug )   
			CloseMsgDialog();

	}     /* end: if stream != NULL */

	/* we are done. */
	return result;  
}

/*     
 *
 * net_error: put up alert dialog box if network problems were encountered.
 * 
 *
 */
#pragma segment Network
net_error( result, msg )
int	result;
char	*msg;
{
	Str255	error;
	
	sprintf(error,"(%d) %s", result, msg );
      	OpenAlertDialog( NETWORK_ALERT, error);
	CloseMsgDialog();  /* to be on the safe side, ie. the server kills the connection */
}

/*     
 *
 * net_banner: 
 *	Returns in argument the current banner.
 *	Called from network dialog routine 
 *
 */
#pragma segment Network
net_banner( status )
char *status;
{
	strcpy(status,banner);
}

#define flush_out() \
 { if (out_ptr != outbuf) {\
 	wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset); \
 	result = tcp_send_data(f_stream, outbuf, out_ptr - outbuf ); \
	out_ptr = outbuf; \
} }

#define out_char(c) \
 { if (out_ptr == out_limit) { \
 	wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset); \
 	result = tcp_send_data(f_stream, outbuf, BSIZE - 1); \
	out_ptr = outbuf; \
 }\
   *out_ptr++ = c; }
   
#define check_result() \
{	if ( result ) { \
		if (result != USER_ABORTED) \
			net_error( result, msg ); \
		if ( debug ) \
			CloseMsgDialog(); \
		(void)net_end(); \
		return(result); \
	} \
}

#define BSIZE	2048

/*     
 *
 * net_talk: Send a command to the server.  Command ends in CRLF.  Read from socket
 * until end of message (.CRLF) has been sent.  Each line of the message is terminated
 * by a LF (\012).  Each record is binded to a cell thru a list manager call.
 *
 */
#pragma segment Network
int
net_talk( send_str,send_length,debug,sendType,replyType,listCtr,bad_ctr )
char 	*send_str;
Boolean debug;
int	 	sendType;
int	 	replyType;
int		*listCtr;
int		*bad_ctr;
int	 	send_length;
{
     Str255 	msg;
     char      	buf[BSIZE-1],temp[BSIZE];
     int       	result,num_read;
     char 		*sp,*endsp,*tp;
     Boolean	done = false;
     Boolean	rc;
     int		lost_bytes;
     char		*out_ptr;
     char		*out_limit;
     char		outbuf[BSIZE];
     char		*in_ptr;
     char		*end_tp;
     char		*last_char;
     long		text_size;
	 int		list_size;
     char		*delim_ptr;
     short		watch_cursor_index = -1;
     short		watch_cursor_offset = 5;
     long		running_count = 0;
     float		percent = 0.0;
     Boolean	thermometer = false;
     long		max_text_size;
	 DialogPtr	th_handle;
	 Str255		list_string;
	 Str255		title;
	 long		image_size;
	 long		image_sent;
	 int		header_size;
	 long		image_running_total;
	 
    enum{init,show,close};
	 
	/* initialize number of list cells created and those that could not be created */
	*listCtr = 0; 
	*bad_ctr = 0;

	/* we may not have a connection if the user aborted the previous transaction */
	if ( !net_check() ) {
		rc = net_open( false);
		if (rc > 0) /* lets get out of here if we could not get a connection */
			return rc;
	}
		
	/* sending text to server */
	sprintf(msg, "%s","Sending message ...");
	if ( debug ) 
		OpenMsgDialog( msg );
		
	result = 0;		/* need this as result may not have a value on first call to check_result */
	switch( sendType) {
		case text:

			out_ptr = outbuf;
			out_limit = &outbuf[BSIZE-1];
			in_ptr = send_str;

			for (;*in_ptr != '\0';in_ptr++) {
				/* swap mac LF & CR to match that of non mac environments */
				if (*in_ptr == '\n')
					*in_ptr = '\r';
	
				out_char(*in_ptr);
				check_result();
			}

			flush_out();
			check_result();

			/* send "end of text message" NOTE:  the text must end with a LF before we
 				send this EOM stream.  util_soft_text checks for this */
			wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset);
			result = tcp_send_data(f_stream, ".\015\012", 3 );
			check_result();

			break;
	  
		case command:
			wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset);
			result = tcp_send_data(f_stream, send_str, send_length);
			check_result();
			break;
	}

	/* Process response from server.  Each record will be terminated by a \n"*/
	/* We need to parse buffer into a record and call a List routine to bind */
	/* the record into a cell.	*/
	sprintf(msg, "%s", "Waiting for response ...");
	if ( debug ) 
		OpenMsgDialog( msg ); 

	last_char = temp;
	tp = temp;
	temp[0] = '\0';
	end_tp = &temp[BSIZE-1];
	list_size = 0;
	if (replyType == message )
		*gMessage = '\0';
	
	for (; !done ;) {
		/* change cursor from an arrow to the watch clock */
		wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset);

		result = tcp_get_data(f_stream, buf, sizeof(buf), &num_read);
		running_count = running_count + num_read;

		if (result != 0) {
   			if (result == TIMED_OUT) 
				continue;
   			else 
	  			if (result == USER_ABORTED ) {
					done = true;
					(void)net_end();
					if (replyType == gif) {
						*listCtr = 0;  /* make sure we don't try to display the image */
						result = 0;    /* the calling routine needs a return of 0 */
						continue;	   /* let's get out of here */
					}
						
					result = 0;  /* we want to return a good result inorder to display the
									partial list or document */
				}
  				else {
					net_error( result, msg );
					done = true;
					(void)net_end();
					continue;
   				}
		}
		
		/* handle the result of an gif image file */
		if (replyType == gif) {
			/* if this is the first record, retrieve the header */
			if (*listCtr == 0) {
				/* get total size of file, e.g. stat of file */
				image_size = atol(buf);
				
				/* make sure the file was found, too bad we don't have an error code to check */
				if (image_size == 0) {
					/* since listCtr == 0, gif_net_data should return false, which will */
					/* force an gif error message to display */
					wind_set_cursor(0);  
					return(result);
				}
					
				/* store the image size in a global variable in GIFPictMgr.c */
				gif_store_image_size(image_size);
				
				/* get number of bytes sent for this transaction */
				for(sp = buf; *sp != ':'; sp++);
				sp++;
				image_sent = atol(sp);

				/* find the first two LF's as this terminates the header */
				/* someday, we may have a gif header and not need to do this */
				for(; *sp != LF;  sp++);
				header_size = sp - buf + 2;
				sp = sp + 2;  /* move past two LF's */
				
				(*listCtr)++;
				
				image_running_total = num_read - header_size;
				
				/* add the buffer to the image buffer */
				gif_bind(sp,image_running_total,0);
			}
			else {
				/* add the buffer to the image buffer */
				gif_bind(buf, num_read, image_running_total);

				image_running_total = image_running_total + num_read;
			}
			
			if (image_running_total == image_sent) {
				/* remove the EOM from the image buffer */
				gif_net_data(gif_correct_size);
				
				/* we are done receiving */
				done = true;
			}
				
			/* don't continue processing other types of nodes, e.g. menu's or documents */
			continue;
		}
		
		/* form record, which terminates in a LF (\012). */
		/* use temp string as a working string to formulate the record. */ 
		endsp = buf + num_read;
		sp = buf;
  		while (sp < endsp) {
  			if (*sp == LF) {
  				/* if first three characters of new record begin with a ".\r\n", then this
  					must be the EOM sequence, so we are done reading from the socket.   */
				if ( (temp[0] == '.' ) && (temp[1] == CR ) ){
  					done = true;
  					break;
  				}
  
  				switch ( replyType ) {
  					case list:
						/* don't count the first record as this represents the list count */
						
						if (list_size == 0) {
							list_size = atol(temp);
							if (list_size >= 100) {
								rc = list_last_click_buffer(list_string);
								if (rc) { 
									rc = get_field(list_string,TITLE,title);
									if (rc) c2pstr( title );
								}
								else
									/* we don't have a title - probably came from a "set startup" */
									strcpy(title,"\p ");

								thermometer = true;
								dlog_thermometer(init,&th_handle,0.0,title);
							}
						}

						else {
  							rc = list_Bind(temp,tp - temp,*listCtr);
							(*listCtr)++;  /* number of records read */
							
							/* increment bad list counter if we could not bind
  							the record to the list data object */
  							if (!rc) 
  								*bad_ctr = *bad_ctr + 1;
							
							if (thermometer && (*listCtr % 10 == 0)) {
								percent = (float)*listCtr / (float)list_size;
								dlog_thermometer(show,&th_handle,percent," " );
							}
  						}
  
						if ( debug ) {
							sprintf(msg, "Creating list .. %d",*listCtr);
							OpenMsgDialog( msg );
						}
						
						break;
				
					case text:
						if ( *listCtr == 0 ) {	/* first record */
							
							*tp = '\0';	/* make temp null terminated for strstr functions */
							
							/* find # of incoming bytes */
							/* protocal:  max bytes count DELIM incoming bytes DELIM date stamp */
							text_size = 0;
							delim_ptr = strstr(temp,DELIM);				
							if (delim_ptr != NULL ) 
								text_size = atol(delim_ptr+1);
							
							/* find total size of the document */
							max_text_size = atol(temp);
										
							/* now, remove the byte counts - everything before second DELIM */
							if (delim_ptr != NULL) {
								delim_ptr = strstr(delim_ptr+1,DELIM);
								if (delim_ptr != NULL) {
									strcpy(temp,delim_ptr+1);
									tp = temp + strlen(temp);	/* tp points to last char + 1 */
								}
							}

							rc = list_last_click_buffer(list_string);
							if (rc)  {
								rc = get_field(list_string,TITLE,title);
								if (rc) c2pstr( title );
							}
							else
								/* we don't have a double click */
								strcpy(title,"\p ");

							/* no magic to 6400, just seemed like a good number */
							if (text_size > 6400) {
								thermometer = true;
								dlog_thermometer(init,&th_handle,0.0,title);
							}
							
							/* save text size as static data for use in the menuMgr */
							data_text_size(store,&max_text_size);
							
						}
				
			   			*tp = '\n';			/* terminate string with a CR */
						*(tp+1) = '\0';
						(*listCtr)++;
						
						/* throw out the first line (after the date stamp and a blank line)
							if it is not the first section of a multiple section document */
						if ((*listCtr == 3) && (gBitBucket)) 
							gBitBucket = false;
						else
			   				lost_bytes = text_bind( temp );
				
						if (thermometer && 
							(((float)running_count / (float)text_size) > percent)) {
							dlog_thermometer(show,&th_handle,percent,"" );
							percent = percent + .01;
						}

			   			break;
				
					case message:
						*tp = '\0';
						if (strlen(temp) >= MESSAGE_RECEIVE_SIZE) {
							net_end();
							BigBadError(SMALL_BUFFER);
						}
						
						strcat(gMessage,temp);
						break;
				}

				tp = temp;     /* reset record pointer */
				temp[0] = '\0';
			}
			
			else {  /* do not exceed the line size */
				if ( tp != end_tp ) {
					/* don't add "_BS" found in unix manuals and other doc's */
					if ((*sp == '\010' )  && (*last_char == '_'))
						tp = tp - 1; 
					else {
						*tp = *sp;
						last_char = tp;
						tp++;
					}
				}
			}
			
			sp++;
		}	/* end while loop */
	}		/* end for loop   */

	wind_set_cursor(0);  

	/* close message box */
	if ( debug )   
		CloseMsgDialog();

	if (thermometer)
		dlog_thermometer(close,&th_handle,0.0,"");

	return(result);
}

/*     
 *
 * mpanic:  called from net_stuff.c file when memory can not be allocated.
 *
 */
mpanic(s)
char *s;
{
	#pragma unused ( s );
	
	/* out of memory ==> from netStuff.c */
	write(1,"out of memory",13);
	exit(1);
}

/*     
 *
 * user_abort:  called from wait_for in file netstuff.c 
 *  check whether or not the user has requested an abort;
 *
 */
#pragma segment Network
int
user_abort()
{
	EventRecord	er;
	
	if (EventAvail(keyDownMask,&er)) {
		if ((er.message & charCodeMask) == '.' &&
			(er.modifiers & cmdKey) != 0) {
				GetNextEvent(keyDownMask, &er);
				CloseMsgDialog(); /* do this to be on the safe side */
				return(USER_ABORTED);
		}
	}
	
	return(0);
}
    
/*     
 *
 * NetworkInit:  called from Initialize in file main.c 
 *  this installs the user abort handler function user_abort()
 *
 */
void NetworkInit()
{
	set_user_abort(user_abort);
}
