// LCD1602 to Arduino with function parameters and char arrays.
////Dec 6 22:08
// Displaying menus, and moving from one menu item to the next by a single button push
//The code searches the char array looking for the next menu item, finds it and places the cursor under the first letter
//This skips menu items of any length and empty spaces.
//1 press of "left" or "right" button skips to the next menu item
//Direction is controlled through A0 (for test using pot) "left" or "right" button skips to the next menu item
//As a test if it finds the "Color" label the menu is changed from the Main menu to the Color menu
//Dec 1
// Have state of menu. Function prints the relevant menu based on state.
//need expand the function that changes state of menu based on label selected

//moving this to VSC/platformIO: 
//Need #include<Arduino.h>
//Can't compare char array to string "Color" etc
//The "Color" etc have to be declared as char arrays.
//The pins of the LCD are different
//The values of the input buttons are different
//Some functions say they return, but don't - warning
//One has no return value - warning   

#include <LiquidCrystal.h>
///////////////////// LED indicators which are not needed. They show inner working of search wihtin menus
#define labelLED 2 
#define spaceLED 3
#define textLED 4

enum stateOfbutton_t {unknown, select, left, up, down, RIGHT};// 5 push button inputs read by analogue voltage
//The values read in the function were set on a simulator & will need to be changed to match the actual buttons/voltages

enum stateOfMenu_t 
{MenuUnknown, 
showMain1, 
show2Color, show2Flicker, show2Day,show2OnOff, // level 2 of hierarchy of menu

show3Color, show3Flicker, show3Day, show3OnOff,            // level 3
showNoMenu} menuState;


LiquidCrystal lcd(12, 11, 10, 9, 8, 7);// This is set on a simulator. May not theyMatch the actual hardware<<<<<

stateOfbutton_t buttonState=unknown;// buttonState is used in other parts of the software

enum pointTo_t {arrowLeft, arrowRight};

//;char instructions[4]
////menu labels
char 
Main = "MAIN",
Flicker="Flicker", 
Day="Day", 
color = "Color", 
OnOff = "OnOff", 
EXIT = "EXIT",

OutputMarker = ">OUT<",    
SelectLED="SelectLED",

Length="Length",
upDown="up/down",
ROTATE= "ROTATE",

SpeedDepth ="SpeedDepth", 
SpeedChance = "SpeedChance";     


////////////////////  new stuff

void ShowProjectName(){
  Serial.print ("ProejctName");
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("     Lights On !");
}


void displayTopRow( char* labelLeft, pointTo_t arrow,  char* labelRight ){
  lcd.setCursor(0,0);
  lcd.print(labelLeft); if(arrow == arrowLeft) lcd.print("<-"); else lcd.print("  ");//delete <-
  lcd.setCursor(9,0); 
  if(arrow == arrowRight) lcd.print("->"); else lcd.print("  ");
  lcd.print(labelRight); 
}

void displayBottomRow(){
lcd.setCursor(0,1); 
lcd.print("Select confirms ");
delay(100);
lcd.setCursor(0,1); 
lcd.print("L R to change   ");
delay(100);
lcd.setCursor(0,1); 
lcd.print("Down to next..  ");
delay(100);
lcd.setCursor(0,1); 
lcd.print("Up to go back   ");

}


void showMainMenu(){
displayTopRow( "Main" , arrowRight , "EXIT"  );
displayBottomRow();
}

void showColorMenu(){
displayTopRow( "Color" , arrowRight , "EXIT"  );
displayBottomRow();  
}

void showMenu(stateOfMenu_t  menuState, pointTo_t arrowPointer){

switch (menuState){

  case showNoMenu: ShowProjectName(); break;//does this cause problems?

  case MenuUnknown: break;//does this cause problems?

  case showMain1: displayTopRow( "Main" , arrowRight , "EXIT"  ); break;

  case show2Color:displayTopRow( "Color" , arrowRight , "EXIT"  ); break;

  case show2Flicker:displayTopRow("Flicker" , arrowRight , "EXIT"  ); break;
 
  case show2Day: displayTopRow(   "Day" , arrowRight , "EXIT"  ); break;
  
  case show2OnOff: displayTopRow( "OnOff" , arrowRight , "EXIT"  ); break;

  case show3Color:displayTopRow( "Colo3   " , arrowRight , "EXIT"  ); break;
 
  case show3Flicker:displayTopRow( "F3" , arrowRight , "EXIT"  ); break;

  case show3OnOff: displayTopRow( "onOff3" , arrowRight , "EXIT"  ); break;

 } 
}




