#pragma once

#include <cstdint>

struct TimerControl {
    union {
        struct {
            uint32_t MDT0 : 1;
            uint32_t UDT0 : 1;
            uint32_t GENT0 : 1;
            uint32_t CAPT0 : 1;
            uint32_t ARHT0 : 1;
            uint32_t LOAD0 : 1;
            uint32_t ENIT0 : 1;
            uint32_t ENT0 : 1;
            uint32_t T0INT : 1;
            uint32_t PWMA0 : 1;
            uint32_t ENALL : 1;
            uint32_t CASC : 1;

            uint32_t reserved : 20;
        };
        uint32_t raw;
    };
};

struct Timer {
    volatile TimerControl TCSR0;
    volatile uint32_t TLR0;
    volatile uint32_t TCR0;

    uint32_t _reserved;

    volatile TimerControl TCSR1;
    volatile uint32_t TLR1;
    volatile uint32_t TCR1;

    void EnableT1() {
        TCSR1.ARHT0 = 1;
        TCSR1.ENIT0 = 1;  // enable interrupt for overflows
        TCSR1.ENT0 = 1;
    }

    uint32_t GetT1Ticks() { return TCR1; }

    bool HasT1Overflowed() { return TCSR1.T0INT; }
    void ClearT1Overflow() { TCSR1.T0INT = 1; }

    void SetupAsWdt(uint32_t timeout_ticks) {
        TLR0 = timeout_ticks;
        TCSR0.LOAD0 = 1;  // reset counter
        TCSR0.UDT0 = 1;   // count backwards from the load value
        TCSR0.ENIT0 = 1;  // enable interrupt

        TCSR0.LOAD0 = 0;  // allow counter to run
        TCSR0.ENT0 = 1;   // enable timer
    }

    void Pet() {
        TCSR0.ENT0 = 0;
        TCSR0.LOAD0 = 1;
        TCSR0.LOAD0 = 0;
        TCSR0.ENT0 = 1;
    }

    void ClearInterrupt() { TCSR0.T0INT = 0; }

    static Timer* Instance(uint32_t base) {
        return reinterpret_cast<Timer*>(base);
    }
};
