//============================================================================
// Name        : fsdwClient.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C, Ansi-style
//============================================================================

#include "fsdwClient.h"

#define MAX_MSGLEN 1024
void error(const char *msg) {
	perror(msg);
	exit(0);
}

const char * program_name;
//  File Descriptor for output;
FILE * outputfds = stdout;

//	Default ports
int cmdport = 1700;
int dataport = 1701;

//	Server Address
char * ipAddress = "localhost";

//	Verbose option
bool verbose = false;

void print_usage(FILE * fds, int exit_code) {
	fprintf(fds, "Usage: %s options [ipaddress]\n", program_name);
	fprintf(fds, "  -h --help                  Display this usage information.\n");
	fprintf(fds, "  -o --output [filename]     Set the output file for all messages.\n");
	fprintf(fds, "  -C --cmdport [number]      Select the tcp port for commands.\n");
	fprintf(fds, "  -D --dataport [number]     Select the tcp port for data.\n");
	fprintf(fds, "  -v --verbose               Print verbose data to STDOUT.\n");

	exit(exit_code);

}

/*
 * This reader function interprets piped data for textual command intepretation into the class
 * Command Summary:
 * 	a: test comms by querying version number.
 * 	e: enable ping on channel w/ 1/0 (enable/disable), channels are esb, esshf,esslf, e (all
 * 		channels). EXAMPLE: e1 (all on), e0 (all off), esb1 (subbottom on)
 * 	p: print status to output file establish during class constructor.
 * 	i: query common settings
 *
 */

