///                MentOS, The Mentoring Operating system project
/// @file timer.c
/// @brief Timer implementation.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.

#include "hardware/timer.h"

#include "klib/irqflags.h"
#include "process/scheduler.h"
#include "hardware/pic8259.h"
#include "io/port_io.h"
#include "stdint.h"
#include "mem/kheap.h"
#include "io/debug.h"
#include "process/wait.h"
#include "drivers/rtc.h"
#include "descriptor_tables/isr.h"
#include "devices/fpu.h"
#include "system/signal.h"
#include "assert.h"
#include "sys/errno.h"

/// Number of ticks per seconds.
#define TICKS_PER_SECOND 1193

/// @defgroup picregs Programmable Interval Timer Registers
/// @brief The list of registers used to set the PIT.
/// @{

/// Channel 0 data port (read/write).
#define PIT_DATAREG0 0x40u

/// Channel 1 data port (read/write).
#define PIT_DATAREG1 0x41u

/// Channel 2 data port (read/write).
#define PIT_DATAREG2 0x42u

/// Mode/Command register (write only, a read is ignored).
#define PIT_COMREG 0x43u

/// @}

/// @brief Frequency divider value (1.193182 MHz).
#define PIT_DIVISOR 1193182

/// @brief   Command used to configure the PIT.
/// @details
/// Bits         Usage
/// 6 and 7 [Select channel]
///     0 0 = Channel 0
///     0 1 = Channel 1
///     1 0 = Channel 2
///     1 1 = Read-back command (8254 only)
/// 4 and 5 [Access mode]
///     0 0 = Latch count value command
///     0 1 = Access mode: lobyte only
///     1 0 = Access mode: hibyte only
///     1 1 = Access mode: lobyte/hibyte
/// 1 to 3  [Operating mode]
///     0 0 0 = Mode 0 (interrupt on terminal count)
///     0 0 1 = Mode 1 (hardware re-triggerable one-shot)
///     0 1 0 = Mode 2 (rate generator)
///     0 1 1 = Mode 3 (square wave generator)
///     1 0 0 = Mode 4 (software triggered strobe)
///     1 0 1 = Mode 5 (hardware triggered strobe)
///     1 1 0 = Mode 2 (rate generator, same as 010b)
///     1 1 1 = Mode 3 (square wave generator, same as 011b)
/// 0       [BCD/Binary mode]
///     0 = 16-bit binary
///     1 = four-digit BCD
///
/// Examples:
///     0x36 = 00|11|011|0
///     0x34 = 00|11|010|0
#define PIT_CONFIGURATION 0x34u

/// Mask used to set the divisor.
#define PIT_MASK 0xFFu

/// The number of ticks since the system started its execution.
static __volatile__ unsigned long timer_ticks = 0;

void timer_phase(const uint32_t hz)
{
    // Calculate our divisor.
    unsigned int divisor = PIT_DIVISOR / hz;
    // Set our command byte 0x36.
    outportb(PIT_COMREG, PIT_CONFIGURATION);
    // Set low byte of divisor.
    outportb(PIT_DATAREG0, divisor & PIT_MASK);
    // Set high byte of divisor.
    outportb(PIT_DATAREG0, (divisor >> 8u) & PIT_MASK);
}

void timer_handler(pt_regs *reg)
{
    // Save current process fpu state.
    switch_fpu();
    // Check if a second has passed.
    ++timer_ticks;
    // Update all timers
    run_timer_softirq();
    // Perform the schedule.
    scheduler_run(reg);
    // Restore fpu state.
    unswitch_fpu();
    // The ack is sent to PIC only when all handlers terminated!
    pic8259_send_eoi(IRQ_TIMER);
}

void timer_install()
{
    dynamic_timers_install();

    // Set the timer phase.
    timer_phase(TICKS_PER_SECOND);
    // Installs 'timer_handler' to IRQ0.
    irq_install_handler(IRQ_TIMER, timer_handler, "timer");
    // Enable the IRQ of the timer.
    pic8259_irq_enable(IRQ_TIMER);
}

