/*
 * *************************************************************************
 *
 * Trimble Navigation, Ltd.
 * OEM Products Development Group
 * P.O. Box 3642
 * 645 North Mary Avenue
 * Sunnyvale, California 94088-3642
 *
 * Corporate Headquarter:
 *    Telephone:  (408) 481-8000
 *    Fax:        (408) 481-6005
 *
 * Technical Support Center:
 *    Telephone:  (800) 767-4822	(U.S. and Canada)
 *                (408) 481-6940    (outside U.S. and Canada)
 *    Fax:        (408) 481-6020
 *    BBS:        (408) 481-7800
 *    e-mail:     trimble_support@trimble.com
 *
 * *************************************************************************
 *
 * Vers	Date		   Changes										         Author
 * ----	---------   ----------------------------------------	   ----------
 * 1.10	21 Jun 93	Initial version								 	pvwl
 * 1.11	31 Jul 93	Revisions									 	pvwl
 * 1.12	10 Feb 94	Revisions: SPDRIVE removed; Superpackets     	pvwl
 * 1.12a 31 Mar 94	Revisions: added repeat display window       	pvwl
 * 1.12b  4 Apr 94	Revisions: added 8E                          	pvwl
 * 1.20   5 Jun 95   Revisions: time, repeat window, od tracking 	ahl
 * 1.40  16 Jul 95   simplifications								pvwl
 *
 * *************************************************************************
 *
 * TSIPCHAT.C contains the main() entry function to the TSIPCHAT program.
 * The main() function serves as a model for programs that interface with
 * TSIP-based receivers.  The files required for compilation include:
 *
 *    TSIP_IFC.H  -  parse/format prototypes
 *    TSIP_IFC.C  -  TSIP standard command/report interface
 *
 *    TSIPINCL.H  -  prototypes for commands, reports, serial I/O,
 *                   and utilities
 *    TSIPCHAT.C  -  main program
 *    TSIP_UTL.C  -  user interface functions
 *    TSIP_CMD.C  -  command generator
 *    TSIP_RPT.C  -  report interpreter
 *    TSIP_ALM.C  -  receiver almanacs
 *    TSIP_SIO.C  -  serial input/output setting routines
 *    T_SERIAL.C  -  serail buffer routines
 *
 * Portability
 * The code for TCHAT is written in ANSI C and is highly portable.  The
 * TSIPCHAT code is very restrictive and requires a Borland Compiler.
 * TCHAT can be created using the Microsoft Visual C++ Compiler (version
 * 1.0 or later) or the Borland C++ Compiler (version 3.1 or later).
 *
 * TSIPCHAT Features
 * TSIPCHAT has the following capabilities:
 *
 * 1) Binary Output Storage:  If a filename for binary output is specified,
 * all reports will be saved into a binary file.  The storage file can be
 * opened at the start of TSIPCHAT by specifying a filename as argument.
 * During a TSIPCHAT session, a new storage file can be opened, or an old
 * storage file can be closed or re-opened, with '^F'. The TSIPPRNT program
 * can be used to translate the binary file into ASCII format.
 *
 * 2) Serial Communications:  There are BORLAND-specific functions in
 * TSIP_SIO.C that supply a buffered serial port to operate TSIPCHAT.
 * The computer port settings (eg, baud, data bits, stop bits, parity)
 * can be changed using the '^I' keystroke.
 *
 * Developers of new applications are encouraged to use the functions in
 * TSIP_IFC.C, and pattern their drivers on main() in TCHAT.C.
 * For technical assistance about using TSIPCHAT or creating TSIP-based
 * applications, please contact our Technical Support Center.
 *
 * *************************************************************************
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <dos.h>		/* for ctrlbrk */
#include <math.h>
#include <signal.h>

#include "tsip_ifc.h"
#include "tsipincl.h"