void reader() {

	char buffer[1024];

	EdgeTechHandler * Handler = new EdgeTechHandler(ipAddress, cmdport, dataport, outputfds);

	//Test Connection
	if (!Handler->isConnected()) {
		perror("main: Handler not connnected");
		exit(ERROR_NOCONNECTION);
	} else {
		Handler->Initialize();
		fprintf(stdout, "Handler Connected, Ready for commands.\n");
	}

	//	Test Communications
	/*
	 * Here is a Routine for for checking comms each loop cycle and attempting
	 * a reconnect and state assessment after regaining connection.
	 */
//	int i = 10;
//	while (i > 0) {
//
//		if (!Handler->IsHealthy()) { //sends a test message to validate communication
//
//			fprintf(stdout, "ERROR, comms lost\n");
//			//Attempt Connection and ReInitialize
//			if (Handler->Connect()) {
//				Handler->Initialize();
//
//			}
//		}
//		i--;
//		sleep(3);
//	}
//	Start processing stream commands from the parent process
	//struct pollfd fd;
	struct timeval timeout;
	fd_set readfds;
	int iActivity;
	timeout.tv_sec = (time_t) floor((double) (POLL_TIME_MILLISECONDS / 1000));
	timeout.tv_usec = (suseconds_t)((POLL_TIME_MILLISECONDS % 1000) * 1000);

	//fd.fd = STDIN_FILENO;
//	fd.events = POLLIN;
//	fd.revents = 0;
	double floatval;
	//int timeout = POLL_TIME_MILLISECONDS;
	while (!feof(stdin) && !ferror(stdin) && buffer[0] != 'q') {
		FD_ZERO(&readfds);

		FD_SET(STDIN_FILENO, &readfds);

		iActivity = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &timeout);
		if (FD_ISSET(STDIN_FILENO, &readfds)) {	//(poll(&fd, 1, timeout) > 0) && (fd.revents & POLLIN != 0)) {
			fgets(buffer, sizeof(buffer), stdin);
			fflush (stdin);
			//fprintf(stdout, "GOT: %s", buffer);

//			We shouldn't care about keyboard input then.
//			if (!Handler->isConnected()) continue;

//		We accept single letter commands to start
			switch (buffer[0]) {
			case 'a': //ACK
				if (Handler->IsHealthy())
					fprintf(stdout, "System communcations confirmed.\n");
				else {
					fprintf(stdout, "No response from Edgetech system.\n");
//
				}
				break;
			case 'c':
				Handler->SetTimeFromSystem(false);
				break;
			case 'd':
				if (!strncmp(buffer, "dsshf", 5)) {
					floatval = atof(&buffer[5]);
					if (floatval == 0 && buffer[5] != '0')
						break; //bad atof
					Handler->SetPulseDuration(SUBSYSID_SSHF, floatval, true);
				} else if (!strncmp(buffer, "dsslf", 5)) {
					floatval = atof(&buffer[5]);
					if (floatval == 0 && buffer[5] != '0')
						break; //bad atof
					Handler->SetPulseDuration(SUBSYSID_SSLF, floatval, true);
				} else if (!strncmp(buffer, "dsb", 3)) {
					floatval = atof(&buffer[3]);
					if (floatval == 0 && buffer[3] != '0')
						break; //bad atof
					Handler->SetPulseDuration(SUBSYSID_SB, floatval, true);
				} else if (!strncmp(buffer, "d", 1)) { //all channels
					floatval = (float) atof(&buffer[1]);
					if (floatval == 0 && buffer[1] != '0')
						break; //bad atof
					Handler->SetPulseDuration(SUBSYSID_SB, floatval, true);
					Handler->SetPulseDuration(SUBSYSID_SSHF, floatval, true);
					Handler->SetPulseDuration(SUBSYSID_SSLF, floatval, true);
				}
				break;
			case 'l':
				if (buffer[1] == '1' || buffer[1] == '0') {
					Handler->EnableStorage(((buffer[1] == '1') ? true : false), true);
				}

				break;
			case 'D':
				//Send Depth Value
				if (strlen(buffer) > 1) {
					floatval = (float) atof(&buffer[1]);
					if (floatval == 0 && buffer[1] != '0')
						break; //bad atof
					Handler->SendPressureData(0, 1, 2, 3, 4, floatval, 5);
					Handler->SendAltitudeData(0,1,3,4);
					ulonglong timestamp;
					Handler->SendCompositeSituation(0,1,timestamp,3,4,5,6,7,8,9,10,11,12,13,14,15);
					timestamp.HighPart = 0;
					timestamp.LowPart  = 0;
					Handler->SendSituation(0,timestamp,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28);
				}
				break;



			case 'e':
				if (buffer[1] == '1' || buffer[1] == '0') {
					Handler->EnablePingAllChannels((buffer[1] == '1') ? true : false, true);
				} else if ((!strncmp(buffer, "esb", 3)) && (buffer[3] == '1' || buffer[3] == '0')) {
					Handler->EnablePing(SUBSYSID_SB, (buffer[3] == 1) ? true : false, true);
				} else if ((!strncmp(buffer, "esshf", 5)) && (buffer[5] == '1' || buffer[5] == '0')) {
					Handler->EnablePing(SUBSYSID_SSHF, (buffer[5] == '1') ? true : false, true);
				} else if ((!strncmp(buffer, "esslf", 5)) && (buffer[5] == '1' || buffer[5] == '0')) {
					Handler->EnablePing(SUBSYSID_SSLF, (buffer[5] == '1') ? true : false, true);
				} else
					fprintf(stdout, " eX - enable/disable (X?1:0) pings on all channels\n");

				break;

			case 'g':
				if (!strncmp(buffer, "gsshf", 5)) {
					floatval = atof(&buffer[5]);
					if (floatval == 0 && buffer[5] != '0')
						break; //bad atof
					Handler->SetTxPower(SUBSYSID_SSHF, floatval, true);
				} else if (!strncmp(buffer, "gsslf", 5)) {
					floatval = atof(&buffer[5]);
					if (floatval == 0 && buffer[5] != '0')
						break; //bad atof
					Handler->SetTxPower(SUBSYSID_SSLF, floatval, true);
				} else if (!strncmp(buffer, "gsb", 3)) {
					floatval = atof(&buffer[3]);
					if (floatval == 0 && buffer[3] != '0')
						break; //bad atof
					Handler->SetTxPower(SUBSYSID_SB, floatval, true);
				} else if (!strncmp(buffer, "g", 1)) { //all channels
					floatval = (float) atof(&buffer[1]);
					if (floatval == 0 && buffer[1] != '0')
						break; //bad atof
					Handler->SetTxPower(SUBSYSID_SB, floatval, true);
					Handler->SetTxPower(SUBSYSID_SSHF, floatval, true);
					Handler->SetTxPower(SUBSYSID_SSLF, floatval, true);
				}
				break;

			case 'o':
				if (!strncmp(buffer, "osshf", 5)) {
					Handler->PingOnce(SUBSYSID_SSHF);
				} else if (!strncmp(buffer, "osslf", 5)) {
					Handler->PingOnce(SUBSYSID_SSLF);
				} else if (!strncmp(buffer, "osb", 3)) {
					Handler->PingOnce(SUBSYSID_SB);
				} else { //Subbottom is default.
					Handler->PingOnce(SUBSYSID_SB);
				}
				break;

			case 'p':
				if (!strncmp(buffer, "psshf", 5)) {
					floatval = atof(&buffer[5]);
					if (floatval == 0 && buffer[5] != '0')
						break; //bad atof
					Handler->SetPingRate(SUBSYSID_SSHF, floatval, true);
				} else if (!strncmp(buffer, "psslf", 5)) {
					floatval = atof(&buffer[5]);
					if (floatval == 0 && buffer[5] != '0')
						break; //bad atof
					Handler->SetPingRate(SUBSYSID_SSLF, floatval, true);

				} else if (!strncmp(buffer, "psb", 3)) {
					floatval = atof(&buffer[3]);
					if (floatval == 0 && buffer[3] != '0')
						break; //bad atof
					Handler->SetPingRate(SUBSYSID_SB, floatval, true);

				} else if (!strncmp(buffer, "p", 1)) { //all channels
					floatval = (float) atof(&buffer[1]);
					if (floatval == 0 && buffer[1] != '0')
						break; //bad atof
					Handler->SetPingRate(SUBSYSID_SB, floatval, true);
					Handler->SetPingRate(SUBSYSID_SSHF, floatval, true);
					Handler->SetPingRate(SUBSYSID_SSLF, floatval, true);
				}
				break;
			case 'r':
				if (!strncmp(buffer, "rsshf", 5)) {
					floatval = atof(&buffer[5]);
					if (floatval == 0 && buffer[5] != '0')
						break; //bad atof
					Handler->SetRange(SUBSYSID_SSHF, floatval, true);
				} else if (!strncmp(buffer, "rsslf", 5)) {
					floatval = atof(&buffer[5]);
					if (floatval == 0 && buffer[5] != '0')
						break; //bad atof
					Handler->SetRange(SUBSYSID_SSLF, floatval, true);

				} else if (!strncmp(buffer, "rsb", 3)) {
					floatval = atof(&buffer[3]);
					if (floatval == 0 && buffer[3] != '0')
						break; //bad atof
					Handler->SetRange(SUBSYSID_SB, floatval, true);

				} else if (!strncmp(buffer, "r", 1)) { //all channels
					floatval = (float) atof(&buffer[1]);
					if (floatval == 0 && buffer[1] != '0')
						break; //bad atof
					Handler->SetRange(SUBSYSID_SB, floatval, true);
					Handler->SetRange(SUBSYSID_SSHF, floatval, true);
					Handler->SetRange(SUBSYSID_SSLF, floatval, true);
				}
				break;

			case 's':
				Handler->printStatus(stdout);
				break;

			case 't':
				if (!strncmp(buffer, "tsshf", 5)) {
					switch (buffer[5]) {
					case ('I'):
						Handler->SetTriggerMode(SUBSYSID_SSHF, Handler->triggerModeInternal, true);
						break;
					case ('E'):
						Handler->SetTriggerMode(SUBSYSID_SSHF, Handler->triggerModeExternal, true);
						break;
					case ('C'):
						Handler->SetTriggerMode(SUBSYSID_SSHF, Handler->triggerModeCoupled, true);
						break;
					case ('G'):
						Handler->SetTriggerMode(SUBSYSID_SSHF, Handler->triggerModeGated, true);
						break;

					default:
						fprintf(stdout, " tX or t<ch>X- settriggermode (X? I = internal, E = external, C = coupled, G = gated\n");
						break;
					}
				} else if (!strncmp(buffer, "tsslf", 5)) {
					switch (buffer[5]) {
					case ('I'):
						Handler->SetTriggerMode(SUBSYSID_SSLF, Handler->triggerModeInternal, true);
						break;
					case ('E'):
						Handler->SetTriggerMode(SUBSYSID_SSLF, Handler->triggerModeExternal, true);
						break;
					case ('C'):
						Handler->SetTriggerMode(SUBSYSID_SSLF, Handler->triggerModeCoupled, true);
						break;
					case ('G'):
						Handler->SetTriggerMode(SUBSYSID_SSLF, Handler->triggerModeGated, true);
						break;
					default:
						fprintf(stdout, " tX or t<ch>X- settriggermode (X? I = internal, E = external, C = coupled, G = gated\n");
						break;
					}
				} else if (!strncmp(buffer, "tsb", 3)) {
					switch (buffer[3]) {
					case ('I'):
						Handler->SetTriggerMode(SUBSYSID_SB, Handler->triggerModeInternal, true);
						break;
					case ('E'):
						Handler->SetTriggerMode(SUBSYSID_SB, Handler->triggerModeExternal, true);
						break;
					case ('C'):
						Handler->SetTriggerMode(SUBSYSID_SB, Handler->triggerModeCoupled, true);
						break;
					case ('G'):
						Handler->SetTriggerMode(SUBSYSID_SB, Handler->triggerModeGated, true);
						break;
					default:
						fprintf(stdout, " tX or t<ch>X- settriggermode (X? I = internal, E = external, C = coupled, G = gated\n");
						break;
					}
				} else if (!strncmp(buffer, "t", 1)) { //all channels
					switch (buffer[1]) {
					case ('I'):
						Handler->SetTriggerMode(SUBSYSID_SB, Handler->triggerModeInternal, true);
						Handler->SetTriggerMode(SUBSYSID_SSHF, Handler->triggerModeInternal, true);
						Handler->SetTriggerMode(SUBSYSID_SSLF, Handler->triggerModeInternal, true);
						break;
					case ('E'):
						Handler->SetTriggerMode(SUBSYSID_SB, Handler->triggerModeExternal, true);
						Handler->SetTriggerMode(SUBSYSID_SSHF, Handler->triggerModeExternal, true);
						Handler->SetTriggerMode(SUBSYSID_SSLF, Handler->triggerModeExternal, true);

						break;
					case ('C'):
						Handler->SetTriggerMode(SUBSYSID_SB, Handler->triggerModeCoupled, true);
						Handler->SetTriggerMode(SUBSYSID_SSHF, Handler->triggerModeCoupled, true);
						Handler->SetTriggerMode(SUBSYSID_SSLF, Handler->triggerModeCoupled, true);
						break;
					case ('G'):
						Handler->SetTriggerMode(SUBSYSID_SB, Handler->triggerModeGated, true);
						Handler->SetTriggerMode(SUBSYSID_SSHF, Handler->triggerModeGated, true);
						Handler->SetTriggerMode(SUBSYSID_SSLF, Handler->triggerModeGated, true);
						break;
					default:
						fprintf(stdout, " t<X> or t<ch><X> - set triggermode (X? I = internal, E = external, C = coupled, G = gated\n");
						break;
					}
				} else {
					fprintf(stdout, " t<X> or t<ch><X>- set triggermode (X? I = internal, E = external, C = coupled, G = gated\n");
				}

				break;

			case 'v':
				if (!strncmp(buffer, "vg", 2) && strlen(buffer) > 2) {
					floatval = atof(&buffer[2]);
					if (floatval == 0 && buffer[2] != '0')
						break; //bad atof
					if (Handler->isAGCEnabled() == true) {
						fprintf(stdout, "ERROR: AGC is already enabled, disable to change gain.\n");
						break;
					}
					Handler->SetRxGain(floatval, true);
				} else if (!strncmp(buffer, "v", 1) && strlen(buffer) > 1) {
					if (buffer[1] == '1' || buffer[1] == '0') {
						Handler->EnableAGC((buffer[1] == '1') ? true : false, true);
					}
				}
				break;
			case 'P':
				if (!strncmp(buffer, "Psshf", 5) && strlen(buffer) > 5) {

					Handler->SetPulseFile(SUBSYSID_SSHF, &buffer[5], strlen(buffer) - 6, true);
				} else if (!strncmp(buffer, "Psslf", 5) && strlen(buffer) > 5) {
					Handler->SetPulseFile(SUBSYSID_SSLF, &buffer[5], strlen(buffer) - 6, true);

				} else if (!strncmp(buffer, "Psb", 3) && strlen(buffer) > 3) {
					Handler->SetPulseFile(SUBSYSID_SB, &buffer[3], strlen(buffer) - 4, true);
				} else
					fprintf(stdout, " P - P<chan>X \n");

				break;

			case 'i':
				Handler->QueryCommonSettings();
				break;
			case 'q':
				Handler->Disconnect();
				exit(0);
				break;
			case '?':
			default:
				fprintf(stdout, "Commands are as follows:\n"
						"   <chan> is 'sb','sslf','sshf'\n"
						" a - (a)ck test unit communications.\n"
						" c - set (c)lock time from pc.\n"
						" e<X> or e<ch><X>- (e)nable/disable (X?1:0) pings on all channels\n"
						" i - (i)nquire common settings\n"
						" l<X> - (l)ogging enable/disable (X?1:0)"
						" o<ch> - ping (o)nce on <ch>annel [SB is default]\n"
						" s - print (s)tatus\n"
						" d<ch><X> - set pulse duration in milliseconds\n"
						" p<ch><X> - set ping rate in pings per second\n"
						" r<X> or r<ch><X> - set (r)ange in meters [0 is max]\n"
						" t<X> or t<ch><X> - set(t)riggermode (X? I = internal, E = external, C = coupled, G = gated\n"

						" v<X>  - Enable/disable recei(v)e gain on subbottom channel (X?1:0)\n"
						" vg<X> - Set receive gain on subbottom in percent\n"
						" P - set (P)ulsefile name P<ch><X> \n"
						" g<X> or g<ch><X> - set transmit (g)ains in percent\n"
						" D<X> - send a (D)epth message with depth value X in meters\n"

						"\n q - (q)uit test program\n");

				break;
			}

		} else if (!Handler->isConnected()) { // Not connected, let's try again.
			fprintf(stdout, "Attempting to reconnect in 5 seconds...\n");
			sleep(5);
			if (Handler->Connect()) {
				// We reconnected, re-initialize
				Handler->Initialize();
			}
		} else { // Nothing to send and we are connected, poll ports for information.

			Handler->ReceiveMessage();

		}
	}
}

