// #include "Arduino.h"
#include <Keyboard.h>
#include <string.h>
#include <EEPROM.h>

#define version "Login Assistant Version 2.1"


		
//#define allPrintsOn

	#ifdef allPrintsOn
		//#define key
		#define dP
		#define dP2
		#define dP5
		#define dP9
	#endif

unsigned long StartForTimeStampULong = millis();
/**********************************************************************************

The user populates their data via a terminal emulator like Tera Term. They see:

             Login Assistant

         Menu
         Show the current account
         Change this account
         Advance to the next account
         Return to being a Login Assistant after saving changes

Enter the first letter of any of these actions.

If they type "c", they get

Current account name: <the current account name>

menu|account name|URL|user name|password|return to main menu

Type "m" to print this sub menu, "a" to change the account name, "U" to change the URL, "u" to change the user name, "p" to change the password, and "r" to return to the main menu.


The data is stored in the ATMEGA32U4's EEPROM which can hold 1024 bytes. Each field is 32 bytes so a maximum of 8 accounts can be stored.
*************************************************************************************/



#define length(array) (sizeof(array)/sizeof((array)[0]))

//#define DIAG
#define USEKEYBOARD

/* the current maximum length of all entries is 32 but any one can be made longer */

const byte maximumEntryLengthByte = 32;

enum states { //define editing states which gate which functions can respond
viewAccounts,
editCurrentAccount,
};

states editState = viewAccounts; //init

typedef struct {
    char accountName[maximumEntryLengthByte];
    char url[maximumEntryLengthByte];
    char username[maximumEntryLengthByte];
    char password[maximumEntryLengthByte];
} account;

account accounts[8]; //the EEPROM has room for 8 accounts


/* Define button and nearby logic 0 logical pin numbers */
const byte Button1 = 9;
const byte Button2 = 10;
const byte logicZeroA = 7; //button 1 connects GPIO 9 to 7 when pushed
const byte logicZeroB = 14;//button 2 connects GPIO 4 to 6 when pushed

const byte outputAccountNames = 0;
const byte outputContentsOfCurrentAccount = 1;

const byte URLpart = 0;
const byte usernamePart = 1;
const byte passwordPart = 2;
const byte wrapBackToURL  = 3;

byte currentTextLengthByte = 0; //is used when backspacing over text displayed in the navigation window of the browser
char command = 'M'; 
char emptyField[maximumEntryLengthByte] = {0};
boolean returnToLoginAssistantBool = false;
const byte keyboardDelay = 10; //milliseconds spent with simulated keyboard button held down or released
const byte buttonReadDelay = 10; //sampling time in milliseconds for reading buttons 1 and 2

byte buttonsJustPressed = 0; //bits are 0 0 0 0  0 0 B2 B1 

byte recordNumber = 0; //there are a total of 8 records

char bufferText[maximumEntryLengthByte] = {0};//Null in first element

byte i = 0;//used as index in for loops

void checkButtonState() {
    /* Make a persistent private variable to keep track of our button States */
    static byte currentButtonStates = 0;
    /* Make a persistent private variable that keeps track of the last time we checked buttons */
    static unsigned long lastReadTimeOfButtons = 0;
    /* Make a release mask so that we don't double trigger when waiting for button releases */
    static byte buttonsIgnored = 0;
    
    /* If it has been less than buttonReadDelay milliseconds... */
    if(millis() < lastReadTimeOfButtons + buttonReadDelay) {
        /* Clear our main loop variables so nothing gets retriggered */
        buttonsJustPressed = 0;
        /* We are done */
        return;
    }
    
    /* Since we are reading, set our next read for buttonReadDelay milliseconds from now */
    lastReadTimeOfButtons = millis() + buttonReadDelay;
    /* Get a copy of the old button values */
    byte oldButtonStates = currentButtonStates;
    /* Read in the new button values */
    byte newButtonStates = ((!digitalRead(Button2)) << 1) | (!digitalRead(Button1));
    currentButtonStates = newButtonStates;
    
    #ifdef DIAG
        Serial.print("B");
    	Serial.print(currentButtonStates);
    	Serial.println(" held");
	#endif
    
    /* Get the changed buttons */
    byte changedButtons = oldButtonStates ^ newButtonStates; //create a mask to identify all buttons that changed state
    
    /* If something changed, check if it changed to pressed */
    buttonsJustPressed = changedButtons & newButtonStates; //use the mask to show which buttons are now pressed. 
    
    #ifdef DIAG
        Serial.print("B");
    	Serial.print(buttonsJustPressed);
    	Serial.println(" pressed");
	#endif
    
	/*If there are no pressed buttons... */
	if(newButtonStates == 0) {
		/* unlock the release lock */
		buttonsIgnored = 0; //prevents double trigger when waiting for button releases
	}
	
	/* If we are waiting for the pushed button to be released... */
	if (buttonsIgnored) { 
	/* if any button is being ignored, buttonsIgnored has a non-zero value so this test evaluates to true */
		buttonsJustPressed = 0; //since we are ignoring all buttons, we say no buttons were just pushed
		return;
	}
	
	buttonsIgnored = buttonsJustPressed; //to get here, no button was being held down; all button are locked
	#ifdef DIAG
    	Serial.print("B");
    	Serial.print(buttonsJustPressed);
    	Serial.println(" trigged");
    #endif
}

