#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
 bool Vibrgyro;
  // СПІЛЬНІ КОНСТАНТИ ПІД ХОД ДЛЯ БАГ ФУНК
  #define no_action 0 // НЕМА ДІЇ
  #define no_data 0 // немає даних
  #define no_indication 0   // немає вказівки
  #define off 0 
  #define get_status 1               // получити настройки
  #define yes 1
  #define save_settings 4           // зберехти настройки
  #define reset 11
  #define on 12
  #define click 13
  #define execute_code 14// execution without comman - виконання без команди
  #define gyro 15
  #define profil 16
  
  #define melody_start 1
  #define melody_off_power 2

  #define x_axis_hor 1
  #define y_axis_ver 2
  #define z_axis_ver 3


  #define get_y 2
  #define get_z 3
  #define get_vibr 4
  
   //Піни карта
  //#define b_mein 25
  #define b_triger 33
  #define b_analog 32
  #define b_on_off 14
  #define b_emul_dpad 26
  #define b_sw 4
  #define p_dt 3
  #define p_clk 2
 
  #define thandle_touch_button 27

  //Mf_EncoderButton();
   //#define click 13
   #define press 20
   #define hol_but 21
   #define released 22
   #define released_const 23
   #define press_const 24
   #define hol_const 25
   #define hol_released 26
   #define status_button 27
  
  //Md_Display(byte Receiving) {
    #define reminder_display_on 20
    #define info_display_on 21
    #define menu_on 22 
    #define finishing_work 23 
    #define finishing_work_reset 24

    #define lang_eng 25
    #define lang_ukr 26 
    #define click_in_dis_on 27
  
   //Md_MainDisplayENG();
    #define menu_off 21
    #define menu_start 22
   
   //byte Md_addres();
    //#define click 13
    #define back 23
    #define address_entered 24
    #define delete_last 27
    #define get_add 29
    #define int_add 30 //introduction введення
    #define int_last 31 //introduction введення
    #define previous_address 32
    #define backpoint 33
    #define deselect 34
    
    //int Md_cursor();
    //#define reset 11
    #define selection_inversion 21
    #define freeze_position 22
    #define get_pos 23
    #define return_pos 24
    
   
    //Ae_InterruptReadPinDtEncoder()
    //Mf_Encoder();
     #define rotated_rights 20
     #define rotated_left 21
    
    
    //Mf_consenDisplay()
    //#define gyro 15
    //#define profil 16
    #define click_mod 21
    #define hold_mod 22
    
    
    //Mf_changeInspector();
    //#define gyro 15
    //#define profil 16
    #define copy_profile 21
    #define gyro_check 22
    #define return_save 23
    #define doesnt_match 24//не збігається
    #define data_match 25//збіг даних
    #define profilSp 26
    #define profilMp 27

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
  
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//char ProfileName(char Letter);
//char LetterSelect();

void setup() {
 
  pinMode(b_sw, INPUT_PULLUP);
  
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
 display.clearDisplay();
 display.setTextSize(1);
 display.setTextColor(WHITE); 
 attachInterrupt(digitalPinToInterrupt(p_dt), Ae_InterruptReadPinDtEncoder, CHANGE);
}

void loop() {
 display.clearDisplay();
 
 Name(); //принимаєм получ бук
 
 display.display();

}
 //String NameTest = "Y           fddfgdf  ";
 String NameTest = "                     ";

