#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include <Bounce2.h>
#include <STM32RTC.h>

// Настройки SPI для ST7735
#define TFT_CS PB0   // Chip Select
#define TFT_RST PB9  // Reset
#define TFT_DC PB8   // Data/Command
#define TFT_SCLK PB3 // SPI Clock (SCK)
#define TFT_MOSI PB5 // SPI Data (MOSI)

// Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

#define BTN_1_PIN PA3
#define BTN_2_PIN PA4
#define BTN_3_PIN PA5

#define BUZZER_PIN PA2

Bounce2::Button btn1 = Bounce2::Button();
Bounce2::Button btn2 = Bounce2::Button();
Bounce2::Button btn3 = Bounce2::Button();

void buzzerOn(unsigned int freq, unsigned long duration);
void buzzerOff();
uint32_t mes();

#define DAC_1_CS PA6 // SIN1 // яркосить
#define DAC_2_CS PA7 // SIN2

#define RINT PB1 // RINT

uint32_t ANOUT = 0;
uint32_t ANCTRL = 0;

int x = 12 * 8;
int y = 16 * 1;

uint32_t SIGNAL_COUNTER = 0;
uint32_t SIGNAL_COUNTER_PREV = 0;

// uint32_t SIGNAL_COUNTER_TOTAL = 0;

#define SIGNAL_MAX 1500

// Получаем экземпляр RTC
STM32RTC &rtc = STM32RTC::getInstance();

bool ISRUN = false;
byte sec = 0;
byte sec_prev = 0;

uint32_t ANOUT_PREV = 0;
uint32_t ANCTRL_PREV = 0;

#define counter_max 500
uint16_t counter = counter_max;

byte STATE = 0;
#define CALIBRATE_MENU 1
int SPEED = 1;
int SPEEDCOUNTER;

// Функция для отправки данных в DAC
void writeDAC(int CS_PIN, uint16_t value)
{
  value = map(value, 0, 3300, 0, 1023);

  if (value > 1023)
    value = 1023; // Ограничение до 10 бит

  // Формируем 16-битное слово для отправки
  uint16_t data = 0x0000; // Команда и данные
  data |= (0x00 << 12);   // Команда: запись и обновление выхода
  data |= (value << 2);   // 10-битное значение, сдвинутое на 2 бита влево

  // Активируем чип
  digitalWrite(CS_PIN, LOW);

  // Отправляем данные через SPI
  SPI.transfer16(data);

  // Деактивируем чип
  digitalWrite(CS_PIN, HIGH);
}

// #define FONT_COLOR ST7735_WHITE
// #define BACK_COLOR ST7735_BLACK

inline void setBrightness(int mV)
{
  writeDAC(DAC_1_CS, mV); // яркосить
}

inline void secCompensation(int mV)
{
  writeDAC(DAC_2_CS, mV);
}

void pause()
{
  if (ISRUN)
  {
    tft.setTextColor(ST7735_BLACK);
    tft.setTextSize(2);
    tft.setCursor(1, 1);
    tft.println("ANOUT : ");
    // tft.setCursor(1, y);
    // tft.println("ANCTRL: ");
  }
  ISRUN = !ISRUN;
  if (ISRUN)
  {
    tft.setTextColor(ST7735_GREEN);
    tft.setCursor(1, y * 4);
    tft.println("START");

    tft.setTextColor(ST7735_BLACK);
    tft.setCursor(1, y * 4);
    tft.println("PAUSE");
  }
  else
  {
    tft.setTextColor(ST7735_GREEN);
    tft.setCursor(1, y * 4);
    tft.println("PAUSE");

    tft.setTextColor(ST7735_BLACK);
    tft.setCursor(1, y * 4);
    tft.println("START");
  }
}

inline void clearScreen(int color)
{
  tft.setCursor(0, 0);
  tft.fillScreen(color);
}