void ClearText() {
    #ifdef DIAG
        Serial.println("CLEARING TEXT BOX");
    #endif
    #ifdef USEKEYBOARD
        for (byte i = 0; i < currentTextLengthByte; i++) {
            Keyboard.press(KEY_BACKSPACE);
            delay(keyboardDelay);
            Keyboard.release(KEY_BACKSPACE);
            delay(keyboardDelay);
        }
    #endif
}

void WriteText(char* printMe) {
	
	currentTextLengthByte = strlen(printMe);//use when later backspacing over this text
	if (currentTextLengthByte > maximumEntryLengthByte) { currentTextLengthByte = maximumEntryLengthByte;}
	
    #ifdef DIAG
        Serial.print(printMe);
    #endif
    #ifdef USEKEYBOARD
        Keyboard.print(printMe);
    #endif
}


/* start by pointing to the last account so that the first press of button 1 wraps around to the first account */
byte currentAccount = length(accounts) - 1;
void MoveToNextAccount() {
    /* If we are not looking at the end account... */
    if(currentAccount != length(accounts) - 1) {
        /* Move to the next account */
		currentAccount = currentAccount + 1;
		//if this account name is Null, advance until I find a non-Null account name. If all 8 accounts and Null, return currentAccount equal to 0.
		
		byte cycleCount = 0;
		while(accounts[currentAccount].accountName[0] == '\0'){//skip all accounts with null account names
			currentAccount = currentAccount + 1;
			if(currentAccount > 7)currentAccount = 0;//wrap back to 0
			if(cycleCount >= 7){
				currentAccount = 0;
			break;//after going through all accounts, all were Null so set currentAccount to 0 and leave.
			}
			cycleCount++;
		}
    }
    /* Otherwise... */
    else {
            /* Start at the beginning of the list of accounts*/
            currentAccount = 0;
    }
}

 
void ClearTextAndDisplayNextAccount() {
	/*Clear out the textbox we are sitting in */
	ClearText();
    /* Move to the next account and write the name to the box */
    MoveToNextAccount();
    WriteText(accounts[currentAccount].accountName);
}

byte whichPartOfAccount = URLpart;
void WriteCurrentAccountInfo() {
    char* textToPrint;
    switch(whichPartOfAccount) {
        case URLpart:
            textToPrint = accounts[currentAccount].url;
        break;
        
        case usernamePart:
            textToPrint = accounts[currentAccount].username;
        break;
        case passwordPart:
            textToPrint = accounts[currentAccount].password;
        break;
    }
	
    WriteText(textToPrint);//output URL, username, or password
    
    whichPartOfAccount = whichPartOfAccount + 1;//advance to next part of account
    if (whichPartOfAccount >= wrapBackToURL) { whichPartOfAccount = URLpart; }//after password, cycle back to URL if button 1 pushed again
}

void setup() {
	pinMode(Button1, INPUT_PULLUP);// make logical pin Button1 an input and turn on the pullup resistor so it goes high unless connected to ground
	pinMode(Button2, INPUT_PULLUP);//make logical pin Button2 an input and turn on the pullup resistor so it goes high unless connected to ground
	
	pinMode(logicZeroA, OUTPUT);//provides a logic 0 for Button1  because it is closer to pin 9 than the ground pin
	digitalWrite(logicZeroA, LOW);
	pinMode(logicZeroB, OUTPUT);//provides a logic 0 for Button2
	digitalWrite(logicZeroB, LOW);
	
    #ifdef DIAG
        Serial.begin(9600);//warning: do not enable Serial and Keyboard at same time or you may get run time errors
    #endif
    
	#ifdef USEKEYBOARD
        Keyboard.begin();//warning: do not enable Serial and Keyboard at same time or you may get run time errors
    #endif
	
					#ifdef dP 
					delay(3000);
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					#endif 
	
	replaceAll_0xFF_with_x();//manufacturer inits EEPROM to 0xFF which is nonprinting. I'll replace them with 'x'	if necessary
	readAllOfEEPROM();//read all of the ATMEGA32U4's 1024 bytes of EEPROM and place it into accounts[]
					
					#ifdef dP 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					#endif
				
}