uint64_t timer_get_seconds()
{
    return timer_ticks / TICKS_PER_SECOND;
}

unsigned long timer_get_ticks()
{
    return timer_ticks;
}

//======================================================================================
// Dynamics timers

/// Contains timer for each CPU (for now only one)
static tvec_base_t cpu_base = { 0 };

/// Contains all process waiting for a sleep
static wait_queue_head_t sleep_queue;

/// @brief Initialize dynamic timer system
void dynamic_timers_install()
{
#ifndef ENABLE_REAL_TIMER_SYSTEM
    list_head_init(&cpu_base.list);
#endif

    // Initialize tvec_base structure
    tvec_base_t *base = &cpu_base;
    base->timer_ticks = 0;

    for(int i = 0; i < TVR_SIZE; ++i)
        list_head_init(base->tv1.vec + i);

    for(int i = 0; i < TVN_SIZE; ++i) {

        list_head_init(base->tv2.vec + i);
        list_head_init(base->tv3.vec + i);
        list_head_init(base->tv4.vec + i);
        list_head_init(base->tv5.vec + i);
    }

    // Initialize sleeping process list
    list_head_init(&sleep_queue.task_list);
    spinlock_init(&sleep_queue.lock);
}

/// Prints used slots of timer vector
static void __print_tvec_slots(tvec_base_t *base, int tv_index) {
    
    if (tv_index < 0 || tv_index > 5)
        return;

    // Write buffer
    char result[TVN_SIZE + 1];
    result[TVN_SIZE] = '\0';

    struct timer_vec* tv = NULL;
    switch(tv_index) {
        
        // Root
        case 1: {
            
            pr_debug("base->tv1.vec:");
            for(int i = 0; i < TVR_SIZE; ++i) {

                // New line in order to not clutter the screen
                int index = i % TVN_SIZE;
                if (i != 0 && index == 0)
                    pr_debug("\n\t%s", result);

                if (!list_head_empty(base->tv1.vec + i))
                    result[index] = '1';
                else
                    result[index] = '0';
            }

            // The last line
            pr_debug("\n\t%s\n", result);
            return;

        } break;

        // Normal
        case 2: tv = &base->tv2; break;
        case 3: tv = &base->tv3; break;
        case 4: tv = &base->tv4; break;
        case 5: tv = &base->tv5; break;
    }

    for(int i = 0; i < TVN_SIZE; ++i) {
        if (list_head_empty(tv->vec + i))
            result[i] = '0';
        else
            result[i] = '1';
    }
            
    pr_debug("base->tv%d.vec:\n\t%s\n", tv_index, result);
}

/// Dump all timer vector in base
static inline void __dump_all_tvec_slots(tvec_base_t *base) {
    __print_tvec_slots(base, 1);
    __print_tvec_slots(base, 2);
    __print_tvec_slots(base, 3);
    __print_tvec_slots(base, 4);
    __print_tvec_slots(base, 5);
}

/// Select correct timer vector and position inside of it for the input timer
/// index contains the position inside of the tv_index timer vector
static void __find_tvec(tvec_base_t *base, struct timer_list *timer, int* index, int* tv_index)
{
    assert(index && "index is NULL");
    assert(tv_index && "tv_index is NULL");

    unsigned long expires = timer->expires;
    unsigned long ticks = expires - base->timer_ticks;

    unsigned long tv1_ticks = TIMER_TICKS(0);
    unsigned long tv2_ticks = TIMER_TICKS(1);
    unsigned long tv3_ticks = TIMER_TICKS(2);
    unsigned long tv4_ticks = TIMER_TICKS(3);

    // Can happen if you add a timer with expires == ticks, or in the past
    if ((signed long)ticks < 0) {
        *index = base->timer_ticks & TVR_MASK;
        *tv_index = 1;
    }
    // tv1
    else if (ticks < tv1_ticks) {
        *index = expires & TVR_MASK;
        *tv_index = 1;
    }
    // tv2
    else if (ticks < tv2_ticks) {
        *index = (expires >> TIMER_TICKS_BITS(0)) & TVN_MASK;
        *tv_index = 2;
    }
    // tv3
    else if (ticks < tv3_ticks) {
        *index = (expires >> TIMER_TICKS_BITS(1)) & TVN_MASK;
        *tv_index = 3;
    }
    // tv4
    else if (ticks < tv4_ticks) {
        *index = (expires >> TIMER_TICKS_BITS(2)) & TVN_MASK;
        *tv_index = 4;
    }
    // tv5
    else {
        *index = (expires >> TIMER_TICKS_BITS(3)) & TVN_MASK;
        *tv_index = 5;
    }
}

