#include "SevSeg.h"
#include <LiquidCrystal_I2C.h>

// --- buzzer ---

void tom (char pino, int frequencia, int duracao);

#define buzzer 8
int modo = 0;

// Encoder
byte CLK = 2;    // Botão A do encoder
byte DT = 3;    // Botão B do encoder
byte SW = 4;   // Pino do botão do encoder
bool escolha;
byte dtvalue;
String modalidade = "";

// Time A
#define btnVerde_A 53
#define btnVermelho_A 52
#define Led_A 9

// Time B
#define btnVerde_B 35
#define btnVermelho_B 37
#define Led_B 10

// Inicializar display LCD
#define I2C_ADDR    0x27
#define LCD_COLUMNS 20
#define LCD_LINES   4

LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);


// Controlar os displays de 7 segmentos

// Time A
SevSeg P_A;
SevSeg S_A;

// Time B
SevSeg P_B;
SevSeg S_B;

unsigned long scoreA = 0; // Pontuação da equipe A
unsigned long scoreB = 0; // Pontuação da equipe B
int setsA = 0;  // Sets vencidos pela equipe A
int setsB = 0;  // Sets vencidos pela equipe B
int SetAtual = 1; // Set atual
bool Quadra = true; // Se o jogo é na quadra
bool tiebreak = false; // Se o jogo está na partida de desenpate

// Variaveis de somatória
unsigned long Ntotal = 0;

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  
  pinMode(btnVerde_A, INPUT_PULLUP);
  pinMode(btnVermelho_A, INPUT_PULLUP);
  pinMode(btnVerde_B, INPUT_PULLUP);
  pinMode(btnVermelho_B, INPUT_PULLUP);
  pinMode(Led_A, OUTPUT);
  pinMode(Led_B, OUTPUT);
  pinMode(buzzer, OUTPUT);


  // Display Padrão
  escolheModo();

  // Configuração Padrão (Display de 7 Segmentos)
  bool zerosNaEsquerda = false; // 'true' pra manter os zeros à esquerda
  bool PontoDecimal = true; // 'false' se o ponto decimal existe
  byte pinosDesenho[] = {51, 49, 47, 43, 45, 41, 39};

  // Display Pontuação A
  byte digitos_P_A = 6;
  byte pinos_P_A[] = {25, 23, 27, 33, 31, 29};
  P_A.begin(COMMON_ANODE, digitos_P_A, pinos_P_A, pinosDesenho, false,
  false, zerosNaEsquerda, PontoDecimal);

  // Display Sets A
  byte digitos_S_A = 1;
  byte pinos_S_A[] = {27};
  S_A.begin(COMMON_ANODE, digitos_S_A, pinos_S_A, pinosDesenho, false,
  false, zerosNaEsquerda, PontoDecimal);

  // Display Sets B
  byte digitos_S_B = 1;
  byte pinos_S_B[] = {33};
  S_B.begin(COMMON_ANODE, digitos_S_B, pinos_S_B, pinosDesenho, false,
  false, zerosNaEsquerda, PontoDecimal);
}

