
#include <timer.h>
#include <interrupts.h>
#include <audio.h>

static timer_context ctx = {0};

void timer_init() {
    ctx.div = 0XAC00;
}

void timer_tick() {
  	u16 prev_div = ctx.div;
    ctx.div++;
    bool timer_update = false;
    switch(ctx.tac & 0b11) {
        case 0b00:
            timer_update = (prev_div & (1 << 9)) && (!(ctx.div & (1 << 9)));
            break;

        case 0b01:
            timer_update = (prev_div & (1 << 3)) && (!(ctx.div & (1 << 3)));
            break;

        case 0b10:
            timer_update = (prev_div & (1 << 5)) && (!(ctx.div & (1 << 5)));
            break;

        case 0b11:
            timer_update = (prev_div & (1 << 7)) && (!(ctx.div & (1 << 7)));
            break;
    }

    if(timer_update && ctx.tac & (1 << 2)) {
        ctx.tima++;
        if(ctx.tima == 0xFF){
            ctx.tima = ctx.tma;
            cpu_request_interrupt(IT_TIMER);
        }
    }

    if((prev_div & (1 << 12)) && (!(ctx.div & (1 << 12)))){
        audio_tick();
    }

    if((prev_div & (1 << 0)) && (!(ctx.div & (1 << 0)))){
        audio_period_tick();
    }
}

void timer_write(u16 address, u8 value){
    switch(address) {
        case 0xFF04:
            //DIV
            ctx.div = 0;
            break;

        case 0xFF05:
            //TIMA
            ctx.tima = value;
            break;

        case 0xFF06:
            //TMA
            ctx.tma = value;
            break;

        case 0xFF07:
            //TAC
            ctx.tac = value;
            break;
    }
}

u8 timer_read(u16 address) {
        switch(address) {
        case 0xFF04:
            //DIV
            return ctx.div >> 8;

        case 0xFF05:
            //TIMA
            return ctx.tima;

        case 0xFF06:
            //TMA
            return ctx.tma;

        case 0xFF07:
            //TAC
            return ctx.tac;
    }
}

timer_context *timer_get_context() {
    return &ctx;
}