void moveDownToNextMenuLable(stateOfMenu_t &menuState, pointTo_t &arrowPointer){
Serial.print(" In moveDownToNextMenuLabel() "); //buttonState= ");
arrowPointer = arrowRight; //point it at EXIT in new menu
//pointTo_t {arrowLeft, arrowRight};
//Serial.print( buttonState);
Serial.print(" menuState =  ");
Serial.print(menuState);
//Serial.print(" arrowPointer = ");
//Serial.print( arrowPointer);

switch (menuState){

  case showNoMenu:  break;//does this cause problems?

  case MenuUnknown: break;//does this cause problems?

  case showMain1: menuState=show2Color; break;

  case show2Color:menuState=show2Flicker; break;

  case show2Flicker:menuState=show2Day; break;

  case show2Day: menuState=show2OnOff; break;

  case show2OnOff: menuState=showMain1; break;
 
  case show3Flicker: break;

  case show3OnOff:  break;

 } 



}

void moveUPToPreviousMenuLable(stateOfMenu_t menuState){
Serial.print(" In moveUPToPreviousMenuLable() ");  
}


/////////////////////////////////////// Respond

void respondToButtons(stateOfMenu_t  &menuState, pointTo_t &arrowPointer, stateOfbutton_t buttonState)
{
//need take into acount menu state & button state
Serial.print(" In Respond  buttonState= ");
Serial.print( buttonState);
Serial.print(" menuState =  ");
Serial.print(menuState);
Serial.print(" arrowPointer = ");
Serial.print( arrowPointer);

/////////////// LEFT or RIGHT
// pointTo_t {arrowLeft, arrowRight};
if (buttonState == left || buttonState == RIGHT) 
{// swap direction of arrow
  if (arrowPointer == arrowLeft) arrowPointer = arrowRight; 
  else if (arrowPointer == arrowRight) arrowPointer = arrowLeft;
}

Serial.print(" checked LRUD ArrowPointer==");
Serial.print(arrowPointer);



///////////////// UP or DOWN
if (buttonState == down) moveDownToNextMenuLable(menuState, arrowPointer);
  else if (buttonState == up) moveUPToPreviousMenuLable(menuState);

else

/////////////////// SELECT
/*
stateOfMenu_t 
{MenuUnknown, 
showMain1, 
show2Color, show2Flicker, show2Day,show2OnOff, // level 2 of hierarchy of menu

show3Flicker, show3Day, show3OnOff,            // level 3
showNoMenu} menuState;

enum pointTo_t {arrowLeft, arrowRight};

*/

if (buttonState == select) {
//depends on menu and arrow 

Serial.print(" select ");
switch (menuState){

  case showNoMenu: if(buttonState!= unknown) menuState= MenuUnknown; // any button changes state
  buttonState =  unknown; break;//does this cause problems?

  case MenuUnknown: if(buttonState) menuState= showMain1; buttonState =  unknown;
  break; //does this cause problems?

  case showMain1: if(arrowPointer == arrowLeft) {
    menuState=showNoMenu; 
    buttonState = unknown; 
    arrowPointer = arrowRight;}
                  else if(arrowPointer == arrowRight) {
    menuState=showNoMenu; 
    buttonState = unknown;
    }
  ShowProjectName();  
   break;
//clicking either MAIN or Exit means exit

  case show2Color:if(arrowPointer == arrowLeft) {menuState=show3Color; buttonState = unknown;}
  else if(arrowPointer == RIGHT) {menuState=showMain1; buttonState = unknown;}  break;
//Right is always pointing to exit? So go up one level of menu

  case show2Flicker: if(arrowPointer == arrowLeft) {menuState=show3Flicker; buttonState = unknown;}
  else if(arrowPointer == RIGHT) {menuState=showMain1; buttonState = unknown;}  break;
//Right is always pointing to exit? So go up one level of menu break;
 
  case show2Day:  break;
  
  case show2OnOff:  break;
 
  case show3Flicker: break;

  case show3OnOff:  break;

 } 
}
Serial.print(" leaving Rspond  buttonState= ");
Serial.print( buttonState);
Serial.print(" menuState = >");
Serial.print(menuState);
Serial.print("< arrowPointer = ");
Serial.println( arrowPointer);
Serial.println();


displayBottomRow();

buttonState = unknown; //try to prevent double response to buttons
}



//////////////////////////////// setup