boolean wrongInitialButtonPushed = true;//if button 2 is pushed right after power up, this flag being 1 causes the version number to be displayed. It is set to 0 after button 1 is pushed.
boolean secondAndSubsequentPushesOfButton2 = false;//if button 2 is pushed a second time after power up, this flag changes to 1 display a message saying to push button 1.
byte currentButtonState = 0;//this is the state variable. 

//we start with the keyboard active
void loop()
{	
    //if terminal emulator connected and a key is pressed, stop looking at buttons and focus on this input until user asked to return to being a Login Assistant

	if(scanForTopMenuCommandAndOutputTitle()){//if any key pressed, output main menu. and clear command. UART is switched from Keyboard to Serial	
		recordNumber = 0; //reset pointer to first record 
		
					#ifdef dP5 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("command = "));
					Serial.println(command);
					#endif 
					
		
		while(1){//once a keystroke has been detected, stay focused on keyboard until user says to exit back to scanning buttons
		
					#ifdef dP2
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("command = "));
					Serial.println(command);
					Serial.print(F("editState = "));
					Serial.println(editState);
					#endif
					
			if(editState == viewAccounts)outputMenuQ();//Menu|Show|Change|Advance|Return   no state change
			if(editState == viewAccounts)showEntireCurrentAccountQ();//no state change
			if(editState == viewAccounts)changeCurrentAccountQ();//this changes editState to editCurrentAccount			
			if(editState == viewAccounts)advanceToNextAccountQ();//no state change
			if(editState == viewAccounts)returnToBeingLoginAssistantQ();//no state change but returnToLoginAssistantBool is set true so we break out of this while()
			if(editState == editCurrentAccount)outputSubMenuQ();//Menu|Account name|URL|Youser name|password|Return to main menu.  //editState changed to editCurrentAccount
			if(editState == editCurrentAccount)changeCurrentAccountNameQ();//no state change
			if(editState == editCurrentAccount)changeCurrentAccountURL_Q();//no state change
			if(editState == editCurrentAccount)changeCurrentAccountUsernameQ();//no state change
			if(editState == editCurrentAccount)changeCurrentAccountPasswordQ();//no state change
			if(editState == editCurrentAccount)returnToMainMenuAndDisplayIt();//this changes editState to viewAccounts			

			if(returnToLoginAssistantBool)break;//change to dealing with buttons. I must break out while loop.
			
					#ifdef dP2
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("command = "));
					Serial.println(command);
					#endif
					
			scanForCommand();//after servicing the first keystroke, scan for the next one and come around again
			
					#ifdef dP2
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("command = "));
					Serial.println(command);
					#endif
					
		}
	}
	//no key press detected, we check buttons for the rest of the loop() cycle and then check for key press again
	Serial.end();
	Keyboard.begin();
	delay(1);
	checkButtonState();
    switch(buttonsJustPressed){
        /* Nothing pressed */
        case 0:
            break;
        
        /* Button 2 pressed */
        case 2:
        {
            if(wrongInitialButtonPushed) {//if Button 2 is pushed right after power up, the user will see the version number. They should have pushed button 1.
                ClearText();
                if (!secondAndSubsequentPushesOfButton2) {//so is the first push of button 2 after power up
                    WriteText(version);
                } else {//if Button 2 is pushed a second time right after power up, they will be told to push button 1.
                    WriteText("Push the Scroll button.");
                }
                secondAndSubsequentPushesOfButton2 = true;//addional pushes of button 2 after power up will get "Push the Advance button."
                break;
            }
            /* after button 1 has already been pushed, advance to the next state when button 2 pushed */
            currentButtonState = currentButtonState + 1;
            if (currentButtonState >= 2) { currentButtonState = outputAccountNames; }//when currentButtonState reaches or exceeds 2, clear it back to 0.
            
            /* Reset account to the end of list so that the next cycle is the first account name in the list */
            if(currentButtonState == outputAccountNames) { //this state outputs account names so pushing button 2 selects the current account
                currentAccount = length(accounts) - 1; // WHAT IS HE DOING HERE?
            } else { //currentButtonState must be 1 which outputs the contents of the current account.
                /* Reset at entering URL */
                whichPartOfAccount = URLpart;
                ClearText();//clear account name since URL will be in the same window
            }
        }
            
        /* Button 1 pressed */
        case 1:
        {
            wrongInitialButtonPushed = false;//once button 1 has been pushed after power up, button 2 is a valid action
            /* If we are selecting the account... */
            if(currentButtonState == outputAccountNames) { //display account names state
                ClearTextAndDisplayNextAccount();
            }
            /* If we are printing out account info so sequence through URL, login and password as button 1 is pushed repeatedly*/
            else if (currentButtonState == outputContentsOfCurrentAccount) {  //display contents of current account state
                WriteCurrentAccountInfo();
            }
            break;
        }
        
        /* Button 3 pressed. There is no button 3 right now*/
        case 4:
        /* Buttons 1 and 2 pressed */
        case 3:
		/* Buttons 1 and 3 pressed */
        case 5:
		/* Buttons 2 and 3 pressed */
        case 6:
		/* Buttons 1, 2 and 3 pressed */
        case 7:
			/* Do nothing, since these combinations are unused */
            break;
           
        default:
            /* Putting this in just in case something goes wrong... */
            /* Stops the keyboard and stays here doing nothing forever */
            #ifdef DIAG
                Serial.println("Stopped keyboard");
            #endif
            #ifdef USEKEYBOARD
                Keyboard.end();
            #endif
            while(1){}
    }
}//end of loop()