/// Add timers into different lists based on their expire time
static void __add_timer_tvec_base(tvec_base_t *base, struct timer_list *timer) {

    int index = 0, tv_index = 0;
    __find_tvec(base, timer, &index, &tv_index);

    struct list_head* vec;
    switch(tv_index) {
        case 1: vec = base->tv1.vec + index; break;
        case 2: vec = base->tv2.vec + index; break;
        case 3: vec = base->tv3.vec + index; break;
        case 4: vec = base->tv4.vec + index; break;
        case 5: vec = base->tv5.vec + index; break;
    }

    pr_debug("Adding timer at time_index: %d in tv%d\n", index, tv_index);
    list_head_add_tail(&timer->entry, vec);

#ifdef ENABLE_REAL_TIMER_SYSTEM_DUMP
    __dump_all_tvec_slots(base);
#endif

}

/// Remove timer from tvec_base
static void __rem_timer_tvec_base(tvec_base_t *base, struct timer_list *timer) {

    int index = 0, tv_index = 0;
    __find_tvec(base, timer, &index, &tv_index);

    struct list_head* vec;
    switch(tv_index) {
        case 1: vec = base->tv1.vec + index; break;
        case 2: vec = base->tv2.vec + index; break;
        case 3: vec = base->tv3.vec + index; break;
        case 4: vec = base->tv4.vec + index; break;
        case 5: vec = base->tv5.vec + index; break;
    }

    pr_debug("Removing timer at time_index: %d in tv%d\n", index, tv_index);
    list_head_del(&timer->entry);

#ifdef ENABLE_REAL_TIMER_SYSTEM_DUMP
    __dump_all_tvec_slots(base);
#endif 

}

/// Move all timers from tv up one level
static int cascate(tvec_base_t* base, timer_vec* tv, int time_index, int tv_index) {

    if (!list_head_empty(tv->vec + time_index)) {
        pr_debug("Relocating timers in tv%d.vec[%d]\n", tv_index, time_index);

        // Reinsert all timers into base in the new correct list
        struct list_head *it, *tmp;
        list_for_each_safe (it, tmp, tv->vec + time_index) {
            struct timer_list *timer = list_entry(it, struct timer_list, entry);
            list_head_del(it);

            __add_timer_tvec_base(base, timer);
        }

    }
    return time_index;
}

