#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#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);

//Profiles 1  
  
  #define t_sp 1
  #define t_mp 2
  #define r_off 0 // = off
  #define r_on 12 // = on
  
  class Profiles {
    public:
      Profiles(int deZoH, byte senHor, int deZoV, byte senVer, byte st, String name, byte tape, byte na) {
      
        _deadZoneHor = deZoH;
        _sensitivityHor = senHor;
        _deadZoneVer = deZoV;
        _sensitivityVer = senVer;
        _status = st;
        _name = name; 
        _tape = tape;
        _namber = na;
      }
      int getDeZoHor() {   //get
       return _deadZoneHor;
      } 
      byte getSenHor() {
       return _sensitivityHor;
      }
      int getDeZoVer() {
       return _deadZoneVer;
      }
      byte getSenVer() {
       return _sensitivityVer;
      }
      byte getStatus() {
       return _status;
      }
      String getName() {
       return _name;
      } 
      byte getTape() {
       return _tape;
      }
      byte getNamber() {
       return _namber;
      }
      
      int setDeZoHor(int deZoH) {//set
       if (_deadZoneHor != deZoH) {_sensitivityHor = 0;}
       _deadZoneHor = deZoH; 
      } 
      byte setSenHor(byte senHor) {
       _sensitivityHor = senHor;
      }
      int setDeZoVer(int deZoV) {
        if (_deadZoneVer != deZoV) {_sensitivityVer = 0;}
        _deadZoneVer = deZoV;
      }
      byte setSenVer(byte senVer) {
       _sensitivityVer = senVer;
      }
      byte setStatus(byte st) {
       _status = st;
      }
      byte setTape(byte tape) {
       _tape = tape;
      }
      byte setNamber(byte na) {
        _namber = na;
      }
      
      String getNameActiveFlashing(){
      
       static uint32_t tmr;
       static byte Flag;
      
       if (_status == r_on && tmr < millis()) {
         tmr = millis()+400;
         Flag = !Flag;
       }
       
       else if (_status == r_off) {
         if (!Flag) {
          tmr = Flag = !Flag;
         }
         return _name;
       }
       
       if (Flag) {return _name;}
      
       return "";
      }
      
      byte getgyroDataSum(){
       return _deadZoneHor + _sensitivityHor + _deadZoneVer + _sensitivityVer;
      }
      byte getNamberTape(){
       return _tape * 10 +_namber;//логіка роботи тип може бути 1 або 2 множимо на 10 щоб не збігалася числа
      }
      void copyFrom(const Profiles& other) {//використовується для копіювання шаблона
        _deadZoneHor = other._deadZoneHor;
        _sensitivityHor = other._sensitivityHor;
        _deadZoneVer = other._deadZoneVer;
        _sensitivityVer = other._sensitivityVer;
        _tape = other._tape;
        _namber = other._namber;
        _status = other._status;
        _name = other._name;
      }
     
      bool operator==(const Profiles& other) const {//використовується для порівняння шаблону
        return _deadZoneHor == other._deadZoneHor;
               _sensitivityHor == other._sensitivityHor;
               _deadZoneVer == other._deadZoneVer;
               _sensitivityVer == other._sensitivityVer;
               _tape == other._tape;
               _namber == other._namber;
               _status == other._status;
               _name == other._name;
      }
    
    
    
    private:
      int _deadZoneHor;//(primary початкова) початок руху
      byte _sensitivityHor;
      int _deadZoneVer;
      byte _sensitivityVer;
      byte _tape;
      byte _namber;
      byte _status;
      String _name;

  };
  
  
  Profiles profilSpOne(2000, 100, 2556, 92, r_off, "CodMW", t_sp, 1);
  Profiles profilSpTwo(166, 120, 1965, 112, r_off, "Batl3", t_sp, 2);
  Profiles profilMpOne(0, 0, 0, 0, r_on, "CSS", t_mp, 1);
  Profiles profilMpTwo(1584, 86, 1055, 79, r_off, "MaxP2", t_mp, 2);
  Profiles profilSeve(0, 0, 0, 0, 0, "0", 0, 0);
  
  
  
  Profiles *acPof(){
  
    Profiles *L[] = {&profilSeve, &profilSpOne, &profilSpTwo, &profilMpOne, &profilMpTwo};//L = перелік
    
    for (int i = 1; i <= 4; i++){
      if (L[i] -> getStatus() == r_on) {
      return L[i];
      }
    }
   return 0;
  }
  

  
   Profiles *sendSp(byte namber){ 
    
    Profiles *Sp[] = {&profilSeve, &profilSpOne, &profilSpTwo};
    
    return Sp[namber];
    
   return 0;
  }
  
 Profiles *sendMp(byte namber){ //функція приймає номер профіля і перенаправляє до нього

    Profiles *Mp[] = {&profilSeve, &profilMpOne, &profilMpTwo};
    
    return Mp[namber];
    
   return 0;
  }

