//#include <ModbusRtu.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <Bounce2.h>

#define ID   1

#define NUM_BUTTONS 6
const uint8_t BUTTON_PINS[NUM_BUTTONS] = {22, 23, 24, 25, 26, 27};
Bounce * buttons = new Bounce[NUM_BUTTONS];

bool button_UP; 
bool button_LEFT;
bool button_RIGHT;
bool button_DOWN;
bool button_ESC;
bool button_OK;

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // initalize the display
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
//Create instance
//Modbus slave(ID, Serial, 0); //Node ID. 0 for master, 1-247 for slave
                        //Serial port (0 = TX: 1 - RX: 0)
                        //Serial protocol. 0 for RS-232 + USB (default), any pin greater than 1 for RS-485

bool MainMenuEn = false;
bool SubMenuEn = false;
int8_t MainMenuCnt = 0;
int8_t MainMenuItems = 6;
char *MainMenuStr[] = {"Steps per mm","Limit up","Limit down","Anti dive","PID - P","PID - I","PID - D"};
char *MainMenuUnit[] = {"steps","mm","mm","V.","","",""};
int8_t MainMenuDec[] = {-3,-2,-3,-3,-3,-3,-3};
int8_t MainMenuFrac[] = {0,1,1,0,0,0,0};

typedef struct{
  char MenuStr[15];
  char MenuUnit[10];
  int8_t MenuDec;
  int8_t MenuFrac;
  float MenuValue;
}ParVar;

ParVar Item[7] = {
  {"Steps per mm","steps",-3,0,1200},
  {"Limit up","mm",-2,2,25.80},
  {"Limit down","mm",-2,2,-5.2},
  {"Anti dive","V.",-3,0,5384},
  {"PID - P","%",-3,0,5384},
  {"PID - I","sec",-3,0,5384},
  {"PID - D","sec",-3,0,5384},
  };

int8_t digit[5];
float TempValue;

bool CursorState;
unsigned long previousMillis = 0;
const long blinktime = 500;

int8_t CursorPos = 0;
int8_t maxDec = 0;
int8_t maxFrag = 0; 

const int SensorPin = A0;
int ARC_Voltage;
int ARC_Setpoint;
bool SettingsMenu;


boolean led;
int8_t state = 0;
unsigned long tempus;

uint16_t au16data[9]; //The table of records that you want to share over the network
int8_t pressCnt;

float ParStepMM = 5384;
/*********************************************************
 Program settings
*********************************************************/
void setup() {
    for (int i = 0; i < NUM_BUTTONS; i++) {
    buttons[i].attach( BUTTON_PINS[i] , INPUT_PULLUP  );       //setup the bounce instance for the current button
    buttons[i].interval(25);              // interval in ms
  }

  io_setup(); //configure inputs and outputs

  Serial.begin(19200); //Open communication as slave
  //slave.start();
  tempus = millis() + 100; //Save current time + 100ms
  digitalWrite(13, HIGH ); //Turn on the LED on pin 13 (the one on the board)

  u8g2.begin();
  u8g2.setFont(u8g2_font_6x12_tr);
  u8g2.setDrawColor(1); /* color 1 for the box */
  StartScreen();
}




/*********************************************************
 Program start
*********************************************************/
void loop() {
  for (int i = 0; i < NUM_BUTTONS; i++)  {
    // Update the Bounce instance :
    buttons[i].update();
  }
  button_UP = buttons[0].fell(); 
  button_LEFT = buttons[1].fell();
  button_RIGHT = buttons[2].fell(); 
  button_DOWN = buttons[3].fell(); 
  button_ESC = buttons[4].fell(); 
  button_OK = buttons[5].fell(); 

  ScanMenu();
  //Check the input buffer
  //state = slave.poll( au16data, 9 ); //Parameters: Table of records for information exchange
                                     //            Log table size
                                     //Returns 0 if there is no data request
                                     //Returns 1 to 4 if there was a communication error
                                     //Return more than 4 if the order was processed correctly

  if (state > 4) { //If it is greater than 4 = the order was correct
    tempus = millis() + 50; //Current time + 50ms
    digitalWrite(13, HIGH);//Turn on the led
  }
  if (millis() > tempus) digitalWrite(13, LOW );//Turn off the LED 50ms later
  
  //Update Arduino pins with Modbus board
  io_poll();
} 

/**
 * pin maping:
 * 2 - digital input
 * 3 - digital input
 * 4 - digital input
 * 5 - digital input
 * 6 - digital output
 * 7 - digital output
 * 8 - digital output
 * 9 - digital output
 * 10 - analog output
 * 11 - analog output
 * 14 - analog input
 * 15 - analog input
 *
 * pin 13 reserved to view communication status
 */
