/*
 * @author  xamidev <xamidev@riseup.net>
 * @brief   System call handling
 * @license GPL-3.0-only
 */

#include "config.h"
#include "sched/scheduler.h"
#include <arch/x86.h>
#include <kernel.h>
#include <stddef.h>
#include <io/term/term.h>
#include <sched/process.h>
#include <io/kbd/ps2.h>
#include <fs/initfs.h>
#include <string/string.h>
#include <mem/utils.h>

extern struct process* current_process;

int sys_open(const char* filename, int flags);
int sys_close(int fd);
int sys_lseek(int fd, int offset, int whence);
int sys_tell(int fd); // needed by doom, therefore TOP PRIORITY
int sys_eof(int fd); // same
int sys_read(unsigned int fd, char* buf, size_t count);
int sys_write(unsigned int fd, const char* buf, size_t count);
int sys_exit(int error_code);
int sys_draw_fb(const uint8_t* src, int width, int height, int channels);

/*
 * syscall_handler - System call dispatcher
 * @regs: CPU state
 *
 * This function is called from the interrupt dispatcher,
 * when an interrupt 0x80 is emitted from userland.
 *
 * It switches control to the syscall number provided
 * in %rax. 
 *
 * We try to follow the System V convention here:
 * - syscall number in %rax
 * - args in %rdi, %rsi, %rdx, %r10, %r8, %r9
 * - return value (if any) in %rax
 *
 * Return:
 * <regs> - CPU state after system call
 */
struct cpu_status* syscall_handler(struct cpu_status* regs)
{
    switch (regs->rax)
    {
        case 0:
            DEBUG("sys_read(fd=%u, buf=%p, count=%u)", regs->rdi, regs->rsi, regs->rdx);
            regs->rax = sys_read(regs->rdi, (char*)regs->rsi, regs->rdx);
            break;
        case 1:
            DEBUG("sys_write(fd=%u, buf=%p, count=%u)", regs->rdi, regs->rsi, regs->rdx);
            regs->rax = sys_write(regs->rdi, (char*)regs->rsi, regs->rdx);
            break;
        case 2:
            DEBUG("sys_open(filename=%s, flags=%u)", regs->rdi, regs->rsi);
            regs->rax = sys_open((const char*)regs->rdi, regs->rsi);
            break;
        case 3:
            DEBUG("sys_close(fd=%u)", regs->rdi);
            regs->rax = sys_close(regs->rdi);
            break;
        case 8:
            DEBUG("sys_lseek(fd=%u, off=%d, whence=%u)", regs->rdi, regs->rsi, regs->rdx);
            regs->rax = sys_lseek(regs->rdi, regs->rsi, regs->rdx);
            break;
        case 9:
            DEBUG("sys_tell(fd=%u)", regs->rdi);
            regs->rax = sys_tell(regs->rdi);
            break;
        case 10:
            DEBUG("sys_eof(fd=%u)", regs->rdi);
            regs->rax = sys_eof(regs->rdi);
            break;
        case 11: // No DEBUG() here because it makes significant overhead
            regs->rax = sys_draw_fb((const uint8_t*)regs->rdi, regs->rsi, regs->rdx, regs->r10);
            break;
        case 60:
            DEBUG("sys_exit(error_code=%d)", regs->rdi);
            regs->rax = sys_exit(regs->rdi);
            break;
        default:
            DEBUG("Bad syscall! (rax=%p, rdi=%p, rsi=%p, rdx=%p)",
                    regs->rax, regs->rdi, regs->rsi, regs->rdx);
            regs->rax = 0xbad515ca11;
            break;
    }

    // save overhead for present_fb
    //DEBUG("returned rax=%p (%u)", regs->rax, regs->rax);

    return regs;
}