//P

//Additional_elements 2
  
  bool Vibrgyro;
  // СПІЛЬНІ КОНСТАНТИ ПІД ХОД ДЛЯ БАГ ФУНК
  #define no_action 0 // НЕМА ДІЇ
  #define no_data 0 // немає даних
  #define no_indication 0   // немає вказівки
  #define no 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 info_display 20
    #define menu 21 
    #define confirm_selection 22 

    #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
    byte noData[1];
    byte posOne[] = {1};
    byte posTwo[] = {2};
    byte posThree[] = {3};
    byte posFour[] = {4};
    byte posFive[] = {5};
    byte posSix[] = {6};
    byte posEight[] = {7};
   
    //Ae_InterruptReadPinDtEncoder()
    //Mf_Encoder();
     #define rotated_rights 20
     #define rotated_left 21
    
    
    //Mf_consenDisplay()
     #define choice_yes 20
     #define choice_no 21
    
    
    //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
 
 
  int8_t Ae_Countdown(int8_t acceptedCommand){

   static uint32_t tmr;
   
   if (!tmr) {tmr = (acceptedCommand+1) * 1000 + millis();}
   
   else if (acceptedCommand == reset) { tmr = 0;}
   acceptedCommand = ((tmr - millis()) / 1200) - 1;
   //Serial.println(acceptedCommand);
   
   return acceptedCommand;
  }
 
  byte Ae_BatR(byte Taking) {
   return !digitalRead(Taking);
  }
 
  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;       

  }

  
//Ae

