using namespace std;
#define SERIALPORT "/dev/ttyS0" // port the device is plugged in to
#define BAUDRATE B38400  // baud rate the device spits out at
#define UPDATE_RATE 20   // update speed in Hz  (probably set anywhere from 10-50

#include <iostream>
#include <sys/time.h>   // timers
#include <signal.h>     // timers / serial
#include <termios.h>    // serial
#include <unistd.h>     // serial, file
#include <fcntl.h>      // serial, file
// string help:  http://www.bgsu.edu/departments/compsci/docs/string.html

string serial_buffer; // Unprocessed data off the serial port
int    fd_serial;

void timer_handler(int x);       // used in automation
void serial_handler(int status); // interrupt function called on new data (position isn't guaranteed), pass to ReadSerial()


int main() {
	struct termios tty;    // will be used for new port settings
	struct termios oldtty; // will be used to save old port settings

	fd_serial = open(SERIALPORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
	if (fd_serial < 0) {
		printf("\nUnable to write to serial port (%s), are you root?\n", SERIALPORT);
		_exit(1);
	}
	tcgetattr(fd_serial, &oldtty); // save current port settings
	bzero(&tty, sizeof(tty)); // Initialize the port settings structure to all zeros
	tty.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD | CRTSCTS; // 8N1
	tty.c_iflag = IGNPAR;
	tty.c_oflag = 0;
	tty.c_lflag = 0;
	tty.c_cc[VMIN]  = 0; // 0 means use-vtime
	tty.c_cc[VTIME] = 1; // time to wait until exiting read (tenths of a second)

	tcflush(fd_serial, TCIFLUSH); // flush old data
	tcsetattr(fd_serial, TCSANOW, &tty); // apply new settings
	fcntl(fd_serial, F_SETOWN, getpid()); // enable our PID to receive serial interrupts
	fcntl(fd_serial, F_SETFL, FASYNC);
	
	
	struct sigaction saio; // set the serial interrupt handler
	saio.sa_handler = serial_handler; // to this function
	sigemptyset(&saio.sa_mask);   // clear existing settings
	saio.sa_flags = 0;            // make sure sa_flags is cleared
	saio.sa_restorer = NULL;      // no restorer
	sigaction(SIGIO, &saio, NULL); // apply new settings


// set up the main timer
	struct itimerval timer1;  // set up the timers
	signal(SIGALRM, timer_handler); // call this function on rollover
	timer1.it_interval.tv_sec  = 0;     // reset val
	timer1.it_interval.tv_usec = (__suseconds_t)((1.0/(double)UPDATE_RATE)*1000000); // reset val (converts UPDATE_RATE from Hz into microseconds
	timer1.it_value.tv_sec     = timer1.it_interval.tv_sec;  // initial val
	timer1.it_value.tv_usec    = timer1.it_interval.tv_usec; // initial val
	setitimer(ITIMER_REAL, &timer1, NULL); // apply new settings

	
	while (1) { } // press ctrl-c to exit the program

	tcsetattr(fd_serial, TCSANOW, &oldtty); // restore the old port settings before quitting
}



void serial_handler (int status) {
	// this function is called whenever there is new data to be read off the serial port.
	// It has to execute quickly, so the data processing is *not* done here.
	char temp_buffer[256*2];      // max chars to read at once, if you don't read the entire buffer then the function will get called again
	int len = read(fd_serial, temp_buffer, sizeof(temp_buffer)); // do the actual read
	temp_buffer[len] = 0;         // null terminate the string  **important**
	serial_buffer += temp_buffer; // append what we read to the serial_buffer of unprocessed data
}




void timer_handler(int x) {
	// this function gets called UPDATE_RATE times per second, it handles the data processing off the serial port
	// ** note that serial_buffer gets populated from serial_handler
	signal(SIGALRM, timer_handler); // reset, so it timers more than once

	int infinite_loop_preventer = 0; // sanity check to make sure this section won't run forever
	if (serial_buffer.length() > 0) { // unprocessed data exists
		int start_pos; // position of the first $ char  (all packets must start with a $
		int end_pos_n; // position of the first \n char following the first $
		int end_pos_r; // position of the first \r char following the first $
		int end_pos;   // set to the lesser of end_pos_n or end_pos_r

		do {
			infinite_loop_preventer++;
			// 1) find start char
			// 2) find end char (\n or \r)
			// 3) process that data
			// loop while there's more data

			start_pos = serial_buffer.find("$", 0);
			end_pos_n = serial_buffer.find("\n", start_pos);
			end_pos_r = serial_buffer.find("\r", start_pos);
			end_pos = end_pos_n < end_pos_r ? end_pos_n : end_pos_r; // choose the lesser of the two ending points

			if (start_pos != string::npos && end_pos != string::npos) {
				string data = serial_buffer.substr(start_pos, end_pos-start_pos); // just the current packet
				serial_buffer = serial_buffer.substr(end_pos+1); // remove parsed section
//				cout << "Debug: ";
//				cout << " start_pos=" << start_pos << ";";
//				cout << " end_pos=" << end_pos << ";";
//				cout << " data.len=" << data.length() << ";";
//				cout << endl << "  data=" << data << ";";
//				cout << endl << "  serial_buffer=" << serial_buffer << ";";
//				cout << endl;
				cout << data << endl;
			}
		} while (start_pos != string::npos && end_pos != string::npos && infinite_loop_preventer < 10000); // keep processing if buffer has end char in it
	}
}