bool scanForTopMenuCommandAndOutputTitle(){
//detect if laptop connected. If so, check for any incoming keystroke. Print full menu.	Scan for second keystroke and save it into command.
	Keyboard.end();//switch UART from being accessed by keyboard driver and start being accessed by serial driver
	Serial.begin(9600);
	delay(1);
	if(Serial.available() > 0){ //if there is at least one character in the buffer, process first one
		Serial.read();//I do read just to clear buffer
		//Serial.print(command);//echo back keystroke.
		//Serial.println();
		title();
		Serial.println();
		while(Serial.available() < 1){}//wait for input
		command = Serial.read();

					#ifdef dP5 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("command = "));
					Serial.println(command);
					#endif 
		
		editState = viewAccounts;//defensive
		return true;//we got a command
	}else{
		Serial.end();//switch UART from being accessed by serial driver and start being accessed by keyboard driver
		Keyboard.begin();
		delay(1);
		return false;//if no incoming command, return to scan of buttons
	}
}

void scanForCommand(){
	while(Serial.available() < 1){}//wait for input
	command = Serial.read();
	
					#ifdef dP2
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));	
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("command = "));
					Serial.println(command);
					#endif
					
	return;
}


void scanForAnswer(){
	while(Serial.available() <1){}//wait for answer
	command = Serial.read();	
}

void outputMenuQ(){	
	if(test('m')){
	//if ((command == 'M') || (command == 'm')){
		command = 'x';//retire command		
		outputMenu();
		editState = viewAccounts;//defensive
	}
}

void title(){
		Serial.println();
		Serial.println(F("             Login Assistant"));
		fullOutputMenu();
		Serial.println(F("Enter the first letter of any of these actions."));
		Serial.println();
		command = 'x';//retire whatever initial keystroke received
}

void fullOutputMenu(){
		Serial.println();
		Serial.println(F("         Menu"));
		Serial.println(F("         Show the current account"));
		Serial.println(F("         Change this account"));
		Serial.println(F("         Advance to the next account"));		
		Serial.println(F("         Return to being a Login Assistant"));
		Serial.println();
}
void outputMenu(){
	Serial.println();
	Serial.println(F("Menu|Show|Change|Advance|Return"));
}


void outputSubMenuQ(){
	if(test('m')){
		command = 'x';
		Serial.println();
		outputSubMenu();
		editState = editCurrentAccount;
	}
}

void outputSubMenu(){
		Serial.println(F("menu|account name|URL|user name|password|return to main menu"));
		/*
			Serial.println();
			Serial.println(F("Enter the first letter of any menu item."));
			Serial.println();
			Serial.println(F("             Menu"));
			Serial.println(F("             Change this element"));
			Serial.println(F("             Advance to next element"));
			Serial.println();
		*/
}


