/*
 * @author  xamidev <xamidev@riseup.net>
 * @brief   PepperOS kernel shell
 * @license GPL-3.0-only
 */

#include "fs/initfs.h"
#include <io/term/term.h>
#include <config.h>
#include <io/kbd/ps2.h>
#include <string/string.h>
#include <stdint.h>
#include <kernel.h>
#include <time/date.h>
#include <mem/kheap.h>
#include <sched/process.h>

__attribute__((noinline))
void smash_it()
{
    char buf[16]; (void)buf;
    for (size_t i=0; i<256; i++) {
        buf[i] = (char)i;
    }
}

extern struct process* processes_list;

void ps()
{
    printf("pid\tname\tstatus\n");
    struct process* curr = processes_list;
    while (curr != NULL) {
        char* status;
        switch (curr->status) {
            case READY: status = "READY"; break;
            case RUNNING: status = "RUNNING"; break;
            case DEAD: status = "DEAD"; break;
            default: status = "N/A"; break;
        }
        printf("%u\t%s\t%s\n", curr->pid, curr->name, status);
        if (curr->next != NULL) {
            curr = curr->next;
        } else {
            break;
        }
    }
}

void kill()
{
    char input_buf[11] = {0};
    printf("pid> ");
    keyboard_getline(input_buf, 10);
    int pid = atoi(input_buf);

    struct process* curr = processes_list;
    while (curr != NULL) {
        if (curr->pid == (size_t)pid) {
            curr->status = DEAD; // equivalent of SIGKILL (no clean termination like SIGTERM)
            printf("killed %d\n", pid);
            break;
        }
        if (curr->next != NULL) {
            curr = curr->next;
        } else {
            printf("couldn't find process with PID %d\n", pid);
            break;
        }
    }
}

/*
 * pedicel_main - Kernel shell main function
 * @arg: argument (optional)
 *
 * This is the entry point for the kernel shell process.
 * It is used to start programs and to test different things
 * on different real hardware easily.
 *
 * Named after the root part of the pepper.
 */
void pedicel_main(void* arg)
{
    (void)arg;
    printf("Welcome to the kernel shell!\r\nType 'help' for a list of commands.\r\n");

    for (;;) {
        char input_buf[PEDICEL_INPUT_SIZE] = {0};
        printf(PEDICEL_PROMPT);
        keyboard_getline(input_buf, PEDICEL_INPUT_SIZE);
        
        if (strncmp(input_buf, "help", 4) == 0) {
            printf("++ shell builtins ++\r\n\r\n"
                            "\tclear   - clear the screen\n"
                            "\tpanic   - trigger a test panic\n"
                            "\tsyscall - trigger int 0x80\n"
                            "\tpf      - trigger a page fault\n"
                            "\tnow     - get current date\n"
                            "\tsmash   - smash the stack\n"
                            "\tmem     - get used heap info\n"
                            "\tload    - load an user executable\n"
                            "\tlist    - list initfs.tar contents\n"
                            "\tps      - list running processes\n"
                            "\tkill    - kill a running process by PID\n");
            continue;
        }

        if (strncmp(input_buf, "", 1) == 0) {
            continue;
        } 

        if (strncmp(input_buf, "clear", 5) == 0) {
            printf("\x1b[2J\x1b[H");
            continue;
        }

        if (strncmp(input_buf, "panic", 5) == 0) {
            panic(NULL, "test panic");
        }

        if (strncmp(input_buf, "syscall", 7) == 0) {
            __asm__ volatile("mov $0x00, %rdi");
            __asm__ volatile("int $0x80");
            continue;
        }

        if (strncmp(input_buf, "pf", 2) == 0) {
            volatile uint64_t* fault = (uint64_t*)0xdeadbeef;
            fault[0] = 1;
        }

        if (strncmp(input_buf, "now", 3) == 0) {
            struct date now = date_now();
            printf("Now is %02u:%02u:%02u on %u/%u/%u\r\n", now.hour, now.minute,
                now.second, now.day, now.month, now.year);
            continue;
        }

        if (strncmp(input_buf, "smash", 5) == 0) {
            smash_it();
            continue;
        }

        if (strncmp(input_buf, "mem", 3) == 0) {
            kheap_info();
            continue;
        }

        if (strncmp(input_buf, "load", 4) == 0) {
            loader_load_raw();
            continue;
        }

        if (strncmp(input_buf, "list", 4) == 0) {
            tar_list();
            continue;
        }

        if (strncmp(input_buf, "ps", 2) == 0) {
            ps();
            continue;
        }

        if (strncmp(input_buf, "kill", 4) == 0) {
            kill();
            continue;
        }

        printf("%s: command not found\r\n", input_buf);
    }
}