static short
	nprintchar,					/* character count of ASCII line print */
	repeat_report_code,            /* report to be repeat-requested */
	tsipfiletype = TSIPFILE,	/* other choice = AUTOFILE */
	reply [0x200],				/* indexed by rpt.code; >0 if requested by cmd */
	serial_port_hold = FALSE;

/* file for recording report data */
static FILE
	*tsipstream = NULL;

/* file name for same */
static char
	tsipfilename[80];

/* serial port interface 'primitives' required by TSIP_IFC.C */
short getb (void)
{
	short this_byte;

	this_byte = get_byte ();
	if (this_byte == -1) return -1;
	if (tsipstream != NULL) fputc (this_byte, tsipstream);
	return this_byte;
}

short sendb (unsigned char outbyte)
{
	return send_byte (outbyte);
}

/*
 * three special functions of TSIPCHAT:
 * repeat reports;
 * print non-TSIP characters;
 * hourly file opener.
*/

void ask_for_repeat_report (void)
{
	if (!repeat_report_code)
	  return;

	reply [repeat_report_code] = 1;

	switch (repeat_report_code) {
	case 0x41:
		cmd_0x21();
		break;
	case 0x47:
		cmd_0x27();
		break;
	case 0x4D:
		cmd_0x2D();
		break;
	case 0x5A:
		cmd_0x3A(0);
		break;
	case 0x5C:
		cmd_0x3C(0);
		break;
	case 0x85:
		cmd_0x65(0);
		break;
	default:
		return;
	}
}

void rawprint (short newbyte)
{
	short
		curr_window,
		newlinechar,
		newchar;

	switch (newbyte)
	{
	case '\r': case '\n':
		newlinechar = TRUE;
		newchar = -1;
		break;
	case '$': case '>':
	/* NMEA, TAIP starting characters */
		newlinechar = TRUE;
		newchar = newbyte;
		nprintchar++;
		break;
	default:
		newlinechar = FALSE;
		if (newbyte < 0x20 || newbyte > 0xFF) newchar = ((newchar&0x3F)|0x40);
		else newchar = newbyte;
		nprintchar++;
		break;
	}

	/* Save to reset screen at exit. */
	curr_window = which_window();
	switch_window (AUTOWIN);
	if (newlinechar || nprintchar > 75)
	{
		if (nprintchar) show_crlf();
		nprintchar = 0;
	}
	if (newchar > 0) {
		xprintf ("%c", (char)newchar);
	}
	switch_window (curr_window);
}

static void open_auto_file (void)
{
	static double
		file_start_time = -4000.; /* allows file to open before first fix. */
	double
		current_time;

	if (tsipfiletype != AUTOFILE) return;
	current_time = get_tsip_time ();
	if (current_time > file_start_time + 3600.) {
		file_start_time = current_time - fmod(current_time, 3600.);
		/* open up command window */
		close_file_storage (&tsipstream, tsipfilename, tsipfiletype);
		open_file_storage  (&tsipstream, tsipfilename, tsipfiletype);
	}
}

/*
 * The following routine is used in main and any routines requiring
 * user keyboard input.
 * By using this routine, the incoming serial stream is serviced and output on
 * the screen but can be pre-empted by processing of user keystrokes
 * while inputting data.
 */
