//****************************************************************
// Librerías
//****************************************************************
#include <Arduino.h>
//****************************************************************
// Definición de etiquetas
//****************************************************************
#define LED_1B_PIN  GPIO_NUM_33  //Led 1 Contador
#define LED_2B_PIN  GPIO_NUM_25  //Led 2 Contador
#define LED_3B_PIN  GPIO_NUM_26  //Led 3 Contador
#define LED_4B_PIN  GPIO_NUM_27  //Led 4 Contador

#define LED_1T_PIN  GPIO_NUM_23  //Led 1 Timer
#define LED_2T_PIN  GPIO_NUM_22  //Led 2 Timer
#define LED_3T_PIN  GPIO_NUM_1  //Led 3 Timer
#define LED_4T_PIN  GPIO_NUM_3  //Led 4 Timer

#define LED_A_PIN  GPIO_NUM_21  //Led de Alerta
//****************************************************************
// Prototipos de funciones
//****************************************************************
void actualizarLEDB(); //Actualizar Led Contador
void actualizarLEDT(); //Actualizar Led Temporizador
void actualizarLedA(); //Actulizar Led de Alerta
//****************************************************************
// Variables Globales
//****************************************************************
bool ledState;  //Led de Alerta
volatile int interruptCounter;
int contadorT;
int contadorB;
unsigned long button_time = 0;
unsigned long last_button_time = 0;
//****************************************************************
// Definición de Estructura de Botones
//****************************************************************
struct Button {
  const uint8_t PIN;
  volatile bool pressed;
};

Button btnD = {34, false}; //Decremento de Contador Button 
Button btnI = {35, false}; //Incremento de Contador Button 
Button btnT = {36, false}; //Boton Tactil
//****************************************************************
// ISR: Interrupciones
//****************************************************************
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
//Interrupción Boton Decrementar
void IRAM_ATTR handlerBTNDISR() {
  portENTER_CRITICAL_ISR(&mux);
  button_time = xTaskGetTickCount(); //millis();
  if (button_time - last_button_time > 250) {
    btnD.pressed = true;
    last_button_time = button_time;
    }
    portEXIT_CRITICAL_ISR(&mux);
}
//Interrupción Boton Incrementar
void IRAM_ATTR handlerBTNIISR() {
  portENTER_CRITICAL_ISR(&mux);
  button_time = xTaskGetTickCount(); //millis();
  if (button_time - last_button_time > 250) {
    btnI.pressed = true;
    last_button_time = button_time;
    }
    portEXIT_CRITICAL_ISR(&mux);
}
//Interrupción Boton Tactil
void IRAM_ATTR handlerBTNTISR() {
  portENTER_CRITICAL_ISR(&mux);
  button_time = xTaskGetTickCount(); //millis();
  if (button_time - last_button_time > 250) {
    btnT.pressed = true;
    last_button_time = button_time;
    }
    portEXIT_CRITICAL_ISR(&mux);
}
//Interrupción Timer
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR handlerTimerISR() {
  portENTER_CRITICAL_ISR(&timerMux);
  interruptCounter++;
  portEXIT_CRITICAL_ISR(&timerMux);
}
//****************************************************************
//Configuración
//****************************************************************
void setup() {
  //Definir LEDs como salidas
  pinMode(LED_1B_PIN, OUTPUT); //Led1 Contador
  pinMode(LED_2B_PIN, OUTPUT); //Led2 Contador
  pinMode(LED_3B_PIN, OUTPUT); //Led3 Contador 
  pinMode(LED_4B_PIN, OUTPUT); //Led4 Contador

  pinMode(LED_1T_PIN, OUTPUT); //Led1 Timer
  pinMode(LED_2T_PIN, OUTPUT); //Led2 Timer
  pinMode(LED_3T_PIN, OUTPUT); //Led3 Timer
  pinMode(LED_4T_PIN, OUTPUT); //Led4 Timer

  pinMode(LED_A_PIN, OUTPUT); //Led de Alerta

  //Definir boton Decrementar como entrada e interrupción del botón
  pinMode(btnD.PIN, INPUT_PULLDOWN);
  attachInterrupt(btnD.PIN, handlerBTNDISR, FALLING);

  //Definir boton Incrementar como entrada e interrupción del botón
  pinMode(btnI.PIN, INPUT_PULLDOWN);
  attachInterrupt(btnI.PIN, handlerBTNIISR, FALLING);

  //Definir boton Incrementar como entrada e interrupción del botón
  pinMode(btnT.PIN, INPUT_PULLDOWN);
  attachInterrupt(btnT.PIN, handlerBTNTISR, FALLING);

  //Definir Parametros de Timer
  timer = timerBegin(0, 80, true); // (No. Timer, Prescaler, True: Subida, False: bajada )
  timerAttachInterrupt(timer, &handlerTimerISR, true); //(timer, función de ISR, True: flanco, False: nivel )
  timerAlarmWrite(timer, 250000, true); //(timer, valor de alarma, True: autoreload)
  timerAlarmEnable(timer);

  //Inicalizar variables
  ledState = HIGH;
  contadorB = 0;

  Serial.begin(115200);
}
//****************************************************************
// Loop Principal
//****************************************************************
void loop() {
  Serial.printf("Timer: %d - ", contadorT);
  Serial.printf("Boton %d\n", contadorB);
  if (interruptCounter > 0) {
    portENTER_CRITICAL(&timerMux);
    interruptCounter--;
    portEXIT_CRITICAL(&timerMux);
    contadorT++;
    actualizarLEDT();
  }
  
  portENTER_CRITICAL_ISR(&mux);
  if (btnI.pressed) {
    contadorB++;
    actualizarLEDB();
    btnI.pressed = false;
  }
  portEXIT_CRITICAL_ISR(&mux);

  portENTER_CRITICAL_ISR(&mux);
  if (btnD.pressed) {
    contadorB--;
    actualizarLEDB();
    btnD.pressed = false;
  }
  portEXIT_CRITICAL_ISR(&mux);

  portENTER_CRITICAL_ISR(&mux);
  if (btnT.pressed) {
    contadorT = 0;
    btnT.pressed = false;
  }
  portEXIT_CRITICAL_ISR(&mux);


  if (contadorT > 15){
      contadorT = 0;
    }
  if (contadorB > 15){
      contadorB = 0;
    }
  if (contadorB < 0){
      contadorB = 15;
    }

  if (contadorB == contadorT){
      Serial.println("Son Iguales");
      actualizarLedA();
    }

}
//****************************************************************
//Funciones
//****************************************************************
//Función para actualizar Leds de Contador con Boton
void actualizarLEDB() {
  digitalWrite(LED_1B_PIN, contadorB & 0x01);
  digitalWrite(LED_2B_PIN, (contadorB >> 1) & 0x01);
  digitalWrite(LED_3B_PIN, (contadorB >> 2) & 0x01);
  digitalWrite(LED_4B_PIN, (contadorB >> 3) & 0x01);
}
//Función para actualizar Leds de Contador con Timer
void actualizarLEDT() {
  digitalWrite(LED_1T_PIN, contadorT & 0x01);
  digitalWrite(LED_2T_PIN, (contadorT >> 1) & 0x01);
  digitalWrite(LED_3T_PIN, (contadorT >> 2) & 0x01);
  digitalWrite(LED_4T_PIN, (contadorT >> 3) & 0x01);
}

//Función para actualizar Led de Alerta
void actualizarLedA(){
  ledState = !ledState; // Invierte el estado actual del LED 
  digitalWrite(LED_A_PIN, ledState); // Aplica el nuevo estado al LED
  contadorT = 0; // Reinicia el contador para empezar de nuevo
}