#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
 
#define TFT_DC 9
#define TFT_CS 10
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

#define up 3      //digital pin 3
#define down 2    //digital pin 2
#define select 4  //digital pin 4

#define UP 1
#define DOWN 2
#define SELECT 3

const int arraySize = 100;
bool appRunning = false;
int selected; // currently selected menu item
int upButtonState = 0;
int downButtonState = 0;
int selectButtonState = 0;
char* current_menu[arraySize] = {0};
uint8_t current_menu_index = 1; // current position in the array
uint8_t current_menu_size;
bool page_change = false;
uint8_t menu_top = 35; // top space between title and items

typedef void (* CurrentFuncPtr) (); // Function pointer for current menu
typedef void (* MenuFuncPtr) (); // Function pointer for Main Menu
typedef void (* Test1FuncPtr) (); // Function pointer for Test Menu 1
typedef void (* Test2FuncPtr) (); // Function pointer for Test Menu 2

// function prototypes
void setMenu(char** menu, CurrentFuncPtr* funcs, uint8_t size);
void buildTestMenu1(void);
void buildTestMenu2(void);
void returnMainMenu(void);

char* mainMenu[3] = {"    Main Menu",
                    "Test Menu 1   ",
                    "Test Menu 2   "
                   };

char* testMenu1[4] = {"   Test Menu 1",
                    "Option 1   ",
                    "Option 2   ",
                    "Back       "
                   };

char* testMenu2[4] = {"   Test Menu 2",
                    "Option 1   ",
                    "Option 2   ",
                    "Back       "
                   };


CurrentFuncPtr* current_func = NULL;

// Main menu func pointer
MenuFuncPtr menu_func[4] = {0,
                            buildTestMenu1,
                            buildTestMenu2};
// Test menu Func Pointer
Test1FuncPtr test1_func[5] = {0,
                            0,
                            0,
                            returnMainMenu};

Test2FuncPtr test2_func[5] = {0,
                            0,
                            0,
                            returnMainMenu};


void setup() {
  //Serial.begin(115200);

  // Button Setup
  pinMode(up, INPUT_PULLUP);

  pinMode(down, INPUT_PULLUP);

  pinMode(select, INPUT_PULLUP);

  tft.begin();
  tft.setTextWrap(false);
  tft.setRotation(3);
  tft.setTextSize(3);
  setMenu(mainMenu, menu_func, sizeof(mainMenu)/sizeof(char *)-1);
  displayMenu();
}

void menuInput(void) {
  // read the state of the pushbutton value:
  upButtonState = digitalRead(up);
  downButtonState = digitalRead(down);
  selectButtonState = digitalRead(select);

  if (upButtonState == LOW) {
    // Up
    action(UP);
  }
  else if (downButtonState == LOW) {
    // Down
    action(DOWN);
  }
  else if (selectButtonState == LOW) {
    action(SELECT);
  }
  delay(100);
}

void loop() {
  while(!appRunning) {
    menuInput();
  }
}

void returnMainMenu(void) {
  setMenu(mainMenu, menu_func, sizeof(mainMenu)/sizeof(char *)-1);
  displayMenu();
}

void buildTestMenu1(void) {
  setMenu(testMenu1, test1_func, sizeof(testMenu1)/sizeof(char *)-1);
  displayMenu();
}

void buildTestMenu2(void) {
  setMenu(testMenu2, test2_func, sizeof(testMenu2)/sizeof(char *)-1);
  displayMenu();
}


void setMenu(char** menu, CurrentFuncPtr* funcs, uint8_t size) {
  memset(current_menu, 0, arraySize);

  current_func = funcs;
  int i;
  for(i=0; i<=size; i++) {
    current_menu[i] = menu[i];
  }
  selected = 1;
  current_menu_size = i-2;
}

void displayMenu(void) {
  int i;
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);
  tft.println(current_menu[0]);
  tft.drawLine(0, 25, tft.width()-1, 25, ILI9341_GREEN);

  tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
  for(i = current_menu_index; i <= (current_menu_index + 6); i++) {
      tft.setCursor(0, ((i - current_menu_index) * 30) + menu_top);
      tft.println(current_menu[i]);
  }
  menuHighlight(selected);
}

void menuHighlight(uint8_t index) {
  if (page_change == false) {
      tft.setCursor(0, ((selected - current_menu_index) * 30) + menu_top);
      tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
      tft.println(current_menu[selected]);
  }
  tft.setCursor(0, ((index - current_menu_index) * 30) + menu_top);
  tft.setTextColor(ILI9341_YELLOW, ILI9341_BLUE);
  tft.println(current_menu[index]);
  selected = index;
  page_change = false;
}

void action(uint8_t action) {
  switch (action) {
      case 1: { // up
          if (selected <= (current_menu_index + 6) && (selected > current_menu_index)) {
              menuHighlight(selected - 1);
          }
          else if ((current_menu_index > 1) && (selected <= current_menu_index)) {
              current_menu_index = (current_menu_index - 1);
              selected = selected - 1;
              page_change = true;
              displayMenu();
          }
          break;
      }
      case 2: { // down
          if (selected >= (current_menu_index + 6) &&
              (current_menu_size > 6) && (selected <= current_menu_size)) {
              current_menu_index = (current_menu_index + 1);
              selected = selected + 1;
              page_change = true;
              displayMenu();
          }
          else if ((selected < (current_menu_index + 6)) &&
                  (selected <= current_menu_size)) {
              menuHighlight(selected + 1);
          }
          break;
      }
      case 3: // select
          current_func[selected]();//
          break;
  }
}