void showEntireCurrentAccountQ(){
	if(test('s')){
		command = 'x';//retire command
		
						#ifdef dPd 
						Serial.print(__FUNCTION__);
						Serial.print(F("(): "));		
						Serial.print(__LINE__);
						Serial.print(F(".    Time Stamp  "));	
						Serial.print (millis() - StartForTimeStampULong);
						Serial.println(F(" ms"));
						Serial.print(F("recordNumber = "));
						Serial.println(recordNumber);
						Serial.print(F("accounts[recordNumber].accountName = "));
		
	
						//printEntry(accounts[recordNumber].accountName);
						#endif 
						
		Serial.println();
		Serial.print(F("Account "));
		Serial.print(recordNumber+1);//user will see 1 - 8 rather than 0 - 7
		Serial.print(F(" name is:   "));
		if(accounts[recordNumber].accountName[0] == 0){
			Serial.println(F("<empty>"));
		}else{
			Serial.println(accounts[recordNumber].accountName);
		}
		
		Serial.print(F("URL is:              "));
		if(accounts[recordNumber].url[0] == 0){
			Serial.println(F("<empty>"));
		}else{
			Serial.println(accounts[recordNumber].url);
		}
		
		Serial.print(F("User name is:        "));
		if(accounts[recordNumber].username[0] == 0){
			Serial.println(F("<empty>"));
		}else{
			Serial.println(accounts[recordNumber].username);
		}
		
		Serial.print(F("Partial password is: "));
		if(accounts[recordNumber].password[0] == 0){
			Serial.println(F("<empty>"));
		}else{
			Serial.println(accounts[recordNumber].password);
		}		
		Serial.println();
		editState = viewAccounts;//defensive
	}
}


void advanceToNextAccountQ(){
//I show all 8 accounts because I can't tell what the user wants to do with them.
	if(test('a')){
		command = 'x';//retire command
		recordNumber++;
		if(recordNumber > 7)recordNumber = 0;//wrap back to start
		Serial.println();
		Serial.print(F("Account "));
		Serial.print(recordNumber+1);
		Serial.print(F(" name is: "));
		if(accounts[recordNumber].accountName[0] == 0){
			Serial.println(F("<empty>"));
		}else{
			Serial.println(accounts[recordNumber].accountName);
		}
	editState = viewAccounts;//defensive
	}
}


void changeCurrentAccountQ(){
	if(test('c')){//this is "C" from main menu
		command = 'x';//retire command
		Serial.println();		
		Serial.print(F("Current account name: "));
		if(accounts[recordNumber].accountName[0] == 0){
			Serial.println(F("<empty>"));
		}else{
			Serial.println(accounts[recordNumber].accountName);
		}
		Serial.println();
		outputSubMenu();
		editState = editCurrentAccount;
	}
}


void returnToBeingLoginAssistantQ(){
	if(test('r')){
		command = 'x';//retire command	
		Serial.println();
		Serial.print(F("We are returning to being a Login Assistant."));
		Serial.println();
		Serial.end();//switch from Serial to keyboard
		Keyboard.begin();
		returnToLoginAssistantBool = true;
		editState = viewAccounts;//defensive
		return;
	}
}


void changeCurrentAccountNameQ(){//command came from main menu but we exit in sub menu
	if(test('a')){
		command = 'x';//retire command	
		Serial.println();
		Serial.println(F("Type new account name and then press Enter."));
		printFieldMessage();
		readInputUntilEnter();//output is bufferText
		//save the array bufferText into this accountName

						
						#ifdef dP 
						Serial.print(__FUNCTION__);
						Serial.print(F("(): "));		
						Serial.print(__LINE__);
						Serial.print(F(".    Time Stamp  "));	
						Serial.print (millis() - StartForTimeStampULong);
						Serial.println(F(" ms"));
						Serial.print(F("bufferText = "));
						Serial.println(bufferText);
						#endif
		
		moveArray(accounts[recordNumber].accountName,bufferText);
		saveChangeToEEPROM();
		
						#ifdef dP 
						Serial.print(__FUNCTION__);
						Serial.print(F("(): "));		
						Serial.print(__LINE__);
						Serial.print(F(".    Time Stamp  "));	
						Serial.print (millis() - StartForTimeStampULong);
						Serial.println(F(" ms"));
						#endif 
		
		Serial.println();
		Serial.println(F("Change accepted."));
		Serial.println();
		outputSubMenu();
		Serial.println();
		editState = editCurrentAccount;//defensive
	}
}//end of changeCurrentAccountNameQ()