void loop() {
  // Lógica para adicionar e subtrair pontos

  pontuacao();

  if (digitalRead(SW) == LOW){
    reset();
  }
  // Npa + 10000 --> Somar 1 ponto visual no time A
  // Nsa + 1000 --> Somar 1 ponto visual no Set A
  // Nsb + 100 --> Somar 1 ponto visual no Set B
  // Npb + 1 ou Npb++ --> Somar 1 ponto visual no time B

  // Atualizando os displays de 7 segmentos com a pontuação atual
P_A.setNumber(Ntotal, 6); // n = 00-00-00, n + 100000, n + 10000, n + 1000, n + 100, n + 10 e n + 1.
P_A.refreshDisplay();

S_A.setNumber(setsA, 2);
S_A.refreshDisplay();

S_B.setNumber(setsB, 2);
S_B.refreshDisplay();

  switch (modo){
    //---------------------------------QUADRA---------------------------
    case 1:
      
      if ((scoreA >= 25) && (scoreA - scoreB >= 2)) {
      setsA++;
      delay(500);
      Serial.println(setsA);
      Ntotal = Ntotal + 1000;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("FIM DO SET");
      Ntotal = Ntotal - ((scoreA * 10000) + (scoreB));
      delay(2000);
      lcd.clear();

        if (setsA >= 2 ){
          lcd.print("O Time A venceu");
          delay(30000);
          // Npa + 10000 --> Somar 1 ponto visual no time A
          // Nsa + 1000 --> Somar 1 ponto visual no Set A
          // Nsb + 100 --> Somar 1 ponto visual no Set B
          // Npb + 1 ou Npb++ --> Somar 1 ponto visual no time B
        }

        else{
          lcd.print("Indo para o proximo Set...");
          delay(2000);
          lcd.clear();
          resetScores();
        }
      }

      else if (scoreB >= 25 && scoreB - scoreA >= 2) {
        setsB++;
        Ntotal = Ntotal + 100;
        delay(500);
        Serial.println(setsB);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("FIM DO SET");
        Ntotal = Ntotal - ((scoreA * 10000) + (scoreB));
        delay(30000);
        lcd.clear();
        
          if (setsA >= 2 ){
            lcd.print("O Time B venceu");
            Ntotal + 100;
            delay(30000);
            // Npa + 10000 --> Somar 1 ponto visual no time A
            // Nsa + 1000 --> Somar 1 ponto visual no Set A
            // Nsb + 100 --> Somar 1 ponto visual no Set B
            // Npb + 1 ou Npb++ --> Somar 1 ponto visual no time B
          }

        else{
          lcd.print("Indo para o proximo Set...");
          delay(2000);
          lcd.clear();
          resetScores();
        }
      }

    
      // Verificando se terá o tiebreak
      else if (setsA == 2 && setsB == 2) {
        if ((scoreA == 15 && scoreA - scoreB >= 2) || (scoreB == 15 && scoreB - scoreA >= 2)) {
          // Fim do tiebreak
          lcd.clear();
          lcd.setCursor(0, 0);
          Ntotal = 0;
          lcd.print("Tiebreak terminou");
          lcd.setCursor(0, 1);
          if (scoreA == 15 && scoreA - scoreB >= 2) {
            
            lcd.print("O Time A venceu");
        }

          else if (scoreB == 15 && scoreB - scoreA >= 2) {
            lcd.print("O Time B venceu");
        }

          delay(30000);
          reset();
        }
        else if ((scoreA == 8) || (scoreB == 8)) {
          // Troca de lados no tiebreak
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Troca de lados");
          lcd.clear();
          lcd.setCursor(0, 1);
          lcd.print("Pontos: " + String(scoreA) + "-" + String(scoreB));
          delay(30000);
        }
      }
      pontuacao();
    break;
    //---------------------------------AREIA---------------------------
    case 2:
      if (scoreA >= 21 && scoreA - scoreB >= 2) {
      setsA++;
      Ntotal = Ntotal + 1000;
      delay(500);
      Serial.println(setsA);
      lcd.clear();
      lcd.setCursor(0, 0);
      Ntotal = Ntotal - ((scoreA * 10000) + (scoreB));
      lcd.print("FIM DO SET");
      delay(30000);
      lcd.clear();

        if (setsA >= 2 ){
          lcd.print("O Time A venceu");
          Ntotal + 1000;
          delay(30000);
          // Npa + 10000 --> Somar 1 ponto visual no time A
          // Nsa + 1000 --> Somar 1 ponto visual no Set A
          // Nsb + 100 --> Somar 1 ponto visual no Set B
          // Npb + 1 ou Npb++ --> Somar 1 ponto visual no time B
        }

        else{
          lcd.print("Indo para o proximo Set...");
          delay(30000);
          lcd.clear();
          resetScores();
        }
      }

      else if (scoreB >= 21 && scoreB - scoreA >= 2) {
        setsB++;
        Ntotal = Ntotal + 100;
        delay(500);
        Serial.println(setsB);
        lcd.clear();
        lcd.setCursor(0, 0);
        Ntotal = Ntotal - ((scoreA * 10000) + (scoreB));
        lcd.print("FIM DO SET");
        delay(30000);
        lcd.clear();
      
        if (setsA >= 2 ){
          lcd.print("O Time B venceu");
          //Ntotal + 100;
          delay(30000);
          // Npa + 10000 --> Somar 1 ponto visual no time A
          // Nsa + 1000 --> Somar 1 ponto visual no Set A
          // Nsb + 100 --> Somar 1 ponto visual no Set B
          // Npb + 1 ou Npb++ --> Somar 1 ponto visual no time B
        }

        else{
          lcd.print("Indo para o proximo Set...");
          delay(2000);
          lcd.clear();
          resetScores();
        }
    }

    // Entrando no tiebreak
    else if (setsA == 1 && setsB == 1) {

      // Fim do tiebreak
      if (((scoreA == 15) && (scoreA - scoreB >= 2)) || ((scoreB == 15) && (scoreB - scoreA >= 2))) {
        lcd.clear();
        lcd.setCursor(0, 0);
        Ntotal = 0;
        lcd.print("Tiebreak terminou");
        lcd.clear();
        
        if (scoreA == 15 && scoreA - scoreB >= 2) {
          lcd.print("O Time A venceu");
        }

        else if (scoreB == 15 && scoreB - scoreA >= 2) {
          lcd.print("O Time B venceu");
        }

        delay(30000);
        reset();
      }

      // Troca de lados no tiebreak
      else if (((scoreA % 7 == 0) && (scoreA > 0)) || ((scoreB % 7 == 0) && (scoreB > 0))) {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Troca de lados");
        lcd.clear();
        lcd.setCursor(0, 1);
        lcd.print("Pontos: " + String(scoreA) + "-" + String(scoreB));
        delay(30000);
      }
    } 
    pontuacao();
  }

}