void io_setup() {
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(13, OUTPUT);

  digitalWrite(6, LOW );
  digitalWrite(7, LOW );
  digitalWrite(8, LOW );
  digitalWrite(9, LOW );
  digitalWrite(13, HIGH ); //Board pin 13 led
  analogWrite(10, 0 ); //PWM 0%
  analogWrite(11, 0 ); //PWM 0%
}

/*********************************************************
Link the register table with the pins
*********************************************************/
void io_poll() {
  // digital inputs -> au16data[0]
  // Reads the digital inputs and saves them in bits of the first variable of the vector
  // (It's the same as making a mask)
  bitWrite( au16data[0], 0, digitalRead( 2 )); //Reads Arduino pin 2 and saves it in bit 0 of the au16data[0] variable 
  bitWrite( au16data[0], 1, digitalRead( 3 ));
  bitWrite( au16data[0], 2, digitalRead( 4 ));
  bitWrite( au16data[0], 3, digitalRead( 5 ));

  // digital outputs -> au16data[1]
  // Reads the bits of the second variable and puts them on the digital outputs
  digitalWrite( 6, bitRead( au16data[1], 0 )); //Reads bit 0 of the au16data[1] variable and puts it on pin 6 of the Arduino
  digitalWrite( 7, bitRead( au16data[1], 1 ));
  digitalWrite( 8, bitRead( au16data[1], 2 ));
  digitalWrite( 9, bitRead( au16data[1], 3 ));

  // Change the PWM value
  analogWrite( 10, au16data[2] ); //The value of au16data[2] is written to the PWM output of pin 10 of the Arduino. (0=0% and 255=100%)
  analogWrite( 11, au16data[3] );
  ARC_Setpoint = au16data[3];

  // Reads analog inputs (ADC)
  ARC_Voltage= map(analogRead(SensorPin),0,1023,0,250); 
  //au16data[4] = analogRead( 0 ); //The analog value read on pin A0 is saved in au16data[4]. (0=0v and 1023=5v)
  au16data[4] = ARC_Voltage;
  au16data[5] = 0;

  // Communication diagnostics (for debugging)
  //au16data[6] = slave.getInCnt();  //Returns how many messages were received
  //au16data[7] = slave.getOutCnt(); //Returns how many messages were transmitted
  //au16data[8] = slave.getErrCnt(); //Returns how many errors there were
}

void ScanMenu(){
  if (!MainMenuEn and !SubMenuEn){
    UpdateScreen();
    if (button_ESC) {
      MainMenuEn = true;
      SettingsScreen();
    }
  }  else{
    if ((!SubMenuEn) and button_ESC) {
      MainMenuEn = false;
      StartScreen();
    }

    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= blinktime) {
      previousMillis = currentMillis;
      //ValueScreen();
      if (SubMenuEn){
        if (CursorState == false) {
          CursorState = true;
          ValueScreen();
        } else {
          CursorState = false;
          ValueScreen();
        }
      } 
    }
    if ((MainMenuEn and !SubMenuEn) and (button_DOWN or button_UP or button_OK)){
      if(button_DOWN){
        MainMenuCnt++;
        if (MainMenuCnt > MainMenuItems){
          MainMenuCnt = MainMenuItems;
        }
      }
      if(button_UP){
        MainMenuCnt--;
        if (MainMenuCnt < 0){
          MainMenuCnt = 0;
        }
      }
      SettingsScreen();
      if (button_OK) {
        SubMenuEn = true;
        TempValue = Item[MainMenuCnt].MenuValue;
        button_OK = false;
        CursorPos = 0;
        ValueScreen();
      }
    }
    if (SubMenuEn and button_ESC) {
      SubMenuEn = false;
      SettingsScreen();
    }
    if (SubMenuEn and button_OK) {
      Item[MainMenuCnt].MenuValue = TempValue;
      SubMenuEn = false;
      SettingsScreen();
    }

    if (SubMenuEn and (button_UP or button_DOWN or button_LEFT or button_RIGHT)){
      ValueScreen();
      if(button_LEFT){
        CursorPos--;
      }
      if(button_RIGHT){
        CursorPos++;
      }     
      if (CursorPos < Item[MainMenuCnt].MenuDec){
        CursorPos = Item[MainMenuCnt].MenuDec;
      }
      if (CursorPos > Item[MainMenuCnt].MenuFrac){
        CursorPos = Item[MainMenuCnt].MenuFrac;
      }
   }
  }
}

void StartScreen(){
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_t0_14b_mr);
  u8g2.drawStr(0, 9, "SP:   0 Volt");
  u8g2.drawStr(0, 22, "PV:   0 Volt");  
  u8g2.drawStr(0, 35, "Pos: -3.2 mm");
  u8g2.drawStr(0, 63, "[ESC] for settings");
  u8g2.sendBuffer();
}

void UpdateScreen(){
  char buf[10];
  u8g2.setFontMode(0);
  u8g2.setDrawColor(1);
  sprintf(buf, "%3d", ARC_Setpoint);
  u8g2.drawStr(28, 9, buf); 
  sprintf(buf, "%3d", ARC_Voltage);   
  u8g2.drawStr(28, 22, buf); 
  u8g2.sendBuffer();
}