void changeCurrentAccountURL_Q(){
	if(command == 'U'){
		command = 'x';
		Serial.println();
		Serial.println(F("type new URL and then press Enter."));
		printFieldMessage();
		readInputUntilEnter();//output is bufferText
		//save the array bufferText into this url
		moveArray(accounts[recordNumber].url,bufferText);
		saveChangeToEEPROM();
		Serial.println();
		Serial.println(F("Change accepted."));
		Serial.println();
		outputSubMenu();
		Serial.println();
		editState = editCurrentAccount;//defensive
	}
}	


void changeCurrentAccountUsernameQ(){
	if(command == 'u'){
		command = 'x';
		Serial.println();
		Serial.println(F("type new user name and then press Enter."));
		printFieldMessage();
		readInputUntilEnter();//output is bufferText
		moveArray(accounts[recordNumber].username,bufferText);
		saveChangeToEEPROM();
		Serial.println();
		Serial.println(F("Change accepted."));
		Serial.println();
		outputSubMenu();
		Serial.println();
		editState = editCurrentAccount;//defensive		
	}
}


void changeCurrentAccountPasswordQ(){
	if(test('p')){
		command = 'x';
		Serial.println();
		Serial.println(F("type new partial password and then press Enter."));
		printFieldMessage();
		readInputUntilEnter();//output is bufferText
		moveArray(accounts[recordNumber].password,bufferText);
		saveChangeToEEPROM();
		Serial.println();
		Serial.println(F("Change accepted."));
		Serial.println();
		outputSubMenu();
		editState = editCurrentAccount;//defensive
		}
}				


void readInputUntilEnter(){	//output is in bufferText which is a char array

				#ifdef dPd 
				Serial.print(__FUNCTION__);
				Serial.print(F("(): "));		
				Serial.print(__LINE__);
				Serial.print(F(".    Time Stamp  "));	
				Serial.print (millis() - StartForTimeStampULong);
				Serial.println(F(" ms"));
				#endif 

	byte count = 0;
	char character;
	while(1){//record text name which can be up to maximumEntryLengthByte characters. If fewer, Enter, which is decimal 10, is the last character
		while(Serial.available() < 1){} //wait for user to enter characters
		
				#ifdef dP 
				Serial.print(__FUNCTION__);
				Serial.print(F("(): "));		
				Serial.print(__LINE__);
				Serial.print(F(".    Time Stamp  "));	
				Serial.print (millis() - StartForTimeStampULong);
				Serial.println(F(" ms"));
				#endif 
		
		delay(1);//give time for input buffer to fill
		character = Serial.read();			
		if(character == 8){
			//user entered backspace
			count--;//move back one position in bufferText
			Serial.write(8);//move back one position on screen
			Serial.print(" ");//erase character on screen
			Serial.write(8);//move back one position on screen to complete processing of user pressing backspace
			//skip processing of this character and look for a new one
		}else{
			if((character < maximumEntryLengthByte) || (character > 127))character = 0;//map all non-printable characters to Null
					
						#ifdef dP
						Serial.println();
						Serial.print(__FUNCTION__);
						Serial.print(F("(): "));		
						Serial.print(__LINE__);
						Serial.print(F(".    Time Stamp  "));	
						Serial.print (millis() - StartForTimeStampULong);
						Serial.println(F(" ms"));
						Serial.print(F("character in decimal = "));
						Serial.println(character,DEC);
						#endif
							
			if(character == 0){
				
						#ifdef dP
						Serial.println();
						Serial.print(__FUNCTION__);
						Serial.print(F("(): "));		
						Serial.print(__LINE__);
						Serial.print(F(".    Time Stamp  "));	
						Serial.print (millis() - StartForTimeStampULong);
						Serial.println(F(" ms"));
						#endif
				Serial.println();//end of string so simulate Enter response
			}else{
				Serial.print(character);//echo back keystroke
			}
			
						#ifdef dP
						Serial.println();
						Serial.print(__FUNCTION__);
						Serial.print(F("(): "));		
						Serial.print(__LINE__);
						Serial.print(F(".    Time Stamp  "));	
						Serial.print (millis() - StartForTimeStampULong);
						Serial.println(F(" ms"));
						Serial.print(F("count = "));
						Serial.println(count);
						#endif 
						
			bufferText[count] = character;//accumulate characters
			count++;//advance pointer to next available character position
			if(character == 0){
				
						#ifdef dP 
						Serial.print(__FUNCTION__);
						Serial.print(F("(): "));		
						Serial.print(__LINE__);
						Serial.print(F(".    Time Stamp  "));	
						Serial.print (millis() - StartForTimeStampULong);
						Serial.println(F(" ms"));
						Serial.print(F("bufferText = "));
						Serial.println(bufferText);
						#endif
						
				eraseRemainingCharacters(count);//don't leave any old data in memory even if it should not print
				return;//user is done entering string
			}
			if(count > (maximumEntryLengthByte -1)){ //all fields are maximumEntryLengthByte characters. count is the next available character position so maximumEntryLengthByte means full
				Serial.println();
				Serial.println(F("You are at the character limit so I will save what you have entered."));
				return;
			}
		}
	}		
}//end of readInputUntilEnter()
	