void setup()
{
  // Инициализация встроенного светодиода
  // pinMode(PC13, OUTPUT);

  delay(500);

  // DAC
  pinMode(DAC_1_CS, OUTPUT);
  digitalWrite(DAC_1_CS, HIGH);
  pinMode(DAC_2_CS, OUTPUT);
  digitalWrite(DAC_2_CS, HIGH);

  //
  pinMode(PA0, INPUT);
  pinMode(PA1, INPUT);

  // RINT
  pinMode(RINT, OUTPUT);
  digitalWrite(RINT, LOW);

  // buzzer
  pinMode(BUZZER_PIN, OUTPUT);

  // кнопки
  btn1.attach(BTN_1_PIN, INPUT);
  btn2.attach(BTN_2_PIN, INPUT);
  btn3.attach(BTN_3_PIN, INPUT);

  btn1.interval(5);
  btn2.interval(5);
  btn3.interval(5);

  btn1.setPressedState(LOW);
  btn2.setPressedState(LOW);
  btn3.setPressedState(LOW);

  pinMode(TFT_CS, OUTPUT);
  pinMode(TFT_RST, OUTPUT);
  pinMode(TFT_DC, OUTPUT);

  // 1. Ручной сброс дисплея
  pinMode(TFT_RST, OUTPUT);
  digitalWrite(TFT_RST, HIGH);
  delay(50);
  digitalWrite(TFT_RST, LOW);
  delay(150);

  // void setMISO(uint32_t miso)
  // void setMOSI(uint32_t mosi)
  // void setSCLK(uint32_t sclk)
  // void setSSEL(uint32_t ssel)
  pinMode(TFT_MOSI, OUTPUT);
  pinMode(TFT_SCLK, OUTPUT);

  SPI.setMISO(PB4);
  SPI.setMOSI(TFT_MOSI);
  SPI.setSCLK(TFT_SCLK);
  // SPI.setSSEL(PA15);
  // SPI.setDataMode(SPI_MODE0);
  // SPI.setBitOrder(MSBFIRST);
  //  SPI.setClockDivider(8);
  // SPI.begin();

  // SPIClass mySPI(PB5, PB4, PB3, PB15); //mosi, miso, sclk, ssel

  // ⚡ Уменьшаем скорость SPI до 1 МГц (по умолчанию обычно 18 МГц)
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); // работает
  // SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));

  // Инициализация дисплея
  tft.initR(INITR_MINI160x80_PLUGIN); // Для ST7735S
  tft.setRotation(3);

  // Инициализация RTC
  rtc.begin(); // По умолчанию использует LSE (внешний кварц 32.768 кГц)

  // Если кварца нет, можно использовать LSI (внутренний генератор, менее точный)
  rtc.setClockSource(STM32RTC::LSE_CLOCK);

  // Установка времени (РАСКОММЕНТИРУЙТЕ ПРИ ПЕРВОМ ЗАПУСКЕ!)
  rtc.setTime(12, 0, 0);   // Часы, минуты, секунды
  rtc.setDate(7, 6, 2024); // День, месяц, год (год 4-значный)

  // analogReference(AR_DEFAULT);
  analogReadResolution(12); // 12 бит (0-4095)

  Serial.begin(115200);

  // writeDAC(DAC_1_CS, 1500); // яркосить
  setBrightness(1500);
  writeDAC(DAC_2_CS, 2000);

  buzzerOn(4000, 100);
  delay(300);
  buzzerOn(4000, 100);
  delay(500);

  /************************************************************************************/

  tft.fillScreen(ST7735_BLACK);
  tft.setTextColor(ST7735_WHITE);
  tft.setTextSize(2);
  tft.setCursor(30, 30);
  tft.println("KPOBEMEP");

  tft.setCursor(3, 3);
  tft.setTextSize(1);
  tft.println("XAH38");
  delay(3000);

  /************************************************************************************/

  ISRUN = true;
  clearScreen(ST7735_GREEN);
  pause();
}

void calibrite()
{
  setBrightness(0);
  secCompensation(0);

  buzzerOn(4000, 500);
  delay(1000);

  setBrightness(2000); // 1500
  delay(100);

  int cnt[10];
  for (byte i = 0; i < 10; i++)
  {
    // mes();
    ANCTRL = map(analogRead(PA1), 0, 4095, 0, 3300);
    cnt[i] = ANCTRL;
    delay(10);
  }

  ANCTRL = 0;
  for (byte i = 0; i < 10; i++)
  {
    ANCTRL += cnt[i];
  }
  ANCTRL = ANCTRL / 10;
  secCompensation(ANCTRL);
}

void calibrite_menu()
{
  ISRUN = false;

  clearScreen(ST7735_BLACK);
  tft.setTextColor(ST7735_WHITE);
  tft.setTextSize(2);
  tft.setCursor(25, 20);
  tft.println("CALIBRITE");

  // установка нуля
  tft.setCursor(1, 40);
  tft.setTextSize(1);
  tft.println("press to set zero");

  delay(500);
  btn3.update();
  while (!btn3.pressed())
  {
    btn3.update();
  }

  calibrite();
  tft.setCursor(5, 50);
  tft.setTextColor(ST7735_WHITE);
  tft.println((String)ANCTRL);

  delay(3000);

  // установка скорости
  clearScreen(ST7735_BLACK);
  tft.setTextColor(ST7735_WHITE);
  tft.setTextSize(2);
  tft.setCursor(25, 20);
  tft.println("CALIBRITE");
  tft.setCursor(1, 40);
  tft.setTextSize(1);
  tft.println("press to set speed");

  tft.setCursor(5, 50);
  tft.setTextColor(ST7735_WHITE);
  tft.println((String)SPEED);

  delay(500);
  btn3.update();
  // while (!btn3.pressed())
  int speed_prev = SPEED;
  for (;;)
  {
    btn3.update();
    if (btn3.pressed())
    {
      buzzerOn(6000, 250);
      switch (SPEED)
      {
      case 1:
        SPEED = 5;
        break;
      case 5:
        SPEED = 10;
        break;
      case 10:
        SPEED = 20;
        break;
      case 20:
        SPEED = 30;
        break;

      default:
        SPEED = 1;
        break;
      }
    }

    if (SPEED != speed_prev)
    {
      tft.setCursor(5, 50);
      tft.setTextColor(ST7735_BLACK);
      tft.println((String)speed_prev);
      speed_prev = SPEED;
      tft.setCursor(5, 50);
      tft.setTextColor(ST7735_WHITE);
      tft.println((String)SPEED);
    }

    btn1.update();
    if (btn1.pressed())
    {
      SPEEDCOUNTER = SPEED;
      break;
    }
  }

  // завершение

  buzzerOn(6000, 250);
  delay(500);
  buzzerOn(6000, 250);

  delay(2000);
  clearScreen(ST7735_GREEN);
  ISRUN = true;
  pause();
}

