/*
 * 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;




/*
 * 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.\r\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 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.
 ****************************************************************/
void printCurrentTime(void)
{
	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 current epoch msec time according to the RTC.
 ****************************************************************/
void printCurrentEpochTime(void)
{
    char timeStamp[TIMESTAMPLENGTH];
    uint32_t posixtime_ms, ticks;
    struct tm *time_struct;

    uprintf("\r\n");
    //get the time from the from the RTC so we can print it
    posixtime_ms = ROM_HibernateRTCGet();
    ticks = posixtime_ms;

    posixtime_ms -= (2208963732);       // hacked offset  https://www.epoch101.com/  subtract what rtc is displaying in teraterm
    posixtime_ms *= 1000LL;         // make it msec
    sprintf(timeStamp, "%13llu", posixtime_ms);
    uprintf("%s", timeStamp);

    uprintf("   ");

    //debug
    time_struct = localtime((const time_t*)&ticks);
    strftime(timeStamp, sizeof(timeStamp),"%m/%d/%y %H:%M:%S", time_struct);
    uprintf("%s", timeStamp);
    uprintf("\r\n");
}

/*
 * 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("\r\n\r\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( "\r\n\r\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\r\n\r\n\r\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();      // THOM, why flush???  This gets rid of the key
		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
}

uint8_t get_key_m2m(void)
{
    uint8_t rxBuf[5];

    // Substitute

#ifdef NOCODE
    while(1)
    {
        // Loop till user hits <CR>
        if(UARTPeek('\r') >= 0)
        {
            UARTgets(buff, 5);
            break;
        }
    }
#endif
    return rxBuf[0]; // Return the key hit
}



/******************************************************************
 *  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
}


/******************************************************************
 *  getChar()
 *
 *  assumes UARTRxBytesAvail() was called and a char is ready to be read
 *  blocks waiting for char if non available in rx buf
 *****************************************************************/
int getChar(void)
{
    return UARTgetc();
}


/*
 * if a char is available, return it.
 */
char rx_console_byte(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
}