void readAllOfEEPROM(){
/*******************************************************************************************
read all of the ATMEGA32U4's 1024 bytes of EEPROM and place it into accounts[]

byteCountUInt goes from 0 to 1023. I take blocks of 32 bytes at a time and sequentially fill accounts[]
*******************************************************************************************/

					#ifdef dP1 
					delay(3000);
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.println (millis() - StartForTimeStampULong);
					#endif 
					
unsigned int byteCountUInt = 0;
recordNumber = 0;
byte localCharacterCount = 0;

					#ifdef dP1 
					//delay(1000);
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("byteCountUInt = "));
					Serial.println(byteCountUInt);
					Serial.println();
					Serial.print(F("EEPROM.read(byteCountUInt) = "));
					Serial.println(EEPROM.read(byteCountUInt));
					#endif 

	while(1){
		
					#ifdef dP1 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("byteCountUInt = "));
					Serial.println(byteCountUInt);
					#endif ++;
		
		//accountName
		for(localCharacterCount = 0; localCharacterCount < maximumEntryLengthByte; localCharacterCount++){	
			bufferText[localCharacterCount] = EEPROM.read(byteCountUInt);
			byteCountUInt++;
		}//bufferText[] now contains accountName for record recordNumber
		moveArray(accounts[recordNumber].accountName,bufferText);
		
					#ifdef dP1 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("byteCountUInt = "));
					Serial.println(byteCountUInt);
					#endif 
					
		//URL
		for(localCharacterCount = 0; localCharacterCount < maximumEntryLengthByte; localCharacterCount++){	
			bufferText[localCharacterCount] = EEPROM.read(byteCountUInt);
			byteCountUInt++;
		}//bufferText[] now contains url for record recordNumber
		moveArray(accounts[recordNumber].url,bufferText);
		
					#ifdef dP1 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("byteCountUInt = "));
					Serial.println(byteCountUInt);				
					#endif 
					
		//user name
		for(localCharacterCount = 0; localCharacterCount < maximumEntryLengthByte; localCharacterCount++){	
			bufferText[localCharacterCount] = EEPROM.read(byteCountUInt);
			byteCountUInt++;
		}//bufferText[] now contains username for record recordNumber
		moveArray(accounts[recordNumber].username,bufferText);
		
					#ifdef dP1 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("byteCountUInt = "));
					Serial.println(byteCountUInt);					
					#endif 
					
		//password
		for(localCharacterCount = 0; localCharacterCount < maximumEntryLengthByte; localCharacterCount++){	
			bufferText[localCharacterCount] = EEPROM.read(byteCountUInt);
			byteCountUInt++;
		}//bufferText[] now contains password for record recordNumber	
		moveArray(accounts[recordNumber].password,bufferText);
		
					#ifdef dP1 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("byteCountUInt = "));
					Serial.println(byteCountUInt);					
					#endif 
		
		recordNumber++;//current record is now full so advance to next record unless we are at 1023.
		//32 bytes per field, 4 fields per record, 8 records = 1024 bytes
		
					#ifdef dP1 
					Serial.print(__FUNCTION__);
					Serial.print(F("(): "));		
					Serial.print(__LINE__);
					Serial.print(F(".    Time Stamp  "));	
					Serial.print (millis() - StartForTimeStampULong);
					Serial.println(F(" ms"));
					Serial.print(F("recordNumber = "));
					Serial.println(recordNumber);
					Serial.print(F("byteCountUInt = "));
					Serial.println(byteCountUInt);
					#endif 
					
		if(byteCountUInt > 1023){
		return;
		}
	}
}

