#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>

// LCD Físico I2C (Endereço 0x27 ou 0x3F)
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Teclado Matricial 4x4
const byte LINHAS = 4;
const byte COLUNAS = 4;

char teclas[LINHAS][COLUNAS] = {
  {'D', '#', '0', '*'},
  {'C', '9', '8', '7'},
  {'B', '6', '5', '4'},
  {'A', '3', '2', '1'}
};

byte pinosLinhas[LINHAS] = {9, 8, 7, 6};
byte pinosColunas[COLUNAS] = {5, 4, 3, 2};
Keypad teclado = Keypad(makeKeymap(teclas), pinosLinhas, pinosColunas, LINHAS, COLUNAS);

// Pinos dos Componentes (LED RGB nos pinos Analógicos A0, A1 e A2)
const int BUZZER       = 10;
const int LED_VERMELHO = A0;
const int LED_VERDE    = A1;
const int LED_AZUL     = A2;

// Variáveis de Controle
String senhaCorreta = "1234";
String senhaDigitada = "";
bool modoTexto = false; 
String statusSistema = "BLOQUEADO";
String corLED = "VERMELHO";
char ultimaTecla = '-';
bool campainhaAtiva = false;
bool sistemaDormindo = false;
int tentativasIncorretas = 0;

// Controle do alarme contínuo (não-bloqueante)
unsigned long ultimoTomAlarme = 0;
bool tomAlarmeAlto = false;

String linhaLCD1 = "Digite a Senha:";
String linhaLCD2 = "";

// Protótipos das Funções
void desbloquearFechadura();
void bloquearFechadura();
void dispararAlarmeGeral();
void pararAlarmeGeral();
void resetarEstado();
void atualizarLinha2();
void atualizarDisplayFisico();
void atualizarLED(String cor);
void desligarLEDs();
void tocarTomSucesso();
void tocarTomErro();
void gerenciarSomAlarme();
void enviarStatusSerial();
void processarTecla(char tecla);

void setup() {
  Serial.begin(9600);
  
  pinMode(LED_VERMELHO, OUTPUT);
  pinMode(LED_VERDE, OUTPUT);
  pinMode(LED_AZUL, OUTPUT);
  pinMode(BUZZER, OUTPUT);

  lcd.init();
  lcd.backlight();
  
  atualizarLED("VERMELHO");
  atualizarDisplayFisico();
  enviarStatusSerial();
}

void loop() {
  // Alarme contínuo no buzzer passivo
  if (statusSistema == "ALARME ATIVO" && !sistemaDormindo) {
    gerenciarSomAlarme();
  } else if (statusSistema != "ALARME ATIVO") {
    noTone(BUZZER);
  }

  // 1. Comandos e Teclas Remotas enviadas via Serial (Dashboard / Teclado Virtual)
  if (Serial.available() > 0) {
    String comando = Serial.readStringUntil('\n');
    comando.trim();

    if (comando.length() > 0) {
      if (comando == "SLEEP") {
        sistemaDormindo = true;
        noTone(BUZZER);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("_ _ _ _");
        lcd.setCursor(0, 1);
        lcd.print("DESLIGADO");
        lcd.noBacklight();
        desligarLEDs();
      } 
      else if (comando == "WAKE") {
        sistemaDormindo = false;
        lcd.backlight();
        atualizarLED(corLED);
        atualizarDisplayFisico();
        enviarStatusSerial();
      }
      else if (comando == "UNLOCK" && !sistemaDormindo) {
        desbloquearFechadura();
      }
      else if (comando == "LOCK" && !sistemaDormindo) {
        bloquearFechadura();
      }
      else if (comando == "ALARM" && !sistemaDormindo) {
        dispararAlarmeGeral();
      }
      else if (comando == "SILENCE" && !sistemaDormindo) {
        pararAlarmeGeral();
      }
      else if (comando == "RESET" && !sistemaDormindo) {
        tentativasIncorretas = 0;
        pararAlarmeGeral();
      }
      else if (comando.startsWith("KEY:") && !sistemaDormindo) {
        char teclaRemota = comando.charAt(4);
        processarTecla(teclaRemota);
      }
    }
  }

  if (sistemaDormindo) {
    delay(50);
    return;
  }

  // 2. Leitura de Teclas Físicas do Teclado Matricial
  char teclaFisica = teclado.getKey();
  if (teclaFisica) {
    processarTecla(teclaFisica);
  }
}

