/*
 * IO.c
 *
 * Input/Output routines including time display functions
 *
 *  Created on: Jan 23, 2014
 *      Author: RCG
 */

#include "system.h"
#include "user_io.h"
#include "microsd.h"
#include "uartstdio.h"	// Use local version with larger RX buffer

extern int dbg_flag;

extern struct systemSamplingData sys_samp;

/******************************************************************
 * 	getchar_timed()
 *
 *  Get a character from the console. Return -1 if no character
 *  after timeout value in seconds.
 *****************************************************************/
int getchar_timed(int timeout)
{
	Timer_10ms(true);	// Reset timer to zero
	timeout*= 100; 		// Convert to 10 ms counts

	while( Timer_10ms(false) <= timeout )
	{
		if( UARTRxBytesAvail() )
		{
			return UARTgetc();
		}
	}

	return -1;	// Timeout
}

/*********************************************************************
 *
 */



/*
 * Print out the passed in prompt and offer user
 * a yes or no choice.
 * Returns YES (1), or NO (0).
 */
int yesOrNoMenuChoice(char *prompt, int default_response)
{
	char input;

	// Print prompt
	uprintf("%s", prompt);

	input = get_key();

	if(input == 'Y' || input == 'y') return YES;
	else if(input == 'N' || input == 'n') return NO;
	else return default_response;
}


/*
 * Print out the passed in prompt and offer user
 * a yes or no choice.  Repeat until they give a valid
 * yes or no response.
 * Enforce a TIME LIMIT (seconds).  IF NO VALID INPUT AFTER THE TIME LIMIT,
 * RETURNS TIMELIMITPASSED (-1).
 * Otherwise, returns YES (1), or NO (0).
 */
/*
int yesOrNoMenuChoiceTimeLimit(char *prompt, int timeLimit)
{
	char userInput[16];
	int charCount;
	uint32_t startTicks;
	uint32_t elapsedTicks;

	//print their prompt
	uprintf("%s", prompt);

	//Loop until they give a valid response or time is up
	startTicks = ROM_HibernateRTCGet();
	while(1)
	{
		//if user has enters a CR, store and check the string
		if(UARTPeek('\r') >= 0)
		{
			//store user input in inputBuffer.  charCount stores # of chars sent
			charCount = UARTgets(userInput, sizeof(userInput));

			//they should have only entered one character (Y or N)
			if(charCount == 1)
			{
				if(userInput[0] == 'Y' || userInput[0] == 'y')
				{
					return YES;
				}

				else if(userInput[0] == 'N' || userInput[0] == 'n')
				{
					return NO;
				}
			}

			//They didn't give a valid response.  Prompt them for a better response, and start the loop over.
			uprintf("Did not receive a valid response.  Enter Y or N.\n");
			uprintf("%s", prompt);
		}

		//else, update seconds passed, if timeLimit seconds pass, return -1
		else
		{
			elapsedTicks = ROM_HibernateRTCGet() - startTicks;
			if( (elapsedTicks > timeLimit))
			{
				return TIMELIMITPASSED;
			}
		}
	}
}
*/

/*
 * Fills in the passed in userInputBuffer with input (via UARTgets).
 * Peek for a return carriage before calling UARTgets so that
 * delete/backspace still works.
 * Returns the count of characters that were stored, not including
 * null.
 */
int getUserInput(char *userInputBuffer, int userInputBufferSize)
{
	int charCount;

	while(1)
	{
		//only store a string, if user has entered CR
		if(UARTPeek('\r') >= 0)
		{
			//store user input in userInputBuffer.  charCount stores # of chars sent
			charCount = UARTgets(userInputBuffer, userInputBufferSize);
			break;
		}
	}

	return charCount;
}




/*
 * Fills in the passed in userInputBuffer with input (via UARTgets).
 * Peek for a return carriage before calling UARTgets so that
 * delete/backspace still works.
 * Returns the count of characters that were stored, not including
 * null.
 * Enforce a TIME LIMIT (seconds).  IF NO VALID INPUT AFTER THE TIME LIMIT,
 * RETURNS TIMELIMITPASSED (-1).
 */