void pontuacao() {
    if (digitalRead(btnVerde_A) == LOW) {
    scoreA++;
    tone(buzzer, 400);
    delay(500);
    noTone(buzzer);
    Serial.println(scoreA);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Set " + String(SetAtual) + ":");
    lcd.setCursor(0, 1);
    lcd.print("Pontos: " + String(scoreA) + "-" + String(scoreB));
    digitalWrite(Led_A, HIGH);
    digitalWrite(Led_B, LOW);
    Ntotal = Ntotal + 10000;
  }

  else if (digitalRead(btnVermelho_A) == LOW) {
    scoreA--;
    tone(buzzer, 100);
    delay(500);
    noTone(buzzer);
    Serial.println(scoreA);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Set " + String(SetAtual) + ":");
    lcd.setCursor(0, 1);
    lcd.print("Pontos: " + String(scoreA) + "-" + String(scoreB));
    digitalWrite(Led_B, HIGH);
    digitalWrite(Led_A, LOW);
    Ntotal = Ntotal - 10000;
  }

  else if (digitalRead(btnVerde_B) == LOW) {
    scoreB++;
    tone(buzzer, 500);
    delay(500);
    noTone(buzzer);
    Serial.println(scoreB);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Set " + String(SetAtual) + ":");
    lcd.setCursor(0, 1);
    lcd.print("Pontos: " + String(scoreA) + "-" + String(scoreB));
    digitalWrite(Led_B, HIGH);
    digitalWrite(Led_A, LOW);
    Ntotal++;
  }

  else if (digitalRead(btnVermelho_B) == LOW) {
    scoreB--;
    tone(buzzer, 100);
    delay(500);
    noTone(buzzer);
    Serial.println(scoreB);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Set " + String(SetAtual) + ":");
    lcd.setCursor(0, 1);
    lcd.print("Pontos: " + String(scoreA) + "-" + String(scoreB));
    digitalWrite(Led_A, HIGH);
    digitalWrite(Led_B, LOW);
    Ntotal--;
  }
}

void resetScores() {
  scoreA = 0;
  scoreB = 0;
  SetAtual++;

  if (SetAtual <= 3) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Set " + String(SetAtual) + ":");
    lcd.setCursor(0, 1);
    lcd.print("Pontos: 0-0");
    musica();

    delay(30000);
  } else {
    
    // Fim do jogo
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Jogo terminou");
    delay(30000);
    reset();
  }
}

void reset() {
  scoreA = 0;
  scoreB = 0;
  setsA = 0;
  setsB = 0;
  Ntotal = 0;
  SetAtual = 1;
  Quadra = true;
  tiebreak = false;
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Gire o Encoder");
}

void escolheModo() {
  lcd.setCursor(3, 1);
  lcd.print("Escolha Entre:");
  lcd.setCursor(0, 2);
  lcd.print("a. Quadra  b. Areia");
  delay(1000);
  IniciarEncoder();
  if (modalidade != ""){
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Set " + String(SetAtual) + ":");
    lcd.setCursor(0, 1);
    lcd.print("Pontos: 0-0");
  }
}

void LERencoder() {
 
  int dtvalue = digitalRead(DT);
  delay(100);
  if (dtvalue == LOW){
    modalidade = "Areia";
    modo = 2;
  }

  else if (dtvalue == HIGH){
    modalidade = "Quadra";
    modo = 1;
  }

  escolha = true;
}

void IniciarEncoder() {
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(CLK), LERencoder, FALLING);

  while (escolha == false) {
    delay(25);
  }
  LERencoder();
  lcd.clear();
  lcd.setCursor(2,0);
  lcd.print("Modalidade");
  lcd.setCursor(8 - sizeof(modalidade) / 2, 1);
  lcd.print(modalidade);
  delay(3000);
  lcd.clear();

}

void musica() {

  int tempo = 400;
  tom(buzzer,440,tempo); //LA
  delay(tempo);
  tom(buzzer,294,tempo); //RE
  delay(tempo);
  tom(buzzer,349,tempo/2); //FA
  delay(tempo/2);
  tom(buzzer,392,tempo/2); //SOL
  delay(tempo/2);
  tom(buzzer,440,tempo); //LA
  delay(tempo);
  tom(buzzer,294,tempo); //RE
  delay(tempo);
  tom(buzzer,349,tempo/2); //FA
  delay(tempo/2);
  tom(buzzer,392,tempo/2); //SOL
  delay(tempo/2);
  tom(buzzer,330,tempo);
}

void tom(char pino, int frequencia, int duracao){
  float periodo = 1000.0/frequencia; //Periodo em ms
  for (int i = 0; i< duracao/(periodo);i++){ //Executa a rotina de dentro o tanta de vezes que a frequencia desejada cabe dentro da duracao
    digitalWrite(pino,HIGH);
    delayMicroseconds(periodo*500); //Metade do periodo em ms
    digitalWrite(pino, LOW);
    delayMicroseconds(periodo*500);
  }
}
$abcdeabcde151015202530354045505560fghijfghij