// ----------------------------------------------------
// Processamento Centralizado de Teclas (Físicas e Remotas)
// ----------------------------------------------------
void processarTecla(char tecla) {
  ultimaTecla = tecla;
  campainhaAtiva = false;

  // Tecla D: Envia sinal da campainha Web
  if (tecla == 'D') {
    campainhaAtiva = true;
  }
  // Tecla A: Alterna visibilidade da senha (oculto / visível)
  else if (tecla == 'A') {
    modoTexto = !modoTexto;
    atualizarLinha2();
    atualizarDisplayFisico();
  }
  // Tecla B: Bloqueia a fechadura
  else if (tecla == 'B') {
    bloquearFechadura();
  }
  // Tecla C: Dispara Alarme Geral
  else if (tecla == 'C') {
    dispararAlarmeGeral();
  }
  // Tecla *: Backspace (apaga 1 caractere)
  else if (tecla == '*') {
    if (senhaDigitada.length() > 0) {
      senhaDigitada.remove(senhaDigitada.length() - 1);
      
      if (senhaDigitada.length() == 0 && statusSistema != "ALARME ATIVO" && statusSistema != "DESBLOQUEADO") {
        statusSistema = "BLOQUEADO";
        atualizarLED("VERMELHO");
      }
      
      atualizarLinha2();
      atualizarDisplayFisico();
    }
  }
  // Tecla #: Confirma Senha
  else if (tecla == '#') {
    if (senhaDigitada.length() > 0) {
      if (senhaDigitada == senhaCorreta) {
        tentativasIncorretas = 0;
        desbloquearFechadura();
      } else {
        tentativasIncorretas++;
        tocarTomErro();

        if (tentativasIncorretas >= 5) {
          dispararAlarmeGeral();
        } else {
          linhaLCD1 = "Senha Incorreta!";
          linhaLCD2 = "Erros: " + String(tentativasIncorretas) + "/5";
          atualizarDisplayFisico();
          delay(1200);
          resetarEstado();
        }
      }
    }
  }
  // Teclas Numéricas (0 a 9)
  else if (tecla >= '0' && tecla <= '9') {
    if (senhaDigitada.length() < 8) {
      senhaDigitada += tecla;
      
      if (statusSistema != "ALARME ATIVO" && statusSistema != "DESBLOQUEADO") {
        statusSistema = "DIGITANDO";
        atualizarLED("AZUL");
      }
      
      atualizarLinha2();
      atualizarDisplayFisico();
    }
  }

  enviarStatusSerial();
}

// ----------------------------------------------------
// Funções de Controle de Alarme e Segurança
// ----------------------------------------------------

void gerenciarSomAlarme() {
  unsigned long tempoAtual = millis();
  if (tempoAtual - ultimoTomAlarme >= 150) {
    ultimoTomAlarme = tempoAtual;
    tomAlarmeAlto = !tomAlarmeAlto;
    if (tomAlarmeAlto) {
      tone(BUZZER, 1500);
    } else {
      tone(BUZZER, 800);
    }
  }
}

void dispararAlarmeGeral() {
  statusSistema = "ALARME ATIVO";
  atualizarLED("AZUL");
  linhaLCD1 = "ALARME ATIVADO!";
  linhaLCD2 = "Seguranca Maxima";
  atualizarDisplayFisico();
  enviarStatusSerial();
}

void pararAlarmeGeral() {
  noTone(BUZZER);
  resetarEstado();
  enviarStatusSerial();
}

void desbloquearFechadura() {
  noTone(BUZZER);
  statusSistema = "DESBLOQUEADO";
  atualizarLED("VERDE");
  linhaLCD1 = "Acesso Liberado";
  linhaLCD2 = "Bem-vindo!";
  atualizarDisplayFisico();
  enviarStatusSerial();
  tocarTomSucesso();
}

void bloquearFechadura() {
  noTone(BUZZER);
  tentativasIncorretas = 0;
  resetarEstado();
  enviarStatusSerial();
}

void resetarEstado() {
  senhaDigitada = "";
  statusSistema = "BLOQUEADO";
  atualizarLED("VERMELHO");
  linhaLCD1 = "Digite a Senha:";
  atualizarLinha2();
  atualizarDisplayFisico();
}

// ----------------------------------------------------
// Funções de Atualização de Tela e LEDs
// ----------------------------------------------------

void atualizarLinha2() {
  linhaLCD2 = "";
  for (unsigned int i = 0; i < senhaDigitada.length(); i++) {
    if (modoTexto) {
      linhaLCD2 += senhaDigitada[i];
    } else {
      linhaLCD2 += "*";
    }
  }
}

void atualizarDisplayFisico() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(linhaLCD1);
  lcd.setCursor(0, 1);
  lcd.print(linhaLCD2);
}

void atualizarLED(String cor) {
  corLED = cor;
  desligarLEDs();
  if (cor == "VERMELHO") {
    digitalWrite(LED_VERMELHO, HIGH);
  } else if (cor == "VERDE") {
    digitalWrite(LED_VERDE, HIGH);
  } else if (cor == "AZUL") {
    digitalWrite(LED_AZUL, HIGH);
  }
}

void desligarLEDs() {
  digitalWrite(LED_VERMELHO, LOW);
  digitalWrite(LED_VERDE, LOW);
  digitalWrite(LED_AZUL, LOW);
}

void tocarTomSucesso() {
  tone(BUZZER, 523, 100); delay(120);
  tone(BUZZER, 659, 100); delay(120);
  tone(BUZZER, 784, 200); delay(220);
  noTone(BUZZER);
}

void tocarTomErro() {
  tone(BUZZER, 220, 200); delay(250);
  tone(BUZZER, 180, 300); delay(350);
  noTone(BUZZER);
}

// ----------------------------------------------------
// Envio do Pacote JSON via Serial para a Interface Web
// ----------------------------------------------------

void enviarStatusSerial() {
  Serial.print("{\"l1\":\"");
  Serial.print(linhaLCD1);
  Serial.print("\",\"l2\":\"");
  Serial.print(linhaLCD2);
  Serial.print("\",\"tecla\":\"");
  Serial.print(ultimaTecla);
  Serial.print("\",\"modoSenha\":\"");
  Serial.print(modoTexto ? "TEXTO" : "OCULTO");
  Serial.print("\",\"status\":\"");
  Serial.print(statusSistema);
  Serial.print("\",\"corRGB\":\"");
  Serial.print(corLED);
  Serial.print("\",\"erros\":");
  Serial.print(tentativasIncorretas);
  Serial.print(",\"campainha\":");
  Serial.print(campainhaAtiva ? "true" : "false");
  Serial.println("}");
  
  campainhaAtiva = false;
}