#include <avr/io.h>
#include <avr/interrupt.h>

#define BUTTON_PIN PORTB0 // pin d8
#define LED_PIN PORTB5 // pin 13
#define TIMER_PIN PORTB1 // Using D9 as our timer pin

#define BAUD 9600
#define MY_UBRR F_CPU/16/BAUD-1

volatile uint32_t timer_count = 0;
volatile bool button_pressed = false;
volatile uint8_t button_debounce = 0;

void uart_init(unsigned int ubrr) {
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)ubrr;
    UCSR0B = (1 << TXEN0);
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
} // Initializes UART for serial communication

void uart_transmit(unsigned char data) {
    while (!(UCSR0A & (1 << UDRE0)));
    UDR0 = data;
} // Transmits a single character over UART

void uart_print(const char* str) {
    while (*str) {
        uart_transmit(*str++);
    }
} // Prints a string over UART

void uart_print_float(float value, int decimal_places) {
    char buffer[20];
    dtostrf(value, 0, decimal_places, buffer);
    uart_print(buffer);
} // Prints a float value over UART with specified decimal places

void timer1_init() {
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
    OCR1A = 249; 
    TIMSK1 |= (1 << OCIE1A);
} // Initializes Timer1 for 1ms interrupts

void timer2_init() {
    TCCR2A = (1 << WGM21);
    TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20);
    OCR2A = 249;
    TIMSK2 |= (1 << OCIE2A);
} // Timer 2's initalization is much the same, but used for debouncing.

void enable_global_interrupts() {
    sei();
} // Enables global interrupts

void button_init() {
    DDRB &= ~(1 << BUTTON_PIN);
    PORTB |= (1 << BUTTON_PIN);
} // Initializes the button pin as input with pull-up

void timer_pin_init() {
    DDRB |= (1 << TIMER_PIN); // Set TIMER_PIN as output
    PORTB &= ~(1 << TIMER_PIN); // Initially set it low
} // Initializes the timer pin as output and sets it low

bool is_button_pressed() {
    return (PINB & (1 << BUTTON_PIN)) == 0;
} // Checks if the button is currently pressed

void led_init() {
    DDRB |= (1 << LED_PIN);
} // Initializes the LED pin as output

void led_on() {
    PORTB |= (1 << LED_PIN);
} // Turns the LED on

void led_off() {
    PORTB &= ~(1 << LED_PIN);
} // Turns the LED off

ISR(TIMER1_COMPA_vect) {
    if (PINB & (1 << TIMER_PIN)) {
        timer_count++;
    }
} // Timer1 ISR: Increments timer_count when TIMER_PIN is high

ISR(TIMER2_COMPA_vect) {
    if (button_debounce > 0) {
        button_debounce--;
    }
} // Timer2 ISR: Handles button debouncing

void setup() {
    uart_init(MY_UBRR);
    timer1_init();
    timer2_init();
    button_init();
    timer_pin_init();
    led_init();
    enable_global_interrupts();
    uart_print("Button Press Duration Timer\n");
} // Initializes all components and prints a welcome message

void loop() {
    bool current_button_state = is_button_pressed();

    if (current_button_state && !button_pressed && button_debounce == 0) {
        timer_count = 0;
        button_pressed = true;
        PORTB |= (1 << TIMER_PIN); 
        uart_print("Button pressed!\n");
        button_debounce = 20;
        led_on();
    } else if (!current_button_state && button_pressed) {
        button_pressed = false;
        PORTB &= ~(1 << TIMER_PIN);
        button_debounce = 20;
        led_off();
        float elapsed_time = timer_count / 1000.0;
        uart_print("Total press time: ");
        uart_print_float(elapsed_time, 3);
        uart_print(" seconds\n");
    }
} // Main loop: Handles button press/release and calculates press duration