void Name(){
  
  static int8_t AxisX, AxisY, storedAction, inputIndicator, axisChange;
  static uint32_t tmr;
 
 char letterBig[5][10] = {
    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'},
    {'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'},
    {'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', '-'},
    {'!', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '.', '<'},
    {' ', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>'}
  };
  
  char letterSmall[5][10] = {
    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'},
    {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'},
    {'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '-'},
    {'!', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '.', '<'},
    {' ', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>'}
  };
 
 static char (*ptrLetter)[10] = letterSmall;//вказівник вказує в залежності від настройки на великі або малі букви
  
  if (!inputIndicator && ptrLetter == letterSmall && !storedAction) {//якщо видаляється остання буква одноразово всі букви робляться великими
    
    storedAction = 1;
    
    for (int i = 20; i >= 0; i--) {
      
      if (NameTest[i] != ' '){
       inputIndicator = i+1;
       break;
      }
      
      else if (!i){
       ptrLetter = letterBig;
       inputIndicator = 0;
       break;
      }
    
    }
  }
  
  else if (inputIndicator && storedAction) {
    
    storedAction = 0;
  
  }
  
  switch (Mf_EncoderButton(200)) {
    
    case click:
      
      switch (ptrLetter[AxisY][AxisX]) {
        
        case '!': //міняємо величину букв
          
          if (ptrLetter == letterBig) {
            ptrLetter = letterSmall;
          }
          
          else {ptrLetter = letterBig;}
          
          break;
        
        case '>'://вихыд
          
          break;
        
        case '<': //видаляємо букви
            
            if (inputIndicator ) {
             inputIndicator--;
             NameTest[inputIndicator] = ' ';
            
            }
          
          break;
        
        default://введення вибраної букви
          if (inputIndicator <= 19) {
            NameTest[inputIndicator] = ptrLetter [AxisY][AxisX];
            if (inputIndicator == 0) {
            ptrLetter = letterSmall;
            }
            inputIndicator++;
          }        
      
      }   
      break;
    
    case hol_const://якщо кнопка зажата курсор переміщається по вертикалі
      
      if (!axisChange) {
        axisChange = !axisChange;
      }
    
     break;
    
    case hol_released://повертаємо звичайний режим роботи
      
      if (axisChange) {
        axisChange = !axisChange;
      }
      
      if (AxisY == 4 ){//переміщаємо курсор для коректної роботи якщо останній масив
       
       if (AxisX <= 7){
        AxisX = 0;
       }
       
       else{AxisX = 9;}
      
      }
     break;
  
  }
   
  switch (Mf_encoderActionStorage(get_status)) {
    
    case 0: 
     
     break;
    
    case rotated_rights: 
      
      if (!axisChange) {AxisX--;}//\переміщення в залежності від режиму
      else {AxisY--;}  
      
      if (AxisY == 4 && AxisX == 8) {//корекція курсора якщо попали на останній масив костильі)
        AxisX = 0;
      }
      if (AxisX < 0) {
       AxisX = 9;
       AxisY--;
      }
      if (AxisY < 0) {AxisY = 4;}
     
     break;
    
    case rotated_left: 
      
      if (!axisChange) {AxisX++;}
      else {AxisY++;}  
      
      if (AxisY == 4 && AxisX == 1) {
       AxisX = 9;
      }
      if (AxisX > 9) {
       AxisX = 0;
       AxisY++;
      } 
      if (AxisY > 4 ) {AxisY = 0;} 
     
     break;   
  }
  
  NamePrint(ptrLetter, inputIndicator, AxisX, AxisY);

}
  
byte NamePrint(char letter[][10], byte inputIndicator, byte AxisX, byte AxisY){
   
  static char tracking;//використовується для відслідковування позиції рамки
  char str[] = {'!', '<', ' ', '>'};//маси використовується для мигання необхідної рамки
  static byte flagBlink[5];
  static uint32_t tmr, tmrInd;
   
  display.setCursor(6, 44);///набір різних стрілок
   
  if(letter[1][1] == 'W'){//якщо в масиві є великі букви значить стрілочка вниз
    display.write(25);//стрілочка вниз малі букви
  }
  else{display.write(24);}//стрілочка вверх великі букви
  
  display.setCursor(114, 54);//back
  display.print('s'); 
  display.drawPixel(120, 60, WHITE); //крапка
  
  if(!flagBlink[0]){display.drawCircle(8, 47, 5, WHITE);}//кружок стрілка вниз вверх
  
  if(!flagBlink[1]){// видалення
    display.setCursor(114, 43);
    display.print('x'); 
    display.drawCircle(116, 47, 4, WHITE);
  }
  
  display.fillRect(36, 54, 54, 6, WHITE);//пробіл
  if(letter[AxisY][AxisX] == ' '){display.drawRect(34, 52, 58, 10, WHITE);}//додаткова рамка якщо навели на пробіл
  
  if(!flagBlink[3]){display.drawRoundRect(112, 54, 9, 9, 0, WHITE);}//рамка s.
  
    for (int i = 0; i < 4; i++) {
      
      if(tracking != letter[AxisY][AxisX]){//можливі розрив захист від зникнення рамки
         flagBlink[0] = flagBlink[1] = flagBlink[2] = flagBlink[3] = tmr = 0;//якщо було переміщення обнуляємо всі переміни 
         tracking = letter[AxisY][AxisX];//перезаписуємо перемінно новими даними
      }

      if(letter[AxisY][AxisX] == str[i]){

       if(tmr < millis()){
         tmr = millis()+400;
         flagBlink[i] = !flagBlink[i];
       }
       break;
      }
      
      else if(i == 3 ){//
        display.drawRect(4+AxisX*12, 12+AxisY*10, 9, 11, WHITE);//рамка курса 10 вверх вниз, вліво вправо 12 
        break;
      }
    }
    
    if(inputIndicator != 20){//вертикальна риска біля букви мигання 
      if(tmrInd < millis()){
         tmrInd = millis()+400;
         flagBlink[4] = !flagBlink[4];
      }        
    }
    
    if(flagBlink[4] || inputIndicator == 20){//перестає мигати якщо 20 символів
      display.drawRect(2+6*inputIndicator, 0, 1, 9, WHITE);
    }
    
    display.setCursor(2, 1);//виводив ім'я профіля
    display.print(NameTest);
    
    
    for (int i = 0; i < 4; i++) {//виводим масив букв на дисплей
      for (int j = 0; j < 10; j++) {
        display.setCursor(6+j*12, 14+i*10);
        if(letter[i][j] != '!' && letter[i][j] != '<'){//ці знаки не можна виводити використовується для виконання дій
         display.print(letter[i][j]);
        }
      }
    }
    
   return 0;
  }
  
  void Ae_InterruptReadPinDtEncoder() {  //дивитися на початку в void setup працюэ разом з attachInterrupt(digitalPinToInterrupt(p_dt), Ae_InterruptReadPinDtEncoder, CHANGE);

   static int8_t clkStateCurrent;
   static int8_t clkStateLast;

   clkStateCurrent = digitalRead(p_clk);

    if ((clkStateLast == LOW) && (clkStateCurrent == HIGH)) {

      if (digitalRead(p_dt) == HIGH) {      
        Mf_encoderActionStorage(rotated_rights);
                  
      } else {
         Mf_encoderActionStorage(rotated_left);
                  
      }
    }

    clkStateLast = clkStateCurrent;       
  }
  
  int8_t Mf_encoderActionStorage(int8_t acceptedCommand) {

   static int8_t operationRetention;//збереження спрацювання
  
    if (acceptedCommand == rotated_left || acceptedCommand == rotated_rights) {
    operationRetention = acceptedCommand;
    }
    else if (acceptedCommand == get_status ) {
      acceptedCommand = operationRetention;
      operationRetention = 0;
      return acceptedCommand;
    }
    return 0;
  }
   

int Mf_EncoderButton(int retentionAfter) {
  
  static byte buttonState = released_const;
  static uint32_t tmr;
  
  if (Ae_BatR(b_sw)){
    
    if (buttonState == released_const) {
      buttonState = press_const; 
      tmr = millis() + retentionAfter; 
      return press;
    }
    else if (tmr && tmr < millis()) {
      buttonState = hol_const; 
      tmr = 0; 
      return hol_but;
    }
  }
  
  else if (!Ae_BatR(b_sw)){
    if (buttonState == hol_const) {buttonState = released_const; return hol_released;}
    else if (buttonState == press_const) {buttonState = released_const; return click;}
  }
    
 return buttonState;
  
}
  
byte Ae_BatR(byte Taking) {
 return !digitalRead(Taking); 
}