//Main_display 3
  
  // add1-----------------------------------------------------------------------------------------------------------
    
    byte Md_MainDisplay() {// головна фунція виклик решта функцій дисплея
      
     display.clearDisplay();

      switch (Md_addres(1, int_add)) {
        case no_action: 
        Mf_BluetoothPrint(); Mf_typeNumberPrint(); Mf_batteryPrint();

        Md_ItemOne();break;//1
        case 1: Md_Profile();break;//a2
        //case 2: Md_Additional_effectsENG(); break;
        //case 3: Md_SettingsENG(); break;
        //case 4: Md_InstructionENG(); break;
        case 5:
          Md_cursor(no_data, reset);
          Md_addres(1, back);
          Md_Display(info_display);
          
        break;

      }
    
     display.display();
    }
    
    
    void Md_ItemOne(){
      
        byte Lines[] = {16, 24, 32, 40, 48 };
        Md_cursor(Lines, sizeof(Lines));
        
        display.setCursor(0, 16);
        display.println(" Profile");
        display.println(" Additional effects");
        display.println(" Settings ");
        display.println(" Instruction ");
        display.println(" Close menu ");
    
    }
  //
  // add2-----------------------------------------------------------------------------------------------------------
    
    void Md_Profile() {
      
      if (acPof()->getTape() == t_sp) {
      
      switch (Md_addres(2, int_add)) {
        case no_action: Md_Pp_Profile();break;
        case 1: Md_ProfileSettings();break;//a3
        case 2: break;// QR code
        case 3: Md_SelectProfile();break;//a3 
        case 4: 
        Md_cursor(posOne, return_pos);
        Md_addres(1, back);
        break;
        
      }
    }   
    else {
    
    switch (Md_addres(2, int_add)) {
        case no_action: Md_Pp_Profile();break;
        case 1: Md_ProfileSettings();break;//a3
        case 2: Md_SelectProfile();break;//a3 
        case 3: 
        Md_cursor(posOne, return_pos);
        Md_addres(1, back); 
        break;
        
      }
    }   
   } 
    void Md_Pp_Profile() {
        
         byte LinesSp[] = {31, 39, 49, 57};
         byte LinesMp[] = {35, 49, 57};
        
        if (acPof()->getTape() == t_sp) {      
         Md_cursor(LinesSp, sizeof( LinesSp));
        } 
        else {
         Md_cursor(LinesMp, sizeof(LinesMp));
        }
        
      display.setCursor(3, 1);//стрілки по боках активний профіль
      display.write(25);
      display.setCursor(118, 1);
      display.write(25);
      
      display.drawRoundRect(0, 0, 127, 10, 2, WHITE);// РАМКА АКТИВ ПРОФ НАЗВА ГРИ
      display.drawRect(0, 9, 127, 19, WHITE);
      display.drawRect(10, 29, 51, 19, WHITE);// РАМКА НАСТРОЙКИ КЮАР КОД
      
      display.setCursor(21, 1);
      display.print("Active profile");
      
      display.setCursor(98, 36);       //РИСКА В лыво РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
      display.println("_" );
      display.setCursor(93, 36);
      display.println("_" );
      display.setCursor(91, 36);// СТРЫЛКА ВВЕРХ РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
      display.write(24);
      display.setCursor(91, 34);
      display.write(30);
      
      display.drawCircle(113, 42, 11, WHITE);// КРУГ НАВК Sp
      
      display.setCursor(105, 39);
      
      if (acPof()->getTape() == t_sp) { 
       
       display.print("Sp");
       display.println(acPof()->getNamber());
      }
      else {
       
       display.print("Mp");
       display.println(acPof()->getNamber());
      }
      
      display.setCursor(2, 11);
      display.println(acPof()->getName());
      
      display.setCursor(0, 32);       //РИСКА В ПРАВО РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
      display.println("          _" );
      display.setCursor(4, 32);
      display.println("          _" );
      
      display.setCursor(66, 32);// СТРЫЛКА ВВЕРХ РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
      display.write(24);
      display.setCursor(66, 30);
      display.write(30);
      
      
      
      if (acPof()->getTape() == t_sp) {      
        display.setCursor(0, 31);
        display.println("  Settings   " );
        display.println("  QR code");
        } 
        else { 
        display.setCursor(0, 35);
        display.println("  Settings   " );
        }
      
      display.setCursor(0, 49);
      display.println(" Select profile ");
      display.println(" Back" );
      
    }
   

  //        
  // add3-----------------------------------------------------------------------------------------------------------

    void Md_SelectProfile() {
    
      switch (Md_addres(3, int_add)) {
        case no_action: Md_Pp_SelectProfile();break;
        case 1: Md_StandardProfiles();break;
        case 2: Md_MyProfiles();break;
        case 3: 
         Md_cursor(posTwo, return_pos);
         Md_addres(2, back); 
         break;
      }
    }
    
    void Md_Pp_SelectProfile() {
      
      byte Lines[] = {16, 24, 32};
      Md_cursor(Lines, sizeof(Lines));
    
      display.setCursor(0, 16);
      display.println(" Standard profiles" );
      display.println(" My profiles");
      display.println(" Back" );
    
    }
  
    void Md_ProfileSettings() {// a121  в меню дисплея підписано Settings
      
      if (acPof()->getTape() == t_sp) {  
        
        switch (Md_addres(3, int_last)) {
          case no_action: Md_Pp_ProfileSettings();break;
          case 1:break;//Buttons
          case 2: 
          Md_cursor(posOne, return_pos);
          Md_addres(2, back); 
          break; 
        }
      }
        
      else {
        switch (Md_addres(3, int_add)) {
          case no_action: Md_Pp_ProfileSettings();break;//a2
          case 1:Md_gyroscopeSettings();break;//Gyroscope //a4
          case 2:break;//Buttons
          case 3:break;//Name
          case 4:break;//Reset
          case 5: 
          Md_cursor(posOne, return_pos);
          Md_addres(2, back); 
          break;
          case address_entered:
             Mf_changeInspector(copy_profile);//зберігаємо дані профіля для перевірки при виході для того щоб був вибір зберігати настройки чи ні
          break;  
        }
      }
    }
       
    void  Md_Pp_ProfileSettings() {
      
      byte LinesSp[] = {16, 24};
      byte LinesMp[] = {8, 16, 24, 38, 50};
      
      if (acPof()->getTape() == t_sp) {      
        Md_cursor(LinesSp, sizeof(LinesSp));
        display.setCursor(0, 16);
        display.println(" Buttons" );//не добавляти пункт reset не потрібно
        display.println(" Back" );
      
      } 
      else {
        Md_cursor(LinesMp, sizeof(LinesMp));

        display.drawRoundRect(9, 6, 59, 28, 0, WHITE);
        display.setCursor(0, 8);
        display.println("  Gyroscope" );
        display.println("  Buttons" );
        display.println("  Name" );
        
        if(Md_cursor(noData, get_pos) == 4){
          display.setCursor(38, 35);// КРИВА СТРЫЛКА ВВЕРХ СТРЫЛКА
          display.println("_" );
          display.setCursor(40, 35);
          display.write(24);
        }
        
        display.setCursor(0, 38);
        display.println(" Reset" );
        
        display.setCursor(0, 50);
        display.println(" Back" );
      }

    }
  //                
  // add4-----------------------------------------------------------------------------------------------------------
    void Md_StandardProfiles() {
    
      switch (Md_addres(4, int_add)) {
        case no_action: Md_Pp_StandardProfiles();break;
        case 8: 
         Md_cursor(posOne, return_pos);
         Md_addres(3, back);
         break;
        
      }
    }
    
    void  Md_Pp_StandardProfiles() {
      
      byte Lines[] = {0, 8, 16, 24, 32, 40, 48, 56};
      Md_cursor(Lines, sizeof(Lines));
    
      display.setCursor(6, 0);// 6 місце для стрілки
      display.println(profilSpOne.getNameActiveFlashing());
      display.println(" Standard profile 2" );
      display.println(" Standard profile 3" );
      display.println(" Standard profile 4" );
      display.println(" Standard profile 5" );
      display.println(" Standard profile 6" );
      display.println(" Standard profile 7" );
      display.println(" Back" );
    
    }
    

    void Md_MyProfiles(){
     
      switch (Md_addres(4, int_add)) {
        case no_action: Md_Pp_MyProfiles();break;
        case 8: 
         Md_cursor(posTwo, return_pos);
         Md_addres(3, back); 
        break;
        case address_entered:
          if (Mf_changeInspector(profilMp) == data_match){//якщо вибрали діючий профіль видаляємо вибраний адрес
           Md_addres(4, back);
          }
          break;
        
      }
    }
    
    void  Md_Pp_MyProfiles() {
      
      byte Lines[] = {0, 8, 16, 24, 32, 40, 48, 56};
      Md_cursor(Lines, sizeof(Lines));
    
      display.setCursor(6, 0);
      display.println(profilMpOne.getNameActiveFlashing());
      display.println(" My profile 2" );
      display.println(" My profile 3" );
      display.println(" My profile 4" );
      display.println(" My profile 5" );
      display.println(" My profile 6" );
      display.println(" My profile 7" );
      display.println(" Back" );
    
    }
   
    void Md_gyroscopeSettings(){//a3
      
      int seveData;//використовую щоб було більш зрозуміліше

      if (Md_addres(4, get_add) != 5){Md_Pp_gyroscopeSettings();}
      
      switch (Md_addres(4, int_last)) {//4
        case no_action:break;
        case 1:
          seveData = Mf_Magnifier(acPof()->getDeZoHor(), 0, 9999, 100);
          acPof()->setDeZoHor(seveData);
         break;
        case 2:
          seveData = Mf_Magnifier(acPof()->getSenHor(), 0, 100, 100);
          acPof()->setSenHor(seveData);
         break;
        case 3:break;
        case 4:break;
        case 5:
          
            Md_addres(3, back);
          
         break;
        
      }
    }
  
    void  Md_Pp_gyroscopeSettings() {
      
      static uint32_t tmr;
      static byte dashFlag;//прапорець м'ягання
      
      if (tmr < millis()) {
        tmr = millis() + 500;
        dashFlag = ! dashFlag;
      }
     
      byte Lines[] = {5, 15, 34, 44, 55};
      Md_cursor(Lines, sizeof(Lines));
    
      //H
      if((Md_cursor(noData, get_pos) == 1 || Md_cursor(noData, get_pos) == 2) && !Md_addres(4, get_add)){// РАМКА  НВКОЛО H
       if(dashFlag){display.drawRoundRect(79, 6, 15, 15, 0, WHITE);}
      }
      else{display.drawRoundRect(79, 6, 15, 15, 0, WHITE);}
      
      display.setCursor(84, 10);
      display.println("H");
    
      
      display.drawRoundRect(6, 2, 69, 23, 3, WHITE); //РАМКА НАВКОЛО Dead zone Sensitivity
      display.setCursor(8, 5);
      display.println("Dead zone ");
      
      if(Md_addres(4, get_add) == 1){ // число рамка
       if(dashFlag){display.drawRoundRect(99, 2, 29, 11, 4, WHITE);}
      }
      else{display.drawRoundRect(99, 2, 29, 11, 4, WHITE);}
      
      
      display.setCursor(102, 4);
      display.println(acPof()->getDeZoHor());
      
      
      display.setCursor(8, 15);
      display.println("Sensitivity");
      
      if(Md_addres(4, get_add) == 2){ // число рамка
       if(dashFlag){display.drawRect(101, 15, 23, 11, WHITE);}
      }
      else{display.drawRect(101, 15, 23, 11, WHITE);}
      
      
      display.setCursor(104, 17);
      display.println(acPof()->getSenHor());
      
      //V
      if((Md_cursor(noData, get_pos) == 3 || Md_cursor(noData, get_pos) == 4) && !Md_addres(4, get_add)){// РАМКА  НВКОЛО V
       if(dashFlag){display.drawCircle(86, 42, 7, WHITE);}
      }
      else{display.drawCircle(86, 42, 7, WHITE);}   
      
      display.setCursor(84, 39);
      display.println("V");
      
      display.drawRoundRect(6, 31, 69, 23, 0, WHITE);//РАМКА НАВКОЛО Dead zone Sensitivity
      display.setCursor(8, 34);
      display.println("Dead zone ");
      
      if(Md_addres(4, get_add) == 3){ // число рамка
       if(dashFlag){display.drawRect(99, 31, 29, 11, WHITE);}
      }
      else{display.drawRect(99, 31, 29, 11, WHITE);}
      
      display.setCursor(104, 46);
      display.println(acPof()->getDeZoVer());
      
      
      display.setCursor(8, 44);
      display.println("Sensitivity");
      
      if(Md_addres(4, get_add) == 4){ // число рамка
       if(dashFlag){display.drawRoundRect(101, 44, 23, 11, 4, WHITE);}
      }
      else{display.drawRoundRect(101, 44, 23, 11, 4, WHITE);}
      
      display.setCursor(102, 33);
      display.println(acPof()->getSenVer());
      
      
      display.setCursor(8, 55);
      display.println("Back");
    }
  //
  // Additional functions-------------------------------------------------------------------------------------------
    
     //consent display погодження дисплей
    byte Mf_consenDisplay(){ //викликається для підтвердження вибору//writtenSentence написане речення 
      
      static int8_t flagChoice;
      
      switch (Mf_encoderActionStorage(get_status)) {//керування рамкою вліво вправо
        case no_action: 
          break;
        case rotated_rights: 
          if (flagChoice > -2) {flagChoice--;}
          break;
        case rotated_left: 
          if (flagChoice < 2) {flagChoice++;} 
          break;
      }
      
      if (Mf_EncoderButton(500) == click) {// V1 якщо відбувся Клік виконується дії в залежності від вибору
        
        if (flagChoice == -2) {//якщо вибрали ні виконуються дії//вихід виконується в любому випадку з збереженими настройками або ні
          
        }
        if (flagChoice == -2) {//функція закриється без виконання дій
          
          return 0;
        }
        else if (flagChoice == 2){//в кінці повернуть правду буде інверсія
         flagChoice = 0;
        }
      }
            
      else{Mf_EncoderButton(500);} //запобіжник від розриву виконання функції
      
      Mf_consenDisplayPrint(flagChoice);//візуальна частина коду
    
     return 0;
    
    }
      
    int8_t Mf_consenDisplayPrint(int8_t acceptedFlag){
      
      static uint32_t tmr;
      static int8_t  FlagFrame;
      
      if (tmr < millis()) {
         tmr = millis() + 400;
         FlagFrame = !FlagFrame;
      }
      
      display.drawRoundRect(20, 8, 87, 22, 0, WHITE); //рамка навколо слова зверху
      
      display.setCursor(4, 15); 
      display.println("       Confirm        ");
           
      
      if (FlagFrame) {
        display.drawRect(52+acceptedFlag*18, 39, 21, 13, WHITE);
      }
      for (int i=39; i <= 75; i+=18){
       display.setCursor(i,42);
       display.write(27);display.write(26); 
      }
      
      display.setCursor(21, 42);
      display.println("NO");
      display.setCursor(18, 42);
      display.println("            YES");
      
      return 0;
    }
    
    byte Mf_changeInspector(byte acceptedCommand) {//функція викликається якщо Md_addres(3, get_add) == 1
     
      switch (acceptedCommand) {//false якщо повертаємо збережені настройки
        case return_save:
           acPof()->copyFrom(profilSeve);//це означає що ми вибрали ні і повертаємо настройки
          break;
        case copy_profile:
           profilSeve.copyFrom(*acPof());//копіювання діючий профіль перед доходом в настройки
          break;
        case gyro:
           if(profilSeve.getgyroDataSum() != acPof()->getgyroDataSum()) {return doesnt_match;}// не збігається
          break;
        case profilSp:
          if (Md_addres(4, get_add) != 8 && (acPof()->getNamberTape() == sendSp(Md_addres(4, get_add))->getNamberTape())){return data_match;}//збіг даних
          break;
        case profilMp:
          if (Md_addres(4, get_add) != 8 && (acPof()->getNamberTape() == sendMp(Md_addres(4, get_add))->getNamberTape())){return data_match;}//збіг даних
          break;
      }
 
     return 0;
    }
    
    
    byte Md_reminderDisplay(byte acceptedCommand) {

      static uint32_t tmr;

      display.clearDisplay();

      display.drawRect(0, 14, 127, 30, WHITE);// РАМКА НАВКОЛО НАЗВИ

      if (acceptedCommand == reset) {
        tmr = 0;
      }
      else if (!tmr) {
        tmr = millis() + 2000;
      }
      else if (tmr < millis()) {
        tmr = 0;
        Md_Display(info_display);
      }

      display.setCursor(0, 16);

      display.println("  Read instruction");// НАГАДУВАННЯ
      display.println("   " );//27
      display.println(" Make game settings " );//27

      display.setCursor(60, 50);
      display.print("b");

      display.drawCircle(62, 53, 6, WHITE);//кружок

      display.display();
    }

    void Md_infoDisplay() {

      display.clearDisplay();//не забути доробити періодичне виведення

      Mf_BluetoothPrint(); Mf_typeNumberPrint(); Mf_batteryPrint();

      display.drawRect(0, 14, 127, 19, WHITE);// РАМКА НАВКОЛО НАЗВИ

      display.setCursor(2, 16);
      display.print(acPof()->getName());
      
      display.setCursor(43,44);
      display.write(26);
      
      display.drawRect(49, 42, 27, 11, WHITE);
      display.setCursor(51,44);
      display.print("Menu");
      
      display.display();
    }

    void Mf_BluetoothPrint() {

      static uint32_t tmr;
      static byte FlagPriS;
      static byte BluTest;

      display.setCursor(4, 3);
      //display.write(127 + 47);
      display.print("b");

      if (!BluTest) {
        display.drawCircle(6, 6, 6, WHITE);//кружок
      }

      if (BluTest) {
        display.drawCircle(6, 6, 6, WHITE);//кружок
      }

      if (FlagPriS != 3 && tmr < millis()) {
        FlagPriS++;
        tmr = millis() + 1000;
      }
    }

    void Mf_batteryPrint() {

      display.setCursor(17 * 6, 0);
      display.print("123");

    }

    void Mf_typeNumberPrint() {
      display.setCursor(46, 0);
      if (acPof()->getTape() == t_sp) { 
       
       display.print("(Sp");
       display.print(acPof()->getNamber());
       
      }
      else {
       
       display.print("(Mp");
       display.print(acPof()->getNamber());
      }
      display.print(")");
    }
  //Af