boolean writeAllOfEEPROM(){/*******************************************************************************************
write all of accounts[] into EEPROM. Do read back check and return false if there is a mismatch.
*******************************************************************************************/

	byte characterNumber = 0; 
	unsigned int byteCountUInt = 0;//keeps track of which byte in EEPROM

	for(byte recordNumber = 0; recordNumber < 8; recordNumber++){
		//account name
		for(characterNumber = 0; characterNumber < maximumEntryLengthByte; characterNumber++){
			if(writeEEPROM(byteCountUInt, accounts[recordNumber].accountName[characterNumber]) == false) return false;	
		byteCountUInt++;
		}
		//account URL
		for(characterNumber = 0; characterNumber < maximumEntryLengthByte; characterNumber++){
			if(writeEEPROM(byteCountUInt, accounts[recordNumber].url[characterNumber]) == false) return false;	
		byteCountUInt++;
		}
		//account user name
		for(characterNumber = 0; characterNumber < maximumEntryLengthByte; characterNumber++){
			if(writeEEPROM(byteCountUInt, accounts[recordNumber].username[characterNumber]) == false) return false;
		byteCountUInt++;
		}
		//account user partial password
		for(characterNumber = 0; characterNumber < maximumEntryLengthByte; characterNumber++){
			if(writeEEPROM(byteCountUInt, accounts[recordNumber].password[characterNumber]) == false) return false;	
		byteCountUInt++;
		}
	}
	return true;//all read backs succeeded
}


bool writeEEPROM(unsigned int address, byte data){
/*******************************************************************************************
the funtion returns false if any of these are true:
* address is > 1023
* readback fails
*******************************************************************************************/

	if(	address > 1023) return false;//address beyond end of EEPROM
	if (EEPROM.read(address) == data) return true;//nothing is written because it won't change
	EEPROM.write(address, data);
	if (EEPROM.read(address) == data) return true;
	return false; //otherwise, it is a readback failed
}

void moveArray(char* target, char* source){
	
							#ifdef dP1
							delay(1000);	
							Serial.print(__FUNCTION__);
							Serial.print(F("(): "));		
							Serial.print(__LINE__);
							Serial.print(F(".    Time Stamp  "));	
							Serial.print (millis() - StartForTimeStampULong);
							Serial.println(F(" ms"));
							Serial.print(F("source[0] = "));
							Serial.println(source[0]);
							#endif 
	
//move the maximumEntryLengthByte element source array's content into the same sized target array
	for(byte i = 0; i < maximumEntryLengthByte; i++){
		target[i] = source[i];
		
							#ifdef dP1
							Serial.print(__FUNCTION__);
							Serial.print(F("(): "));		
							Serial.print(__LINE__);
							Serial.print(F(".    Time Stamp  "));	
							Serial.print (millis() - StartForTimeStampULong);
							Serial.println(F(" ms"));
							Serial.print(F("i = "));
							Serial.println(i);
							Serial.print(F("target[i] = "));
							Serial.println(target[i]);
							#endif 
	
	}
}


void replaceAll_0xFF_with_x(){
	//manufacturer sets all unprogrammed EEPROM addresses to 0xFF which is nonprinting. I will replace them with 'x'
	for(unsigned int byteCountUInt = 0;byteCountUInt < 1024; byteCountUInt++){
		if(EEPROM.read(byteCountUInt)== 0xff)writeEEPROM(byteCountUInt,'x');
	}
}


void eraseRemainingCharacters(byte count){
	for(byte i = count;i < maximumEntryLengthByte;i++)bufferText[i] = 'x';
}


void printFieldMessage(){
	Serial.print(F("You can have up to "));
	Serial.print(maximumEntryLengthByte);
	Serial.println(F(" characters."));
	Serial.println(F("................................"));
}


void returnToMainMenuAndDisplayIt(){//this changes editState to viewAccounts
	if((command == 'R') ||(command == 'r')){
		command = 'x';	
		Serial.println();
		outputMenu();		
		editState = viewAccounts;
	}
}


bool test(char input){//returns true if the upper or lower case of input equals command
	if((command == (input & ~(0x20))) || (command == (input | (0x20))))return true;
	return false;
	}
	
void saveChangeToEEPROM(){
//I call this function when any data has changed because only changes to EEPROM are written. 
	if(writeAllOfEEPROM() == false){//try to save change to EEPROM
			Serial.print(F("Hardware failure. Unable to save changes."));
		}
}