#include "pico/stdlib.h"
#include <stdio.h>
#include <stdbool.h>
#include <math.h>

// Definir pines de segmento para el display de 7 segmentos (a - g)
const uint segmentPins[] = {2, 3, 4, 5, 6, 7, 8}; 

// Definir pines de selección de dígitos (para cada dígito)
const uint digitPins[] = {10, 11, 12, 13}; 

// Definir pines de botones (para las funciones de reinicio y reversa)
const uint resetPin = 14;
const uint reversePin = 15;

// Codificación para los dígitos 0-9 en un display de 7 segmentos de cátodo común
const bool segmentEncoding[10][7] = {
  {0, 0, 0, 0, 0, 0, 1}, // Mostrar 0
  {1, 0, 0, 1, 1, 1, 1}, // Mostrar 1
  {0, 0, 1, 0, 0, 1, 0}, // Mostrar 2
  {0, 0, 0, 0, 1, 1, 0}, // Mostrar 3
  {1, 0, 0, 1, 1, 0, 0}, // Mostrar 4
  {0, 1, 0, 0, 1, 0, 0}, // Mostrar 5
  {0, 1, 0, 0, 0, 0, 0}, // Mostrar 6
  {0, 0, 0, 1, 1, 1, 1}, // Mostrar 7
  {0, 0, 0, 0, 0, 0, 0}, // Mostrar 8
  {0, 0, 0, 0, 1, 0, 0}  // Mostrar 9
};

int counter = 0;  // Inicializar el contador
bool isReversing = false;  // Conteo inverso
uint64_t lastUpdateTime = 0;
const uint64_t updateInterval = 1;  // \ \1 ms para actualizaciones de pantalla

// Punteros de función para los callbacks de los botones
void (*onReset)(void);
void (*onReverse)(void);

// Reinicia el valor del contador
void resetCounter() {
    counter = 0;
}

// Alterna entre contar hacia arriba y hacia abajo
void switchReverse() {
    isReversing = !isReversing;
}

// Activa un dígito específico en el display
void activateDigit(int number, int position) {
    // Apagar todos los dígitos primero
    for (int i = 0; i < 4; i++) {
        gpio_put(digitPins[i], 0);
    }
    // Configurar los segmentos para el número especificado
    for (int i = 0; i < 7; i++) {
        gpio_put(segmentPins[i], segmentEncoding[number][i]);
    }
    // Encender el dígito seleccionado
    gpio_put(digitPins[position], 1);
}

// Muestra el número completo de 4 dígitos en el display
void showNumber(int value) {
    for (int i = 0; i < 4; i++) {
        int currentDigit = (value / (int)pow(10, 3 - i)) % 10;  // Extraer cada dígito
        activateDigit(currentDigit, i);  // Mostrar el dígito actual
        sleep_ms(5);  // Breve retraso para una visualización suave
    }
}

// Establece la función de callback para el botón de reinicio
void setResetFunction(void (*callback)(void)) {
    onReset = callback;
}

// Establece la función de callback para el botón de reversa
void setReverseFunction(void (*callback)(void)) {
    onReverse = callback;
}

int main() {
    // Inicializar pines GPIO para los segmentos
    for (int i = 0; i < 7; i++) {
        gpio_init(segmentPins[i]);
        gpio_set_dir(segmentPins[i], GPIO_OUT);
    }

    // Inicializar pines GPIO para la selección de dígitos
    for (int i = 0; i < 4; i++) {
        gpio_init(digitPins[i]);
        gpio_set_dir(digitPins[i], GPIO_OUT);
    }

    // Configurar botones con resistencias de pull-up
    gpio_init(resetPin);
    gpio_set_dir(resetPin, GPIO_IN);
    gpio_pull_up(resetPin);

    gpio_init(reversePin);
    gpio_set_dir(reversePin, GPIO_IN);
    gpio_pull_up(reversePin);

    // Asignar funciones de callback
    setResetFunction(resetCounter);
    setReverseFunction(switchReverse);

    // Bucle principal
    while (true) {
        // Mostrar el valor actual
        showNumber(counter);

        // Manejar la pulsación del botón de reinicio
        if (!gpio_get(resetPin) && onReset != NULL) {
            onReset();
        }

        // Manejar la pulsación del botón de reversa
        if (!gpio_get(reversePin) && onReverse != NULL) {
            onReverse();
        }

        // Gestionar el tiempo para el conteo
        uint64_t currentTime = time_us_64() / 1000;
        if (currentTime - lastUpdateTime >= updateInterval) {
            lastUpdateTime = currentTime;

            if (isReversing) {
                counter--;  // Decrementar en modo inverso
                if (counter < 0) {
                    counter = 9999;  // Volver a 9999
                }
            } else {
                counter++;  // Incrementar en modo normal
                if (counter >= 10000) {
                    counter = 0;  // Volver a 0
                }
            }
        }
    }

    return 0;
}