void setup(){
lcd.begin(16, 2);
Serial.begin(115200);
Serial.println("Newer method");
// pointTo_t {arrowLeft, arrowRight};
pointTo_t arrowPointer = arrowRight;
/*
menuState=showMain1;
menuState=show2Color;
menuState=show2Flicker;
menuState=show2OnOff;
menuState=show2Day;
*/
menuState=showMain1;
showMenu(menuState, arrowPointer);

/*
buttonState = left;

respondToButtons(menuState,arrowPointer, buttonState);
showMenu(menuState, arrowPointer);

buttonState = RIGHT;

respondToButtons(menuState,arrowPointer, buttonState);
showMenu(menuState, arrowPointer);

//prints on right but doesn't delete print on left
*/

buttonState = down;

respondToButtons(menuState,arrowPointer, buttonState);
showMenu(menuState, arrowPointer);


buttonState = left;
respondToButtons(menuState,arrowPointer, buttonState);
showMenu(menuState, arrowPointer);



delay(1000);

buttonState = select; //fails to select the menu label Color<- but menu state is 6
respondToButtons(menuState,arrowPointer, buttonState);
showMenu(menuState, arrowPointer);




}


void loop(){

}

/////////////////////////////////////// end of new stuff









/*
uint8_t DegreeBitmap[]= { 0x6, 0x9, 0x9, 0x6, 0x0, 0, 0, 0 };
byte RightArrow[8]= {
 0b00010, 
 0b00100, 
 0b01000, 
 0b10000, 
 0b0100, 
 0b00100, 
 0b00010, 
 0b00001
};
lcd.createChar(1, RightArrow);
lcd.write(1);
*/







///////////////////// The hierachy of menus for user input

char menu1Main []   {"MAIN Flicker Day Color OnOff EXIT"};//has an extra space between day & color to separate them, but that 1 space is not shown on LCD
                //    0123456789ABCDEF0123456789ABCDEF0 =33 elements

char menu2Color[]   {"COLOR: >OUT<     Select_LED  EXIT"}; // this is a 2nd level menu 
                //    0123456789ABCDEF0123456789ABCDEF0 =33 elements
char menu2Flicker[] {"FLICKER: >OUT<   Select_LED  EXIT"}; // this is a 2nd level menu 
                //    0123456789ABCDEF0123456789ABCDEF0 =33 elements
char menu2Day[]     {"DAY: Length>OUT< up/down     EXIT"}; // this is a 2nd level menu 
                  //  0123456789ABCDEF0123456789ABCDEF0 =33 elements
char menu2OnOff[]   {"ONOFF:Select_LED >OUT< onOff EXIT"}; // this is a 2nd level menu 
                  //  0123456789ABCDEF0123456789ABCDEF0 =33 elements

char menu3Color[]   {"ROTATE:  >OUT<               EXIT"}; // this is a 2nd level menu 
                  //  0123456789ABCDEF0123456789ABCDEF0 =33 elements
char menu3Flicker[] {"Speed Depth >OUT Chance      EXIT"}; // this is a 2nd level menu 
                  //  0123456789ABCDEF0123456789ABCDEF0 =33 elements
char menu3OnOff[]   {"Speed Chance    >OUT< >OUT< EXIT"}; // this is a 2nd level menu 
                  //  0123456789ABCDEF0123456789ABCDEF0 =33 elements




//stateOfMenu_t menuState = MenuUnknown;

bool row=1;  //test only
int startColumn=12 ;//TEST ONLY. Could start with 12,1 as the place where Exit is on lcd
int labelPosition;
char thisLabel[10];

char* menu_ptr=menu1Main;// Not sure if it should start with this value




///////////////////// Put the curso in position and wait so that user can see it move  

void displayCursor(int col, bool row){
lcd.setCursor(col,row);
delay(500); // the delay is just here so the movement of the cursor can be seen for testing
}


 
///////////////////// Read the Menu Label using the known position in the menu array. (So can understand what to do)

char readMenuLabel(char *menu, int labelStart){
 //lcd.print(labelStart);delay(1000);
  //char thisLabel[10]; //is that big enough for any of the labels?
// if(labelStart==0 ) thisLabel[0]='\0'; //ignore the first label which is at positin 0,0. It's the menu title
// else{
   for(int i=labelStart;i<34;i++){//menu has 33 characters including 1 non-printing space separating the rows
    thisLabel[i-labelStart] = menu[i];
    if( menu[i]==' ') { //the first space found is the end of the label. Not here if already past end of menu
    //what if label is EXIT? that is last thing in menu  i=33. Menu 34 = null?
      thisLabel[i-labelStart]='\0';
      break;
  //  }
//  lcd.print(thisLabel);
}
 
 }
//lcd.setCursor(0,0);//lcd.print(":"); /
 //lcd.clear();
 //lcd.print(thisLabel); //TEST
return thisLabel;
}
 