int getUserInputTimeLimit(char *userInputBuffer, int userInputBufferSize, int timeLimit)
{
	int charCount;
	uint32_t startTicks;
	uint32_t elapsedTicks;

	//Loop until they give a valid response or time is up
	startTicks = ROM_HibernateRTCGet();
	while(1)
	{
		//if user has enters a CR, store and check the string
		if(UARTPeek('\r') >= 0)
		{
			//store user input in inputBuffer.  charCount stores # of chars sent
			charCount = UARTgets(userInputBuffer, userInputBufferSize);
			return charCount;
		}

		//else, update seconds passed, if timeLimit seconds pass, return -1
		else
		{
			elapsedTicks = ROM_HibernateRTCGet() - startTicks;
            if( (elapsedTicks > timeLimit))
            {
                return TIMELIMITPASSED;
            }

#ifdef TRIEDTOFIXSAMPLEERROR
            // This HACK for some reason does not allow 'stop' command
			elapsedTicks = ROM_HibernateRTCGet();       // BUG FIX for serial char wakeup, ALIGN data, wakeup happens close (within 'timeLimit') to scheduled RTC sample schedule 30 Dec 2021
			if(sys_samp.nextWakeUp == elapsedTicks);
			{
				// THOM: Fix for ALIGN sample skip 30 Dec 2021
				//if (it is time to take a sample, then take it here)
			    fast_sample(1,1);
	            compute_nextWakeUp();
	            sleep(DEPLOYED, sys_samp.nextWakeUp);   // Sleep until woken by RTC or keystroke
	            // Sleep does not return...
			    return TIMELIMITPASSED;
			}
			elapsedTicks -= startTicks;
			if( (elapsedTicks > timeLimit))
			{
				return TIMELIMITPASSED;
			}
#endif
		}
	}

}

/****************************************************************
 * Print the current time according to the RTC.
 * (Prints the current time followed by a new line.)
 ****************************************************************/
void printCurrentTime()
{
	char timeStamp[TIMESTAMPLENGTH];
	uint32_t ticks;
	struct tm *time_struct;

	//get the time from the from the RTC so we can print it
	ticks = ROM_HibernateRTCGet();

	time_struct = localtime((const time_t*)&ticks);
	strftime(timeStamp, sizeof(timeStamp),"%m/%d/%y %H:%M:%S", time_struct);
	uprintf("%s", timeStamp);
}



/*
 * Print the time according to the passed in time_t value (this is
 * not necessarily the RTC's current time, just any time_t).
 * (Prints that time followed by a new line.)
 */

void print_time_t_Time(time_t time)
{
	char timeStamp[TIMESTAMPLENGTH];
	struct tm *time_struct;

	time_struct = localtime((const time_t*)&time);
	strftime(timeStamp, sizeof(timeStamp),"%m/%d/%y %H:%M:%S", time_struct);
	uprintf("%s", timeStamp);
}



/*
 * getDateAndTime
 * Queries the user for a date and time in 24 hour format.
 * Returns: Unix calendar time (time_t) in seconds
 */
