
#include "interrupts.h"

void init_pins(void) {

    // The reed switch (PD3/INT1) switches to ground and the RTC alarm output
    // (PD2/INT0) is open-drain, so both need the internal pull-up. Leaving them
    // floating makes the inputs self-trigger.
    SET_PIN_IN(DDRD, DDD3);
    SET_PIN_HIGH(PORTD, PD3);

    SET_PIN_IN(DDRD, DDD2);
    SET_PIN_HIGH(PORTD, PD2);
}

// Both external interrupts are configured low-level triggered (ISCn1:0 = 00).
// Edge detection needs the I/O clock, which SLEEP_MODE_PWR_DOWN stops, so a
// falling-edge INT0/INT1 can never wake the MCU. Only level detection is
// asynchronous. Each handler masks its own interrupt while the source is still
// asserted, so the low level does not retrigger in a loop.

void set_up_reed_interrupt(void) {
    EICRA &= ~((1 << ISC11) | (1 << ISC10));
    EIMSK |= (1 << INT1);
}

void set_up_minute_interrupt(void) {
    EICRA &= ~((1 << ISC01) | (1 << ISC00));
    EIMSK |= (1 << INT0);
}

void reed_interrupt_enable(void) {
    EIFR = (1 << INTF1); // Drop anything latched while we were masked
    EIMSK |= (1 << INT1);
}

void minute_interrupt_enable(void) {
    EIFR = (1 << INTF0);
    EIMSK |= (1 << INT0);
}

void wdt_isr_enable(void) {
    uint8_t sreg = SREG;
    cli();
    wdt_reset();

    // WDRF keeps WDE set, which would block the write below, so it must go
    // first. The unlock is a single assignment: a read-modify-write does not
    // open the 4-cycle change window.
    MCUSR &= ~(1 << WDRF);
    WDTCSR = (1 << WDCE) | (1 << WDE);

    // WDP[3:0] = 0b011 -> 0.125 s. Interrupt mode only (WDE clear), so an
    // expiry wakes us to clear the debounce instead of resetting the part.
    WDTCSR = (1 << WDIE) | (1 << WDP1) | (1 << WDP0);

    SREG = sreg; // Restore, never blanket-sei(): these run inside an ISR
}

void wdt_isr_disable(void) {
    uint8_t sreg = SREG;
    cli();
    wdt_reset();

    MCUSR &= ~(1 << WDRF);
    WDTCSR = (1 << WDCE) | (1 << WDE);
    WDTCSR = 0x00;

    SREG = sreg;
}