//Md

//Display 4
  byte Md_Display(byte Receiving) { 
     
    static byte Regime = info_display; 

      switch (Receiving) {
        
        case get_status: 
          
           return Regime;
          
          break;
        
        case menu: 

          Regime = menu;
          
          break;
        
        case info_display: 
          
          Regime = info_display;
        
          break;
        
      }
    
   return Regime;
  } 

//D

//Menu_functions 5 
 
  byte Md_cursor(byte* arr, byte acceptedCommand) { 
    
    static int8_t Arrow, Point = 1;
        
    switch (acceptedCommand) {
      case return_pos: 
          Point = arr[0];
          Arrow = 0;
        break;
      case reset: 
          Arrow = 0;
          Point = 1;
        break;
      case selection_inversion: 
          Arrow = !Arrow;
        break;
      case get_pos: 
         return Point; 
        break;
      default:
        switch (Mf_encoderActionStorage(get_status)) {
          case no_action: 
            break;
          case rotated_left: 
            if (!Arrow) {Point--;}
            if (Point == 0) {Point = acceptedCommand;}
            break;
          case rotated_rights: 
            if (!Arrow) {Point++;}
            if (acceptedCommand < Point) {Point = 1;}
            break;
        }
        if (!Arrow) {
          display.setCursor(0, arr[Point-1]);  //21,8 максимум символів
          display.write(26);
          }
        else if (Arrow) {
          display.setCursor(0, arr[Point-1]);  //21,8 максимум символів
          display.write(16);
        }   
      } 
   return 0;
  } 
    
  byte Md_addres(int8_t AddNumb, byte acceptedCommand) {

    static byte Adr[19], caseBack, namb = 1;
    
    switch (acceptedCommand) {
      case back:
          for (int i=18; i >= 1; i--){
            Adr[i] = 0;
            if (i == AddNumb) {
            namb = AddNumb;
            break;
            }
          }
       break;
      case backpoint: 
        Md_cursor(no_data, selection_inversion);
        caseBack = 1;
       break;
      case get_add:
        return Adr[AddNumb];
       break;
    }

    if (acceptedCommand == int_add) { // acceptedCommand ==  int_add реагуємо на клік тільки коли немає команди
      if (!Adr[AddNumb] && Mf_EncoderButton(500) == click){
         Adr[namb] = Md_cursor(noData, get_pos);
         namb++;
         Md_cursor(noData, reset);
       
       return address_entered;
      }
    }
    else if (acceptedCommand == int_last) { // acceptedCommand ==  int_add реагуємо на клік тільки коли немає команди
      if (!Adr[AddNumb] && Mf_EncoderButton(500) == click){
       Adr[namb] = Md_cursor(noData, get_pos);
       Md_cursor(noData, selection_inversion);
       
       return address_entered;
      }
      else if (!caseBack && Adr[AddNumb] && Mf_EncoderButton(500) == click){//видалення адреса
       Adr[namb] = 0;
       Md_cursor(noData, selection_inversion);
      }  
    }
   return Adr[AddNumb];
  }
  
  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;
      
  }
  int Mf_Magnifier(int Numeric, int Min, int Max, int Speed) {
   
   static uint32_t tmr;
   static int counter = 1;
   
    switch (Mf_encoderActionStorage(get_status)) {
      case rotated_rights:
         tmr = millis()+500;
         Numeric += counter;
         if (Speed > counter) {
          counter++;
         }
        break;
      case rotated_left:
         tmr = millis()+500;
         Numeric += -counter;
         if (Speed > counter) {
          counter++;
         }
        break;
    }
    if (counter != 1 && tmr < millis()) {        
      tmr = millis()+counter/2;
      counter--;
    }
    
    if (Numeric > Max) {
      Numeric = Min;
    } else if (Numeric < Min) {
      Numeric = Max;
    }
   return Numeric;
  }