time_t getDateAndTime()
{
	char monthString[3];
	char dayString[3];
	char yearString[3];
	char hourString[3];
	char minuteString[3];
	char secondString[3];

	char *p;  //helper variable

	//variables for keeping track of user text input:
	char timeInput[23];
	int charCount;

	int month;
	int day;
	int year;
	int hour;
	int minute;
	int second;

	int validInput = 1; //helper boolean

	//time variables:
	time_t rawtime;
	struct tm * timeStruct;

	// Loop until given a valid time string
	while(1)
	{
		uprintf("\n\nEnter time [");
		printCurrentTime();
		uprintf("]: ");

		validInput = 1;

		//store user input in inputBuffer.  charCount stores # of chars sent
		charCount = getUserInput(timeInput, sizeof(timeInput));

		//make sure the user has at least entered the 17 chars of MM/DD...
		if( charCount == 17)
		{
			//now check for appropriate punctuation chars
			if(timeInput[2] == '/' && timeInput[5] == '/' && isspace(timeInput[8]) && timeInput[11] == ':' && timeInput[14] == ':')
			{
				//break up the string into it's individual pieces (month, day...):
				strncpy(monthString, &timeInput[0], 2 );
				monthString[2] = '\0';

				strncpy(dayString, &timeInput[3], 2 );
				dayString[2] = '\0';

				strncpy(yearString, &timeInput[6], 2 );
				yearString[2] = '\0';

				strncpy(hourString, &timeInput[9], 2 );
				hourString[2] = '\0';

				strncpy(minuteString, &timeInput[12], 2 );
				minuteString[2] = '\0';

				strncpy(secondString, &timeInput[15], 2 );
				secondString[2] = '\0';


				//Make sure all the strings convert to an int:
				errno = 0;
				p = monthString;
				month = (int)strtol( monthString, &p, 10);
				if( (errno != 0) || (monthString == p) || (*p != 0))
				{
					// conversion failed (EINVAL, ERANGE)
					// conversion failed (no characters consumed)
					// conversion failed (trailing data)
					validInput = 0;
				}

				errno = 0;
				p = dayString;
				day = (int)strtol( dayString, &p, 10);
				if( (errno != 0) || (dayString == p) || (*p != 0))
				{
					validInput = 0;
				}

				errno = 0;
				p = yearString;
				year = (int)strtol( yearString, &p, 10);
				if( (errno != 0) || (yearString == p) || (*p != 0))
				{
					validInput = 0;
				}

				errno = 0;
				p = hourString;
				hour = (int)strtol( hourString, &p, 10);
				if( (errno != 0) || (hourString == p) || (*p != 0))
				{
					validInput = 0;
				}

				errno = 0;
				p = minuteString;
				minute = (int)strtol( minuteString, &p, 10);
				if( (errno != 0) || (minuteString == p) || (*p != 0))
				{
					validInput = 0;
				}

				errno = 0;
				p = secondString;
				second = (int)strtol( secondString, &p, 10);
				if( (errno != 0) || (secondString == p) || (*p != 0))
				{
					validInput = 0;
				}


				//if all the fields are indeed integers...
				if(validInput)
				{
					if( second >= 0 && minute >= 0 && hour >= 0 && month >= 1 && day >= 1 && year >= 0)
					{
						if( second <= 61 && minute <= 59 && hour <= 23 && day <= 31 && month <= 12)
						{
							//fill in time.h's tm struct initially (timeStruct just points to time.h's struct)
							time(&rawtime);
							timeStruct = localtime(&rawtime);

							//modify as user desired
							timeStruct->tm_sec = second;
							timeStruct->tm_min = minute;
							timeStruct->tm_hour = hour;
							timeStruct->tm_mday = day;
							timeStruct->tm_mon = month - 1;
							timeStruct->tm_year = (year + 2000) - 1900;

							//GMT HAS NO DAYLIGHT SAVINGS
							timeStruct->tm_isdst = 0;


							//remake the struct
							rawtime = mktime(timeStruct);

							//print out the time the user entered
							//uprintf( "\n\nTime is: %s", asctime(timeStruct));

							return rawtime;
						}
					}
				}
			}
		}
		else if(charCount == 0)
		{
		    return 0;
		}
		else
		{
		    uprintf("Enter a valid time string as MM/DD/YY HH:MM:SS\n\n\n");
		}
	}

}


/*
 * u_printf()
 * Prints text to console with floating point support.
 * 255 characters maximum.
 * Returns number of characters written if less than 255 (lookup vsnprintf())
 */
int uprintf(const char *format, ...)
{
   va_list args;
   int chars_written;
   static char buff[256];

   va_start (args, format);
   chars_written = vsnprintf(buff, 256, format, args);
   va_end (args);

   UARTwrite(buff, strlen(buff));

   //uprintf("%s", buff);

   return chars_written;
}

/*
 * u_printf()
 * Prints text to console with floating point support.
 * 255 characters maximum.
 * Returns number of characters written if less than 255 (lookup vsnprintf())
 */
int dbg_printf(const char *format, ...)
{
   va_list args;
   int chars_written;
   static char buff[256];

   if(dbg_flag == 0)
       return 0;

   va_start (args, format);
   chars_written = vsnprintf(buff, 256, format, args);
   va_end (args);

   UARTwrite(buff, strlen(buff));

   return chars_written;
}

/*
 * kbhit()
 * Returns true if user hits a key
 */
bool kbhit(void)
{
	if( UARTRxBytesAvail() )
	{
		UARTFlushRx();
		return true;
	}
	else return false;
}

/*
 * get_key()
 * Gets a keystroke from the console.
 * Waits for carriage return before returning with key hit.
 * Allows user to edit key hit.
 * RCG 5/14
 */
char get_key(void)
{
	char buff[5];

	while(1)
	{
		// Loop till user hits <CR>
		if(UARTPeek('\r') >= 0)
		{
			UARTgets(buff, 5);
			break;
		}
	}

	return buff[0];	// Return the key hit
}