unsigned char read_rpts_wait_for_kbhit (void)
{
	static TSIPPKT
		rpt;		      /* structure for current incoming TSIP report  */
	short
		newbyte,
		ireply,
		curr_window;   /* if in dual window configuration, current window */
	clock_t
		now_clock;
	static clock_t
		last_repeat_clock;
	static short
		first_message,
		new_repeat_report = FALSE;

	/* Current screen state so we are able to reset screen at exit. */
	curr_window = which_window();

	/* do parsing until a kbhit is detected */
	while (!kbhit ()) {
		if (serial_port_hold)
		{
			/* suppress output if the serial port parmeters are being changed */
			continue;
		}
		newbyte = getb();
		tsip_input_proc (&rpt, newbyte);
		if (rpt.status == TSIP_PARSED_FULL)
		{
			/* end of packet received, prepare for display */
			first_message = TRUE;
			ireply = reply_code (&rpt);
			if (reply [ireply])
			{
				/*
				** if this report has been requested, print in command window,
				** and decrement the reply request.  There may be 32 requests,
				** one for each SV: e.g., cmd_0x3A.
				*/
				if (ireply==repeat_report_code && new_repeat_report)
				{
					new_cmd_window ();
					/* the following will re-print the header if necessary */
					print_msg_table_header (repeat_report_code);
					new_repeat_report = FALSE;
				}
				else
				{
					switch_window (CMDWIN);
				}
				reply [ireply]--;
			}
			else
			{
				switch_window (AUTOWIN);
			}

			rpt_packet (&rpt);

			/* return to active window */
			switch_window (curr_window);

			/* Check to see if time to open an hourly storage file. */
			open_auto_file();

			/* refresh repeat code for special display */
			reply [repeat_report_code] = 1;

			/* reset non-TSIP character printer */
			nprintchar = 0;

			/* prevent printing this packet again */
			rpt.status = TSIP_PARSED_EMPTY;
		}
		else if (rpt.status == TSIP_PARSED_EMPTY && newbyte>=0)
		{
			/* non-TSIP character printer */
			rawprint (newbyte);
		}

		/* repeat command alarm clock control every two seconds */
		now_clock = clock();
		if (now_clock >= last_repeat_clock + 2*CLK_TCK) {
			ask_for_repeat_report ();
			new_repeat_report = TRUE;
			last_repeat_clock = now_clock;
		}
	}
	repeat_report_code = 0;
	return (getch ());
}

short closechat (void)
{
	reset_serial_port ();
	reset_screen ();
	return 0;
}

void ctrlchandler (short sig)
{
	closechat ();
	exit (sig);
}

static void do_command (unsigned char kbch)
{
	short
		irpt;
	KB_CMD
		kb_cmd;

	repeat_report_code = 0;

	/* zero out all previous report requests */
	for (irpt=0; irpt < 0x200; irpt++) reply [irpt] = 0;

	/* open up command window */
	new_cmd_window ();

	/* 0x06, 0x07, 0x0C, 0x0D do not generate TSIP commands; */
	/* service them separately. */
	switch (kbch) {
	case 0x06:
		/* '^F' - continuous binary file storage control */
		if (tsipstream == NULL)
		{
			tsipfiletype = TSIPFILE;
			open_file_storage (&tsipstream, tsipfilename, tsipfiletype);
		}
		else
		{
			close_file_storage (&tsipstream, tsipfilename, tsipfiletype);
		}
		break;

	case 0x07:
		/* '^G' - continuous binary file storage control */
		if (tsipfiletype == TSIPFILE && tsipstream != NULL) {
			close_file_storage (&tsipstream, tsipfilename, tsipfiletype);
		}
		tsipfiletype = AUTOFILE;
		break;

	case 0x09:
		/* '^I' - change serial port parameters.
		/* When changing serial port settings, suppress TSIP parsing. */
		serial_port_hold = TRUE;
		set_buf_serial_port ();
		serial_port_hold = FALSE;
		break;

	case 0x0D:  /* <CR> - just blanks out command window */
		break;

	case 0x17:  /* '^W' - setting continuous report command */
		menu_cmd_prompt (REPEAT_MENU, &kb_cmd);
		if (kb_cmd.kbch != 0x1B) {
			repeat_report_code = kb_cmd.replycode;
			ask_for_repeat_report ();
		}
		break;

	case '?':   /* '?' - Keystroke Command List */
		kbch = menu_cmd_prompt (ALL_MENU, &kb_cmd);
		if (kb_cmd.kbch != 0x1B) {
			switch_window (AUTOWIN);
			do_command (kb_cmd.kbch);		/* recursive call */
		}
		break;

	case '!':
		/* Creates a file GPSALM.DAT that contains all information */
		/* required to do a warm start. */
		xprintf ("Transfer GPSALM.DAT from rcvr to PC?");
		clreol ();
		if (!ask_verify_nocmdcrlf ()) break;
		if (!almgetb ()) {
			show_crlf();
			xprintf ("Storage aborted");
		}
		break;

	case '@':
		/* Load a file GPSALM.DAT that contains all information */
		/* required to do a warm start. */
		xprintf ("Transfer GPSALM.DAT from PC to rcvr?");
		clreol ();
		if (!ask_verify_nocmdcrlf ()) break;
		if (!almputb ()) {
			show_crlf();
			xprintf ("Loading aborted");
		}
	   break;

	case '#':
		show_crlf();
		xprintf ("TSIP_IFC version %s:%d",
			TSIP_VERNUM, TSIP_H_DEFINED);
		show_crlf();
	   break;

	case 0x1B:
		/* open up command window */
		highvideo ();
		show_crlf();
		show_crlf();
		xprintf (
"******** Press ESC to exit program; or press any other key to continue *********");
		show_crlf();
		show_crlf();
		normvideo ();
		break;

	default: /* normal TSIP commands */
		proc_kbd (kbch, reply);
		break;
	}
	/* return to auto-report mode */
	switch_window (AUTOWIN);
}