void run_timer_softirq()
{
    tvec_base_t *base = &cpu_base;
    spinlock_lock(&base->lock);

#ifdef ENABLE_REAL_TIMER_SYSTEM

    // While we are not up to date with current ticks
    unsigned long current_ticks = timer_get_ticks();
    while (base->timer_ticks <= current_ticks) {

        // Index of the current timer to execute
        int current_time_index = base->timer_ticks & TVR_MASK;

        // If the index is zero then all lists in base->tv1 have been checked, so they are empty
        if (!current_time_index) {

            // Consider the first invocation of the cascade() function: it receives as arguments 
            // the address in base, the address of base->tv2, and the index of the list
            // in base->tv2 including the timers that will decay in the next 256 ticks. This
            // index is determined by looking at the proper bits of the base->timer_ticks value. 
            // cascade() moves all dynamic timers in the base->tv2 list into the
            // proper lists of base->tv1; then, it returns a positive value, unless all base->tv2
            // lists are now empty. If so, cascade() is invoked once more to replenish
            // base->tv2 with the timers included in a list of base->tv3, and so on.

            int tv2_index = (base->timer_ticks >> TIMER_TICKS_BITS(0)) & TVN_MASK;
            int tv3_index = (base->timer_ticks >> TIMER_TICKS_BITS(1)) & TVN_MASK;
            int tv4_index = (base->timer_ticks >> TIMER_TICKS_BITS(2)) & TVN_MASK;
            int tv5_index = (base->timer_ticks >> TIMER_TICKS_BITS(3)) & TVN_MASK;

            if (!cascate(base, &base->tv2, tv2_index, 2) && 
                !cascate(base, &base->tv3, tv3_index, 3) && 
                !cascate(base, &base->tv4, tv4_index, 4) &&    
                !cascate(base, &base->tv5, tv5_index, 5));
        }

        // If there are timers to execute in this instant
        if (!list_head_empty(&base->tv1.vec[current_time_index])) {
            pr_notice("Executing dynamic timers at %d ticks from start inside of tv1.vec[%d]\n", 
                base->timer_ticks, current_time_index);

            // Trigger all timers
            struct list_head *it, *tmp;
            list_for_each_safe (it, tmp, &base->tv1.vec[current_time_index]) {
                struct timer_list *timer = list_entry(it, struct timer_list, entry);

                // Executes timer function
                spinlock_unlock(&base->lock);
                pr_notice("Executing dynamic timer function...\n");
                timer->function(timer->data);
                spinlock_lock(&base->lock);

                // Removes timer from list
                list_head_del(it);
                kfree(timer);
            }
        }

        // Advance timer check
        ++base->timer_ticks;
    }

    base->running_timer = NULL;

#else

    struct list_head *it, *tmp;
    list_for_each_safe (it, tmp, &base->list) {
        struct timer_list *timer = list_entry(it, struct timer_list, entry);
        if (timer->expires <= timer_get_ticks()) {
            base->running_timer = timer;
            timer->base         = NULL;

            // Executes timer function
            spinlock_unlock(&base->lock);
            pr_notice("Executing dynamic timer function...\n");
            timer->function(timer->data);
            spinlock_lock(&base->lock);

            // Removes timer from list
            pr_notice("Removing dynamic timer...\n");
            list_head_del(it);
            kfree(timer);
        }
    }

#endif

    base->running_timer = NULL;
    spinlock_unlock(&base->lock);
}

void init_timer(struct timer_list *timer)
{
    timer->base = NULL;
    list_head_init(&timer->entry);
    spinlock_unlock(&timer->lock);
}

void add_timer(struct timer_list *timer)
{
    tvec_base_t *base = &cpu_base;
    timer->base = base;

#ifdef ENABLE_REAL_TIMER_SYSTEM
    __add_timer_tvec_base(base, timer);
#else
    list_head_add_tail(&timer->entry, &base->list);
#endif

}

void del_timer(struct timer_list *timer)
{
    tvec_base_t *base = &cpu_base;
    timer->base = NULL;

#ifdef ENABLE_REAL_TIMER_SYSTEM
    __rem_timer_tvec_base(base, timer);
#else
    list_head_del(&timer->entry);
#endif

}

//======================================================================================
// Sleep

/// @brief Debugging function.
/// @param data The data.
static inline void debug_timeout(unsigned long data)
{
    pr_notice("Il timer è stato attivato con successo: %d, ticks: %d, seconds: %d\n",
              data, timer_ticks, timer_get_seconds());
}

/// @brief Contains the entry of a wait queue and timespec which keeps trakc of
///        the remaining time.
typedef struct sleep_data_t {
    /// POinter to the entry of a wait queue.
    wait_queue_entry_t *entry;
    /// Keeps track of the remaining time.
    timespec *rem;
} sleep_data_t;

