#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/timer.h"
#include "hardware/irq.h"
#include "pico/critical_section.h"
#include "pico/cyw43_arch.h"

#define A_PIN 11
#define B_PIN 13
#define C_PIN 17
#define D_PIN 15
#define E_PIN 14
#define F_PIN 12
#define G_PIN 16
const uint SEGMENTS[7] = {A_PIN,B_PIN,C_PIN,D_PIN,E_PIN,F_PIN,G_PIN};

#define D1_PIN 18  // décimas
#define D2_PIN 19  // unidades
#define D3_PIN 20  // decenas
#define D4_PIN 21  // centenas
#define DP_PIN 10
const uint DIGITS[4] = {D1_PIN,D2_PIN,D3_PIN,D4_PIN};

#define START_STOP_PIN 28
#define RESET_PIN      27
#define SET_PIN         9
#define INCREASE_PIN   22
const uint BTN[4] = {START_STOP_PIN, RESET_PIN, SET_PIN, INCREASE_PIN};

const uint8_t segmentTable[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};

volatile uint  tiempo_decimas      = 0;     // 0-9999
volatile uint8_t digits[4]         = {0};
volatile bool cronometro_activo    = false;
volatile bool cuenta_up            = true;  // true = asc, false = desc
volatile bool editing              = false;
volatile uint8_t edit_idx          = 0;
volatile bool blink_500            = false;
volatile uint8_t alarm_left        = 0;     // 10 → 5 s de parpadeo

static uint32_t last_us_start=0,last_us_reset=0,last_us_set=0,last_us_inc=0;

static void gpio_setup(void);
static void update_digits_from_val(uint16_t v);
static uint16_t digits_to_val(void);
static void clear_all(void);
static void display_digit(uint8_t d,uint8_t idx);
static bool t_mux  (repeating_timer_t*);
static bool t_tick (repeating_timer_t*);
static bool t_blink(repeating_timer_t*);
static void __isr gpio_btn_isr(uint gpio,uint32_t events);

int main(void)
{
    stdio_init_all();
    gpio_setup();
    update_digits_from_val(0);

    repeating_timer_t tmux, ttick, tblink;
    add_repeating_timer_ms(2,   t_mux,   NULL,&tmux);
    add_repeating_timer_ms(100, t_tick,  NULL,&ttick);
    add_repeating_timer_ms(500, t_blink, NULL,&tblink);

    while (true) tight_loop_contents();   
}

static void gpio_setup(void)
{
    /* segmentos y dígitos */
    for(int i=0;i<7;i++){ 
        gpio_init(SEGMENTS[i]); 
        gpio_set_dir(SEGMENTS[i],GPIO_OUT); 
        gpio_put(SEGMENTS[i],1);}

    for(int i=0;i<4;i++){ 
        gpio_init(DIGITS[i]);   
        gpio_set_dir(DIGITS[i],GPIO_OUT);  
        gpio_put(DIGITS[i],0);}

    gpio_init(DP_PIN); 
    gpio_set_dir(DP_PIN,GPIO_OUT); 
    gpio_put(DP_PIN,1);

    for(int i=0;i<4;i++){ 
        gpio_init(BTN[i]); 
        gpio_set_dir(BTN[i],GPIO_IN); 
        gpio_pull_down(BTN[i]); }

    //callback para los 4 pines 
    gpio_set_irq_enabled_with_callback(START_STOP_PIN, GPIO_IRQ_EDGE_RISE, true, gpio_btn_isr);
    gpio_set_irq_enabled          (RESET_PIN,      GPIO_IRQ_EDGE_RISE, true);
    gpio_set_irq_enabled          (SET_PIN,        GPIO_IRQ_EDGE_RISE, true);
    gpio_set_irq_enabled          (INCREASE_PIN,   GPIO_IRQ_EDGE_RISE, true);
}

static void __isr gpio_btn_isr(uint gpio,uint32_t events)
{
    uint32_t now=time_us_32();

    /* Start / Stop */
    if(gpio==START_STOP_PIN && now-last_us_start>200000){
        if(editing){                       // salir de edición
            tiempo_decimas = digits_to_val();
            cuenta_up      = (tiempo_decimas==0);
            editing        = false;
        }
        cronometro_activo = !cronometro_activo;
        last_us_start = now;
    }

    /* Reset */
    else if(gpio==RESET_PIN && now-last_us_reset>200000){
        cronometro_activo = false;
        editing=false;
        alarm_left=0;
        tiempo_decimas=0;
        update_digits_from_val(0);
        last_us_reset = now;
    }

    /* Set: seleccionar dígito */
    else if(gpio==SET_PIN && now-last_us_set>200000){
        if(!editing){ editing=true; edit_idx=0; }
        else         { edit_idx=(edit_idx+1)&3; }
        last_us_set = now;
    }

    /* Increase */
    else if(gpio==INCREASE_PIN && now-last_us_inc>200000){
        if(editing){
            digits[edit_idx]=(digits[edit_idx]+1)%10;
        }
        last_us_inc = now;
    }
}

static bool t_mux(repeating_timer_t*)
{
    static uint8_t idx=0;
    display_digit(digits[idx],idx);
    idx=(idx+1)&3;
    return true;
}

static bool t_tick(repeating_timer_t*)
{
    if(!cronometro_activo) return true;

    if(cuenta_up){
        if(++tiempo_decimas==10000) tiempo_decimas=0;
    }else{
        if(tiempo_decimas>0) --tiempo_decimas;
        if(tiempo_decimas==0){
            cronometro_activo=false;
            alarm_left=10;      // 5 s / 0.5 s
        }
    }
    if(!editing) update_digits_from_val(tiempo_decimas);
    return true;
}

static bool t_blink(repeating_timer_t*)
{
    blink_500 = !blink_500;
    if(alarm_left){
        alarm_left--;
        if(!alarm_left) update_digits_from_val(0);
    }
    return true;
}

static void update_digits_from_val(uint16_t v){
    digits[0]= v%10;
    digits[1]=(v/10)%10;
    digits[2]=(v/100)%10;
    digits[3]=(v/1000)%10;
}
static uint16_t digits_to_val(void){
    return digits[0]+digits[1]*10+digits[2]*100+digits[3]*1000;
}

static void clear_all(void){
    for(int i=0;i<4;i++) gpio_put(DIGITS[i],0);
    for(int i=0;i<7;i++) gpio_put(SEGMENTS[i],1);
    gpio_put(DP_PIN,1);
}
static void display_digit(uint8_t d,uint8_t idx)
{
    clear_all();

    /* Punto decimal */
    if(idx==1){
        bool dp = (!editing && cronometro_activo) ||
                  (!editing && !cronometro_activo && blink_500);
        if(dp) gpio_put(DP_PIN,0);
    }

    // alarma de fin y blink de edición 
    if((alarm_left && !blink_500) ||
       (editing && idx==edit_idx && !blink_500))
        return;

    uint8_t seg=segmentTable[d];
    gpio_put(A_PIN,!(seg&0x01));
    gpio_put(B_PIN,!(seg&0x02));
    gpio_put(C_PIN,!(seg&0x04));
    gpio_put(D_PIN,!(seg&0x08));
    gpio_put(E_PIN,!(seg&0x10));
    gpio_put(F_PIN,!(seg&0x20));
    gpio_put(G_PIN,!(seg&0x40));

    gpio_put(DIGITS[idx],1);
}