short ParseCommandLine (short argc, char *argv[])
{
	short
		iarg, comport, retval;
	char
		*argvi;

	retval = TRUE;
	/* provisions for command line arguments starting with '-' or '/' */
	for (iarg = 0; iarg < argc; iarg++) {
		argvi = argv[iarg];
		if (argvi[0] != '-' && argvi[0] != '/') continue;

		if (argvi[1] == 'c' || argvi[1] == 'C')
		{
			comport = argvi[2] - '0';

			/* returns TRUE if serial port failure */
			retval = initialize_serial_port (comport);
		}
		if (argvi[1] == 'f' || argvi[1] == 'F') {
			if (argvi[2] == '\0')
			{
				tsipfiletype = AUTOFILE;
			}
			else
			{
				strcpy (tsipfilename, &argvi[2]);
				open_file_storage (&tsipstream, tsipfilename, tsipfiletype);
			}
		}
	}
	return retval;
}
/**/
/* ************************************************************** */
/* ****************  main routine for tsiptalk ****************** */
/* ************************************************************** */

void main (short argc, char *argv[])
{
	unsigned char
		last_kbch,
		kbch;


	if (ParseCommandLine(argc, argv))
	{
		show_crlf();show_crlf();
		printf ("\nSyntax error. Correct Syntax:");
		printf ("\nTSIPCHAT -c<n> -f<filename>");
		printf ("\nComm Port must be specified by command line argument");
		printf ("\n-c1 for COM1, -c2 for COM2, -c0 for SPDRIVE");
		printf ("\nBinary output file name optional: -f<filename>");
		printf ("\nIf no filename specified, output is divided into hourly files.\n");
		exit(0);
	}

	/* clear screen and start at the bottom */
	initialize_screen ();

	/* initialize two screen setup */
	switch_window (CMDWIN);
	highvideo ();
	xprintf (
		"   TSIPCHAT %s(%s), TSIP Receiver Control Interface Software",
		TSIPCHAT_VERNUM, TSIP_VERNUM);
	show_crlf ();
	xprintf (
		"           (c) Copyright 1993,94,95,96,97 Trimble Navigation, Ltd.");
	show_crlf ();
	xprintf (
		"               Press '?' to access the Keystroke Command List");
	normvideo ();

	switch_window (AUTOWIN);

	signal (SIGINT, ctrlchandler);

	kbch = last_kbch = 0;

	/* MAIN LOOP:  read bytes from buffered serial port input; */
	/* if kbhit, do corresponding command */
	/* double ESC to quit program */

	for (;;)
	{
		kbch = read_rpts_wait_for_kbhit ();
		if (kbch==0x1B && last_kbch==0x1B) break;
		do_command (kbch);
		last_kbch = kbch;
	};

	closechat ();
}