/// @brief Callback for when a sleep timer expires
/// @param data Custom data stored in the timer 
void sleep_timeout(unsigned long data)
{
    // NOTE: We could modify the sleep_on and make it return the wait_queue_entry_t
    //       and then store it in the dynamic timer data member instead of the task pid,
    //       this would remove the need to iterate the sleep queue list.

    sleep_data_t *sleep_data = (sleep_data_t *)data;

    wait_queue_entry_t *entry = sleep_data->entry;
    task_struct *task         = entry->task;

    // Executed entry's wakeup test function
    int res = entry->func(entry, 0, 0);
    if (res == 1) {
        // Removes entry from list and memory
        remove_wait_queue(&sleep_queue, entry);
        kfree(entry);

        pr_debug("Process (pid: %d) restored from sleep\n", task->pid);
    }
}

int sys_nanosleep(const timespec *req, timespec *rem)
{
    // Probabilmente devi salvare rem da qualche parte, perche' dentro ci va
    // messo quanto tempo mancava allo scadere del timer nel caso in cui il
    // timer venga interrotto prima da un segnale.
    pr_debug("sys_nanosleep([s:%d; ns:%d],...)\n", req->tv_sec, req->tv_nsec);

    // Saves pid and rem timespec
    sleep_data_t *data = kmalloc(sizeof(sleep_data_t));
    data->rem          = rem;

    // Create a dinamic timer to wake up the process after some time
    struct timer_list *sleep_timer = kmalloc(sizeof(struct timer_list));
    init_timer(sleep_timer);

    sleep_timer->expires  = timer_get_ticks() + TICKS_PER_SECOND * req->tv_sec;
    sleep_timer->function = &sleep_timeout;
    sleep_timer->data     = (unsigned long)data;

    // Removes current process from runqueue and stores it in the waiting queue,
    // this must be done at the end, because it changes the current active page
    // and invalidates the req and rem pointers (?)
    wait_queue_entry_t *entry = sleep_on(&sleep_queue);
    data->entry               = entry;

    add_timer(sleep_timer);
    return -1;
}

/// @brief Function executed when the real_timer of a process expires, sends SIGALRM to process.
/// @param pid PID of the process whos associated timer has expired 
void alarm_timeout(unsigned long pid)
{
    sys_kill(pid, SIGALRM);

    struct task_struct *cur = scheduler_get_current_process();
    cur->real_timer         = NULL;
}

int sys_alarm(int seconds)
{
    pr_debug("sys_alarm(seconds:%d)\n", seconds);

    struct task_struct *current = scheduler_get_current_process();
    struct timer_list *timer;

    // If there is already a timer running
    int result = 0;
    if (current->real_timer != NULL) {
        del_timer(current->real_timer);
        result = (current->real_timer->expires - timer_get_ticks()) / TICKS_PER_SECOND;
        timer  = current->real_timer;

        // Returns only the amount of seconds remaining
        if (seconds == 0) {
            kfree(current->real_timer);
            current->real_timer = NULL;
            return result;
        }
    } else {
        if (seconds == 0)
            return 0;

        // Allocate new timer
        timer = (struct timer_list *)kmalloc(sizeof(struct timer_list));
    }

    current->real_timer = timer;
    init_timer(timer);

    timer->expires  = timer_get_ticks() + TICKS_PER_SECOND * seconds;
    timer->function = &alarm_timeout;
    timer->data     = current->pid;

    add_timer(timer);

    return result;
}

static void calc_itimerval(unsigned long incr, unsigned long value, struct itimerval *result)
{
    result->it_interval.tv_sec  = incr / TICKS_PER_SECOND;
    result->it_interval.tv_usec = incr / TICKS_PER_SECOND * 1000;

    result->it_value.tv_sec  = value / TICKS_PER_SECOND;
    result->it_value.tv_usec = value / TICKS_PER_SECOND * 1000;
}