///////////////////// theyMatch char arrays 

int theyMatch(char a[],char b[]){
    for(int i=0;a[i]!='\0';i++){
      if (i>10) break;//my limit size
        if(a[i]!=b[i])
            return 0;
    }
    return 1;
}

///////////////////// use the label in the menu to set the menustate so that the correct one will be printed
 
int useLabelToSetMenuState(){//when select button pressed?

//lcd.print (theyMatch(thisLabel , "Color"));//returning 0 always
//lcd.print(thisLabel);
//if(theyMatch(thisLabel , "Color")) {menu_ptr=menu2Color; PrintMenu(menu_ptr);}//MOVE the print

if(theyMatch(thisLabel , "Color")) {menuState = show2Color;}//doesn't change the menu

else
//if(theyMatch(thisLabel , "Flicker")) {menu_ptr=menu2Flicker; PrintMenu(menu_ptr);}//MOVE the print
if(theyMatch(thisLabel , "Flicker")) {menuState = show2Flicker;}//
else
if(theyMatch(thisLabel , "Day")) {menuState = show2Day;}//
else
if(theyMatch(thisLabel , "OnOff")) {menuState = show2OnOff;}//
else
//

if(theyMatch(thisLabel , "EXIT")) {exitToPreviousMenu();}//

 //??? 
}


///////////////////// EXIT was selected. Move to previous menu level or leave the menus & return to LED routine

void exitToPreviousMenu(){
 lcd.setCursor(0,0); lcd.print("Exit to previous"); //

 //switch (menuState){
  // case: 
//If was main menu 
//set menu state to none
//clear LCD
//Print the name
 // Set system state back to active.

//case 
//if a level 2 menu go to main
//if level 3 go to relevant level 2
 }



///////////////////// Look for next label to LEFT in current menu               Difference: decrement index, How handle end of LCD display, order of skip

int8_t FindLabelLeft(char *menu ){  // Return value is used to set labelPosition. 

 //assuming 12,1 is where EXIT is displayed
//lcd.setCursor(12,1);//????????????????????????????????????/

int i=startColumn;
displayCursor(i, row);
lcd.cursor();// make cursor visible

if(i==0) {i=15; row = !row;}//if at start of LCD row change output to be end of other row
else {//lcd.setCursor(5,1);lcd.print(i);
while(menu[i+(row*17)]!= ' ') //skip spaces
{ i--;
if(i<=0) {i=15; row = !row;}// if reached start of row move to other row end =15
displayCursor(i, row);}
}
//found space between first lable and label to the left
digitalWrite(spaceLED, HIGH);
//delay(1000);
while(menu[i+(row*17)] == ' ')
{i--; if(i<=0) {i=15; row = !row;}// move to other row far right col =15
 displayCursor(i, row);} //skip through blank spaces bewteen labels.
 //Now found end of left label
 digitalWrite(textLED, HIGH);digitalWrite(spaceLED, LOW);
//delay(1000);
 
while(menu[i+(row*17)]!= ' ' && i>0)//skip text until find the next space
{ i--;
 displayCursor(i, row);
}// skip through the left label till find space OR i=0 Label begins at 0 or at i+1
 //Now found label to the left
 digitalWrite(labelLED, HIGH);digitalWrite(textLED, LOW);
if(i<=0) startColumn=0; else startColumn = i+1;  //The label was one column to the right, except at 0
 lcd.setCursor(startColumn,row);
//lcd.print(startColumn); delay(2000);lcd.print((startColumn+(row*17)));
return (startColumn+(row*17)); // not sure how this will be used - respond to SELECT button
//when (0,0) this returns 17 which is 16 top row + 1 not displayed space in the array
//top row is (0,0)==0 to  (15,0)==15 [added space 16] then
//bottom row (0,1)==17 to (15,1)==32
}





///////////////////// find next label to RIGHT in menu    (Difference: increment index, How handle end of LCD display, order of skip