void SettingsScreen(){
  u8g2.clearBuffer();
  u8g2.setFontMode(1);  /* activate transparent font mode */
  u8g2.drawBox(0, 0, 128, 14);
  u8g2.setDrawColor(2);
  u8g2.drawStr(15, 11, "SETTINGS MENU");
  u8g2.drawRFrame(0,30,128,16,6);

  u8g2.drawStr(10, 42,Item[MainMenuCnt].MenuStr);
  if (MainMenuCnt > 0) {
    u8g2.drawStr(10, 28,Item[MainMenuCnt-1].MenuStr);
  }
  if (MainMenuCnt < MainMenuItems) {
    u8g2.drawStr(10, 57,Item[MainMenuCnt+1].MenuStr);
  }
  if (MainMenuCnt < MainMenuItems - 1) {
    u8g2.drawStr(10, 71,Item[MainMenuCnt+2].MenuStr);
  }
  u8g2.sendBuffer();
}

void ValueScreen(){
  SplitValue();
  if(button_UP){
    if (digit[CursorPos+3] < 9){
      digit[CursorPos+3]++;
    }
    else {
      digit[CursorPos+3] = 0;
    }
  }
  if(button_DOWN){
    if (digit[CursorPos+3] > 0){
      digit[CursorPos+3]--;
    }
    else {
      digit[CursorPos+3] = 9;
    }
  }
  u8g2.clearBuffer();
  u8g2.setFontMode(1);  /* activate transparent font mode */
  u8g2.drawBox(0, 0, 128, 14);
  u8g2.setDrawColor(2);
  u8g2.drawStr(15, 11, Item[MainMenuCnt].MenuStr);
  if (CursorState) {
    if (Item[MainMenuCnt].MenuFrac > 0) {
      if (CursorPos > 0){
        u8g2.drawBox((CursorPos*12)+52+12-12, 19, 12, 18);
      } else {
        u8g2.drawBox((CursorPos*12)+52+12-24, 19, 12, 18);
      }
    } else {
      u8g2.drawBox((CursorPos*12)+52+12, 19, 12, 18);
    }
  }
  u8g2.setFont(u8g2_font_profont22_mn);
  if (MainMenuCnt == 1) {
    u8g2.setCursor(5,35);
  } else {
    u8g2.setCursor(29,35);
  }

  if ((digit[0] > 0) or ( CursorPos == -3)) {
    u8g2.print(digit[0]);
  } else {
    u8g2.print(" ");
  }
  if ((digit[1] > 0) or ( CursorPos <= -2) or (TempValue > 999)) {
    u8g2.print(digit[1]);
  } else {
    u8g2.print(" ");
  }
  if ((digit[2] > 0) or ( CursorPos <= -1) or (TempValue > 99)) {
    u8g2.print(digit[2]);
  } else {
    u8g2.print(" ");
  }
  u8g2.print(digit[3]);
  if (Item[MainMenuCnt].MenuFrac > 0) {
    u8g2.print(".");
    u8g2.print(digit[4]);
    if (Item[MainMenuCnt].MenuFrac < 3){
      u8g2.print(digit[5]);
    }
  }
  CombineValue();
  //u8g2.print((float)TempValue,MainMenuFrac[MainMenuCnt]);
  u8g2.setFont(u8g2_font_t0_14b_mr);
  //u8g2.print(MainMenuUnit[MainMenuCnt]);
  u8g2.print(Item[MainMenuCnt].MenuUnit);
  u8g2.sendBuffer();
}


void SplitValue(){
  float TempBuffer = TempValue;
  Serial.println(TempValue);
  digit[0] = (TempBuffer / 1000);
  TempBuffer = TempBuffer - (digit[0]*1000);
  digit[1] = (TempBuffer / 100);
  TempBuffer = TempBuffer - (digit[1]*100);
  digit[2] = (TempBuffer / 10);
  TempBuffer = TempBuffer - (digit[2]*10);
  digit[3] = (TempBuffer);
  TempBuffer = TempBuffer - (digit[3]);
  digit[4] = (TempBuffer * 10);
  Serial.println(digit[4]);
  TempBuffer = TempBuffer - (digit[4]*0.1);
  TempBuffer = TempBuffer * 100;
  digit[5] = round(TempBuffer);
 
  Serial.print(digit[0]);
  Serial.print(digit[1]);
  Serial.print(digit[2]);
  Serial.print(digit[3]);
  Serial.print(digit[4]);
  Serial.println(digit[5]); 
}

void CombineValue(){
  //TempValue = (digit[0]*1000.0)+(digit[1]*100.0)+(digit[2]*10.0)+(digit[3])+(digit[4]*0.1)+(digit[5]*0.01);
}