#include "kernel.h"
#include "ioport.h"
#include "timer.h"
#include "intmgr.h"

#include "screen.h"

Timer SystemTimer;

class TimerInterruptHandler : public InterruptHandler
{
private: 
    Timer& timer;

public:
    TimerInterruptHandler(Timer& tm) : timer(tm) {}

    void operator()(uint32 intNum) {
        ++timer;
        IntMgr::setEoi(IntMgr::TIMER_IRQ);
    }
};

TimerInterruptHandler TimerIntHandler(SystemTimer);

Timer::Timer(void) : Count(0), JobQueue(JobCmp) {}

void Timer::init(void) {
    IoPortLib::out8(IoPortLib::PIT_CTRL, 0x34);
    IoPortLib::out8(IoPortLib::PIT_CNT0, 0x9C);
    IoPortLib::out8(IoPortLib::PIT_CNT0, 0x2E);

    InterruptManager.connect(IntMgr::TIMER_IRQ, TimerIntHandler);
    InterruptManager.clearMask(IntMgr::TIMER_MASK);
}

void Timer::add(TimerJob& job) {
    InterruptManager.disable();
    job.Start   = Count;
    job.Timeout = Count + job.Duration;
    JobQueue.enqueue(job);
    InterruptManager.enable();
}

void Timer::abort(TimerJob& job) {
    InterruptManager.disable();
    JobQueue.remove(job);
    InterruptManager.enable();
}

Timer& Timer::operator++(void) {
    if (++Count > MAX_COUNT) Count = 0;

    TimerJob* job = (TimerJob*)JobQueue.peek(0);
    if (job != 0) {
        checkExpire(*job);
    }
 
    return *this; 
}

void Timer::checkExpire(TimerJob& job) {
    if (job.Start > Count) {
        if (Timer::MAX_COUNT + Count > job.Timeout) {
            JobQueue.remove(job);
            job.onExpire();
        }
    } else {
        if (job.Timeout < Count) {
            JobQueue.remove(job);
            job.onExpire();
        }
    }
}