int main(int argc, char *argv[]) {

	const char* const short_options = "hC:D:o:v";
//	const struct option long_options[] = { { "help", 0, NULL, 'h' }, { "cmdport", 1, NULL, 'C' }, { "dataport", 1, NULL, 'D' }, { "output", 1, NULL, 'o' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } };

//	Where to point program output
	const char* output_fname = NULL;

//	Program Name
	program_name = argv[0];

	int next_option;
	do {
		next_option = getopt(argc, argv, short_options);
//		next_option = getopt_long(argc, argv, short_options, long_options, NULL);

		switch (next_option) {
		case 'h':
			print_usage(stdout, 0);
			break;
		case 'o':
			output_fname = optarg;
			outputfds = fopen(output_fname, "a");
			break;
		case 'C':
			cmdport = atoi(optarg);
			if (cmdport == 0) {
				fprintf(stderr, "Unknown Command Port: %s\n", optarg);
				exit(ERROR_BAD_INPUT);
			}
			break;
		case 'D':
			dataport = atoi(optarg);
			if (dataport == 0) {
				fprintf(stderr, "Unknown Data Port: %s\n", optarg);
				exit(ERROR_BAD_INPUT);
			}
			break;
		case 'v':
			verbose = true;
			break;
		case '?':
			print_usage(stderr, 1);
			break;
		case -1:
			break;
		default:
			abort();
		}
	} while (next_option != -1);

//	Process non option arguments
	if ((argc - optind) >= 1) {
		ipAddress = argv[optind];
	}

	reader();

	return 0;
}