static void update_task_itimerval(int which, const struct itimerval *val)
{
    unsigned long interval_ticks = val->it_interval.tv_sec * TICKS_PER_SECOND;
    interval_ticks += val->it_interval.tv_usec * TICKS_PER_SECOND / 1000;

    unsigned long value_ticks = val->it_value.tv_sec * TICKS_PER_SECOND;
    value_ticks += val->it_value.tv_usec * TICKS_PER_SECOND / 1000;

    struct task_struct *curr = scheduler_get_current_process();
    switch (which) {
    case ITIMER_REAL:
        curr->it_real_incr  = interval_ticks;
        curr->it_real_value = value_ticks;
        break;

    case ITIMER_VIRTUAL:
        curr->it_virt_incr  = interval_ticks;
        curr->it_virt_value = value_ticks;
        break;

    case ITIMER_PROF:
        curr->it_prof_incr  = interval_ticks;
        curr->it_prof_value = value_ticks;
        break;
    }
}

int sys_getitimer(int which, struct itimerval *curr_value)
{
    // Invalid time domain
    if (which < 0 || which > 3)
        return EINVAL;

    struct task_struct *curr = scheduler_get_current_process();
    switch (which) {
    case ITIMER_REAL: {
        // Extract remaining time in dynamic timer
        unsigned long value = curr->real_timer->expires - timer_get_ticks();
        curr->it_real_value = value;
        calc_itimerval(curr->it_real_incr, curr->it_real_value, curr_value);
    } break;
    case ITIMER_VIRTUAL:
        calc_itimerval(curr->it_virt_incr, curr->it_virt_value, curr_value);
        break;
    case ITIMER_PROF:
        calc_itimerval(curr->it_prof_incr, curr->it_prof_value, curr_value);
        break;
    }
    return 0;
}

// Real timer interval timemout
static void it_real_fn(unsigned long pid)
{
    struct task_struct *cur = scheduler_get_running_process(pid);
    sys_kill(pid, SIGALRM);

    // If the real incr is not 0 then restart
    if (cur->it_real_incr != 0) {
        // Create new timer for process
        struct timer_list *real_timer = (struct timer_list *)kmalloc(sizeof(struct timer_list));
        cur->real_timer               = real_timer;
        init_timer(real_timer);

        real_timer->expires  = timer_get_ticks() + cur->it_real_incr;
        real_timer->function = &it_real_fn;
        real_timer->data     = cur->pid;

        add_timer(real_timer);
        return;
    }

    // No more timer
    cur->real_timer = NULL;
}

int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value)
{
    // Invalid time domain
    if (which < 0 || which > 3)
        return EINVAL;

    // Returns old timer interval
    if (old_value != NULL)
        sys_getitimer(which, old_value);

    // Get ticks of interval
    unsigned long interval_ticks = new_value->it_interval.tv_sec * TICKS_PER_SECOND;
    interval_ticks += new_value->it_interval.tv_usec * TICKS_PER_SECOND / 1000;

    // If interval is 0 removes timer
    struct task_struct *cur = scheduler_get_current_process();
    if (interval_ticks == 0) {
        // Removes real_timer
        if (which == ITIMER_REAL && cur->real_timer != NULL)
            cur->real_timer = NULL;

        update_task_itimerval(which, new_value);
        return -1;
    }

    switch (which) {
    // Uses Dynamic Timers
    case ITIMER_REAL: {
        // Remove real_timer if already in use
        struct timer_list *timer = cur->real_timer;
        if (timer != NULL) {
            del_timer(timer); // Recycle memory
        } else {
            // Alloc new timer
            timer = (struct timer_list *)kmalloc(sizeof(struct timer_list));
        }

        init_timer(timer);

        timer->expires  = timer_get_ticks() + interval_ticks;
        timer->function = &it_real_fn;
        timer->data     = cur->pid;

        add_timer(timer);

    } break;

    case ITIMER_VIRTUAL:
    case ITIMER_PROF:
        break;
    }

    update_task_itimerval(which, new_value);
    return -1;
}

void update_process_profiling_timer(task_struct *proc)
{
    // If the timer is active
    if (proc->it_prof_incr != 0) {
        proc->it_prof_value += proc->se.exec_runtime;
        if (proc->it_prof_value >= proc->it_prof_incr) {
            sys_kill(proc->pid, SIGPROF);
            proc->it_prof_value = 0;
        }
    }
}