#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>
#include <FS.h>

#include <EEPROM.h>

#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library

#include "GyverButton.h"
#define LcD_POWER_PIN 10
#define BTN_PIN 4
GButton butt1(BTN_PIN);
#define BTN_2 2
GButton butt2(BTN_2);

ADC_MODE(ADC_VCC);

#define RF_PIN 5


static inline int32_t asm_ccount(void)
{
  int32_t r;
  asm volatile("rsr %0, ccount"
               : "=r"(r));
  return r;
}

static inline void asm_nop()
{
  asm volatile("nop");
}

// Variables used for temporary storage
static uint8_t data[64];
static uint8_t value[10];

uint32_t uid;
uint32_t vendor;
String name = "Loading";
String nameOld = "Loading";

int dash = 1;

void prepare_data(uint32_t ID, uint32_t VENDOR)
{
  value[0] = (VENDOR >> 4) & 0XF;
  value[1] = VENDOR & 0XF;
  for (int i = 1; i < 8; i++)
  {
    value[i + 2] = (ID >> (28 - i * 4)) & 0xF;
  }

  for (int i = 0; i < 9; i++)
    data[i] = 1; // header
  for (int i = 0; i < 10; i++)
  { // data
    for (int j = 0; j < 4; j++)
    {
      data[9 + i * 5 + j] = value[i] >> (3 - j) & 1;
    }
    data[9 + i * 5 + 4] = (data[9 + i * 5 + 0] + data[9 + i * 5 + 1] + data[9 + i * 5 + 2] + data[9 + i * 5 + 3]) % 2;
  }
  for (int i = 0; i < 4; i++)
  { // checksum
    int checksum = 0;
    for (int j = 0; j < 10; j++)
    {
      checksum += data[9 + i + j * 5];
    }
    data[i + 59] = checksum % 2;
  }
  data[63] = 0; // footer

  /*
  delay(10);
  Serial.println();
  for (int i=0; i<64; i++) {
    Serial.printf("%d", data[i]);
    if (i>=8 && (i+2)%5==0) Serial.printf("\r\n    ");
  }
  Serial.println();
  delay(10);
  */
}

void sendkey()
{
  int i = 0, j = 0;
  for (i = 0; i < 15; i++)
  {
    for (j = 0; j < 64; j++)
    {
      data[j] ? (GPE |= (1 << RF_PIN)) : (GPE &= ~(1 << RF_PIN));
      delayMicroseconds(255);
      for (int k = 0; k < 14; k++)
        asm_nop(); // fine tuning
      data[j] ? (GPE &= ~(1 << RF_PIN)) : (GPE |= (1 << RF_PIN));
      delayMicroseconds(255);
      for (int k = 0; k < 13; k++)
        asm_nop(); // fine tuning
    }
  }
}
void ICACHE_RAM_ATTR isr()
{
  butt1.tick(); // опрашиваем в прерывании, чтобы поймать нажатие в любом случае
  butt2.tick(); // опрашиваем в прерывании, чтобы поймать нажатие в любом случае
}

void setup()
{
  Serial.begin(9600);
  pinMode(4, INPUT_PULLUP);

  attachInterrupt(4, isr, CHANGE);



  // HIGH_PULL - кнопка подключена к GND, пин подтянут к VCC (PIN --- КНОПКА --- GND)
  // LOW_PULL  - кнопка подключена к VCC, пин подтянут к GND
  butt1.setType(HIGH_PULL);
  butt2.setType(HIGH_PULL);

  // NORM_OPEN - нормально-разомкнутая кнопка
  // NORM_CLOSE - нормально-замкнутая кнопка
  butt1.setDirection(NORM_OPEN);
  butt2.setDirection(NORM_OPEN);

  tft.init();
  tft.setRotation(2);
  tft.fillScreen(TFT_BLACK);

  pinMode(RF_PIN, INPUT);
}

void loop()
{
  butt1.tick(); // опрашиваем в скетче, иначе не будут работать проверки по времени!
  butt2.tick(); // опрашиваем в скетче, иначе не будут работать проверки по времени!
  if (butt1.isClick())
  {
    Serial.println("Click");
    dash++;
    if (dash > 8)
    {
      dash = 1;
    }
  }
  if (butt2.isClick())
  {
    Serial.println("Click");
    dash--;
    if (dash < 1 )
    {
      dash = 8;
    }
  }

  switch (dash)
  {
  case 1:
    uid = 1964417;
    vendor = 0;
    name = "Petr";
    break;
  case 2:
    uid = 10800069;
    vendor = 13056;
    name = "Artem";
    break;
  case 3:
    uid = 3473520;
    vendor = 2048;
    name = "Dezhurnii";
    break;
  case 4:
    uid = 12397139;
    vendor = 22106;
    name = "Cyfral"; //салатовый
    break;
  case 5:
    uid = 16685992;
    vendor = 100;
    name = "Yellow"; //Желтый
    break;
  case 6:
    uid = 00000000;
    vendor = 0;
    name = "Metacom RED"; //Красный
    break;
  case 7:
    uid = 8381467;
    vendor = 22106;
    name = "Vizit NEW"; //Фиолетовый
    break;
  case 8:
    uid = 2995799;
    vendor = 4352;
    name = "Eltis"; //Черный
    break;
  }

  tft.setTextSize(3);
  if (nameOld != name)
  {
    tft.setTextColor(TFT_BLACK, TFT_BLACK);
    tft.drawString(nameOld, 30, 120, 2);
  }

  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.drawString(name, 30, 120, 2);

  if (butt1.isStep()) {                                 // если кнопка была удержана (это для инкремента)
      prepare_data(uid, vendor);
      sendkey();                                           // увеличивать/уменьшать переменную value с шагом и интервалом
      Serial.println('Send');                              // для примера выведем в порт
  }
  if (butt2.isStep()) {                                 // если кнопка была удержана (это для инкремента)
      
      digitalWrite(LcD_POWER_PIN, LOW);                                          // увеличивать/уменьшать переменную value с шагом и интервалом
      Serial.println("OFF");                              // для примера выведем в порт
      ESP.deepSleep(0); 
  }





  butt1.tick(); // обязательная функция отработки. Должна постоянно опрашиваться
  nameOld = name;
}