//Mf

//Gamepad_function 6
//Gf

void setup() {
  
  Serial.begin(9600);// В ФЫНАЛЫ НЕЗАБУТИ ЗАКОМЕНТУВАТИ!!!
   Wire.begin();
  Serial.println("//Profiles");
  Serial.println("//Additional_elements");
  Serial.println("//Main_display");
  Serial.println("// add1");
  Serial.println("// add2");
  Serial.println("// add3");
  Serial.println("// add4");
  Serial.println("// Additional functions");
  Serial.println("// Display");
  Serial.println("// Menu_functions");
  Serial.println("// Gamepad_function");
  //pin 34 35 обережно глюки здаєтся немож зроб INPUT_PULLUP, pin 2 дыод! esp 32

  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  
  pinMode(b_sw, INPUT_PULLUP);  //енкодер кнопка SW
  
  //Ae_InterruptReadPinDtEncoder()
  attachInterrupt(digitalPinToInterrupt(p_dt), Ae_InterruptReadPinDtEncoder, CHANGE);


  //----------Display--------------
  //display.cp437(true);  //rus текст
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setTextWrap(false);
  display.clearDisplay();
  display.display();

}

void loop() {
  
 
  switch (Md_Display(get_status)) {
    
    case info_display: 
        
        if (Mf_EncoderButton(500) == click) {
         Md_Display(menu);
        }
        
        Md_infoDisplay();
        
        /*
        if (bleGamepad.isConnected()) {
        Gf_ModifiedButtonInput();
        Gf_AnLeftAxisX();//H
        Gf_AnLeftAxisY();//V
        Gf_AnRightZ(get_status);//H G
        Gf_AnRightRZ(get_status);//V G
        bleGamepad.sendReport();
        }
      */
      
      break;
   
   case menu:
       
       Md_MainDisplay();
    
      break;
   
   case confirm_selection:
        
        if (Mf_consenDisplay() == choice_no) {
         Md_Display(info_display);
         acPof()->copyFrom(profilSeve);
        }
        
        else if (Mf_consenDisplay() == choice_yes) {
         Md_Display(info_display);
        }
      
      break; 
    
  }  
    
    
    
    
     
    
  
  

}  //void loop()