void rfr()
{
  tft.setTextColor(ST7735_GREEN);
  tft.setCursor(1, y * 2);
  tft.println((String)SIGNAL_COUNTER_PREV);

  tft.setTextColor(ST7735_BLACK);
  tft.setCursor(1, y * 2);
  tft.println((String)SIGNAL_COUNTER);
  // tft.println((String)SIGNAL_COUNTER_TOTAL);

  SIGNAL_COUNTER_PREV = SIGNAL_COUNTER;
  // SIGNAL_COUNTER_PREV = SIGNAL_COUNTER_TOTAL;
  sec_prev = sec;
}

void loop()
{
  if (STATE == CALIBRATE_MENU)
  {
    calibrite_menu();
    STATE = 0;
  }

  if (ISRUN)
  {
    mes();
    if (ANOUT >= SIGNAL_MAX)
    {
      // сигнал сброса
      digitalWrite(RINT, HIGH);
      delay(10);
      digitalWrite(RINT, LOW);
      SIGNAL_COUNTER++;
      // SIGNAL_COUNTER_TOTAL++;
    }
  }

  // start-stop
  btn1.update();
  if (btn1.pressed())
  {
    buzzerOn(6000, 250);
    pause();
  }

  // сброс
  btn2.update();
  if (btn2.pressed())
  {
    SIGNAL_COUNTER = 0;
    // SIGNAL_COUNTER_TOTAL = 0;
    buzzerOn(6000, 250);
    delay(500);
    buzzerOn(6000, 250);
    rfr();
  }

  // калибровка
  btn3.update();
  if (btn3.pressed())
  {
    // calibrite();
    STATE = CALIBRATE_MENU;
  }

  if (ISRUN)
  {
    // обновим показания
    sec = rtc.getSeconds();
    if (sec != sec_prev)
    {
      // тик в 1 секунду
      if (SPEEDCOUNTER == 0)
      {
        SPEEDCOUNTER = SPEED;
        // SIGNAL_COUNTER = 0;
        // встаём на паузу
        pause();
      }
      else
      {
        SPEEDCOUNTER--;
      }
      rfr();
    }
  }
}

uint32_t mes()
{
  // int voltage_mV = map(adcValue, 0, 4095, 0, 3300);  // 0..4095 -> 0..3300 мВ

  ANOUT = map(analogRead(PA0), 0, 4095, 0, 3300);
  // ANCTRL = map(analogRead(PA1), 0, 4095, 0, 3300);

  if (0 == counter--)
  {
    counter = counter_max;
    tft.setTextColor(ST7735_GREEN);

    tft.setCursor(x, 1);
    tft.println((String)ANOUT_PREV);
    // tft.setCursor(x, y);
    // tft.println((String)ANCTRL_PREV);

    tft.setTextColor(ST7735_BLACK);

    tft.setCursor(x, 1);
    tft.println((String)ANOUT);
    // tft.setCursor(x, y);
    // tft.println((String)ANCTRL);

    ANOUT_PREV = ANOUT;
    ANCTRL_PREV = ANCTRL;

    tft.setCursor(0, y * 1);
    byte level = map(ANOUT, 0, SIGNAL_MAX, 0, 11);
    // level = 11;
    if (level > 11)
      level = 11;

    tft.setTextColor(ST7735_BLACK);
    tft.print("[");
    for (byte i = 0; i < level; i++)
    {
      tft.print("=");
    }
    tft.setTextColor(ST7735_GREEN);
    for (byte i = level; i < 11; i++)
    {
      tft.print("=");
    }
    tft.setTextColor(ST7735_BLACK);
    tft.print("]");
  }

  // sec = rtc.getSeconds();
  // if (sec != sec_prev)
  // {
  //   tft.setTextColor(ST7735_GREEN);
  //   tft.setCursor(1, y * 2);
  //   tft.println((String)SIGNAL_COUNTER_PREV);

  //   tft.setTextColor(ST7735_BLACK);
  //   tft.setCursor(1, y * 2);
  //   tft.println((String)SIGNAL_COUNTER);

  //   SIGNAL_COUNTER_PREV = SIGNAL_COUNTER;
  //   sec_prev = sec;
  // }

  return ANCTRL;
}

void buzzerOn(unsigned int freq, unsigned long duration)
{
  tone(BUZZER_PIN, freq, duration);
}

void buzzerOff()
{
  noTone(BUZZER_PIN);
}