int FindLabelRight(char *menu){ //can left & right be combined? // Return value is used to set labelPosition. 

int i=startColumn;
displayCursor(i, row);
lcd.cursor();// make cursor visible
//delay(500);

digitalWrite(textLED, HIGH);//already on text
if(i==15) {i=0; row = !row;}//if at end of LCD row, change to start of other row
else {//lcd.setCursor(5,1);lcd.print(i);
while(menu[i+(row*17)]!= ' ' && i<16)//skip text, but not past end of row
{ i++;  //lcd.setCursor(0,1);lcd.print(i);//????
//displayCursor(i, row);delay(1000);
if(i>15) {i=0; row = !row; break;}// move to other row far left col =0
displayCursor(i, row);}
delay(200);//just to make cursor movement visible.
}
//found space between first label and label to the right
digitalWrite(spaceLED, HIGH);
//delay(500);
  
while(menu[i+(row*17)] == ' ')//skip spaces
{i++; if(i>15) {i=0; row = !row;}// move to other row far right col =15
 displayCursor(i, row);
 
  } //skip through blank spaces bewteen labels.
 
 
 //Now found start of right label
digitalWrite(textLED, HIGH);digitalWrite(spaceLED, LOW);
delay(100);
digitalWrite(labelLED, HIGH);//digitalWrite(textLED, LOW);

displayCursor(i, row);


startColumn =i;//reset for the next movement from this point
return (i+(row*17)); // not sure how this will be used - respond to SELECT button
//probably need to return the position in array not the col position on LCD

}



///////////////////// react To ButtonState Find Label Or Change Menu

int reactToButtonStateFindLabelOrChangeMenu(){
 switch (buttonState){
case unknown: break;
case left:return FindLabelLeft(menu_ptr); break;//returns (i+(row*17));giving 18 should be 16
case RIGHT: return FindLabelRight(menu_ptr); break; // (i+(row*17));
case select: useLabelToSetMenuState(); break; // what to do?
 
default: return 0;
}
}


/////////////////////  print the chosen menu

void PrintMenu(char *menu ){
 int offset=0;
 for (uint8_t row = 0; row<2; row++){
  for(uint8_t i=0;i<16;i++){
  lcd.setCursor(i,row);//first 0-15 prints top row. then need skip one space in file to then print the second row
  offset = row * (17)+i; //the menu has an extra space in between upper & lower row that LCD doesn't have
  lcd.print(menu+offset) ;
  //delay(100);
  }
 }
}

/*
enum stateOfMenu_t 
{MenuUnknown, 
showMain1, 
show2Color, show2Flicker, show2Day,show2OnOff, 
show3Flicker, show3OnOff,
showNoMenu};
*/

///////////////////// Display menu based on menu state

void DisplayMenuBasedOnMenuState(){

switch (menuState){

  case showNoMenu://menu_ptr=nullptr; 
  break;//does this cause problems?

  case MenuUnknown: //menu_ptr=nullptr; 
  break;//does this cause problems?

  case showMain1: menu_ptr = menu1Main; break;

  case show2Color:menu_ptr = menu2Color; break;

  case show2Flicker:menu_ptr = menu2Flicker; break;
 
  case show2Day: menu_ptr = menu2Day; break;
  
  case show2OnOff: menu_ptr = menu2OnOff; break;
 
  case show3Flicker:menu_ptr = menu3Flicker; break;

  case show3OnOff: menu_ptr = menu3OnOff; break;

 } 

PrintMenu(menu_ptr);
};



///////////////////// Respond to user


void respondToUser(){ //if any button, set lights to 'idle' or stay out of system loop? 

DisplayMenuBasedOnMenuState();    
if (analogRead(A0) <400) buttonState=left;// need change to match shield<<<<<<<<<<<<<
else if(analogRead(A0)<800) buttonState=RIGHT;
else if(analogRead(A0)>900) buttonState=select;
//for(int i=0;i<7;i++){
  digitalWrite(2 , LOW);
  digitalWrite(3 , LOW);
  digitalWrite(4 , LOW);

int labelPosition = reactToButtonStateFindLabelOrChangeMenu();
readMenuLabel(  menu_ptr,labelPosition);//shouldn't call it here?
//depends on which menu is displayed?
delay(500);


delay(500);

 }


///////////////////// setup
/*
void setup() {
pinMode (2, OUTPUT); //digitalWrite(spaceLED, HIGH); // indicators which probably won't be in finished product
pinMode (3, OUTPUT); //digitalWrite(textLED, HIGH);
pinMode (4, OUTPUT); //digitalWrite(labelLED, HIGH);

lcd.begin(16, 2);
Serial.begin(115200);
Serial.println("Where?");
//Assuming a button has been pressed and the reaction is to set...
menuState = showMain1;//TEST only - Normally only get to this stage when button pressed <<<<<<<<<<
}

///////////////////// Loop

void loop() {


         //this loop should be separate from main LED loop or the other parts just idle?
respondToUser();// not sure which should be in this loop
//when 'select' the cursor goes all over the LCD  Need to check why. 
delay(100);//just here for test
}
 
*/