#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <lock.h>
#include <kprint.h>
#include <arch/amd64/hal/tsc.h>
#include <sys/time.h>


#define FORMAT_LENGTH       1
#define NORMAL              0
#define STATE_SHORT         2
#define STATE_LONG          3
#define FORMAT_SPECIFIER    1

#define LENGTH_DEFAULT      0
#define LENGTH_SHORT_SHORT  1
#define LENGTH_SHORT        2
#define LENGTH_LONG         3
#define LENGTH_LONG_LONG    4


void klog(const char *func, const char *msg, ...){
    kprintf("{ksk}: {s}\n", ANSI_COLOR_MAGENTA, func, ANSI_COLOR_RESET, msg);

    return;
}


atomic_flag printf_lock = ATOMIC_FLAG_INIT;

/* 
    printf()
    params:
        string
        arguments
    
    available format specifiers:
        {i}, {d} - integer
        {s}      - string
        {c}      - char
        {k}      - color
        {n}      - newline (doesnt take in a argument)
        {x}      - base16
        {b}      - binary
 */

int kprintf(const char *format_string, ...){
    extern struct flanterm_context *ft_ctx;
    acquire_spinlock(&printf_lock);
    int state = NORMAL;
    va_list a_list;
    va_start(a_list, format_string);
    for(uint64_t i = 0; i < strlen(format_string); i++){
        char current = format_string[i]; // current char in string
        switch (state){
            case NORMAL:
                switch (current) {
                    case '{':
                        state = FORMAT_SPECIFIER;
                        break;
                    case '\n':
                        print_char(ft_ctx, '\n');
                        print_char(ft_ctx, '\r');
                        break;
                    default:
                        print_char(ft_ctx, current);
                        break;                     
                }
                break;
            case FORMAT_SPECIFIER:
                switch (current) {
                    case 'n':
                        print_char(ft_ctx, '\n');
                        print_char(ft_ctx, '\r');
                        break;
                    case 'k':
                        print_str(ft_ctx, va_arg(a_list, char*));
                        break;
                    case 'd':
                    case 'i':
                        print_int(ft_ctx, va_arg(a_list, long long));
                        break;
                    case 's':
                        print_str(ft_ctx, va_arg(a_list, char*));
                        break;
                    case 'c':
                        ;
                        int ch = va_arg(a_list, int);
                        print_char(ft_ctx, ch);
                        break;
                    case 'x':
                        print_hex(ft_ctx, va_arg(a_list, uint64_t));
                        break;
                    case 'b':
                        print_bin(ft_ctx, va_arg(a_list, uint64_t));
                        break;
                    case 'l':
                        current++;
                        switch (current) {
                            case 'd':
                                print_int(ft_ctx, va_arg(a_list, long long int));
                                break;
                        
                        }
                        break;
                    case '}':
                        state = NORMAL;
                        break;

                }
                break;
        }

    }

    va_end(a_list);
    free_spinlock(&printf_lock);
    return 0;
}

int ksnprintf(char * str, const char *format_string, ...){
    int state = NORMAL;
    va_list a_list;
    va_start(a_list, format_string);
    for(uint64_t i = 0; i < strlen(format_string); i++){
        char current = format_string[i]; // current char in string
        switch (state){
            case NORMAL:
                switch (current) {
                    case '{':
                        state = FORMAT_SPECIFIER;
                        break;
                    default:
                        *(str+i) = current;
                        break;                     
                }
                break;
            case FORMAT_SPECIFIER:
                switch (current) {
                    case 'k':
                        break;
                    case 'd':
                    case 'i':
                        break;
                    case 's':
                        
                        break;
                    case 'c':
                        ;
                        int ch = va_arg(a_list, int);
                        
                        break;
                    case 'x':
                        
                        break;
                    case 'b':
                        
                        break;
                    case 'l':
                        current++;
                        switch (current) {
                            case 'd':
                                
                                break;
                        
                        }
                        break;
                    case '}':
                        state = NORMAL;
                        break;

                }
                break;
        }

    }

    va_end(a_list);
    return 0;
}


#define MAX_INTEGER_SIZE 128

void print_char(struct flanterm_context *ft_ctx, char c){
    kernel_framebuffer_print(&c, 1);
}

void print_str(struct flanterm_context *ft_ctx, char *str){
    kernel_framebuffer_print(str, strlen(str));
}

void print_int(struct flanterm_context *ft_ctx, uint64_t num){
    char buffer[MAX_INTEGER_SIZE] = {0};

    if(num == 0){
        buffer[0] = '0';
    }

    int arr[MAX_INTEGER_SIZE] = {0};
    int j = 0;

    while(num != 0){
        int mod = num % 10;
        arr[j] = dtoc(mod);
        num /= 10;
        j++;

        if(j == MAX_INTEGER_SIZE){
            return;
        }
    }

    /* Reverse buffer */
    for(int i = 0; i < j; i++){
        buffer[i] = arr[j - i - 1];
    }
    
    kernel_framebuffer_print(buffer, strlen(buffer));
}

void print_hex(struct flanterm_context *ft_ctx, uint64_t num){
    char buffer[MAX_INTEGER_SIZE] = {0};

    if(num == 0){
        buffer[0] = '0';
    }

    int arr[MAX_INTEGER_SIZE] = {0};
    int j = 0;

    while(num != 0){
        int mod = num % 16;
        arr[j] = dtoc(mod);
        num /= 16;
        j++;

        if(j == MAX_INTEGER_SIZE){
            return;
        }
    }

    /* Reverse buffer */
    for(int i = 0; i < j; i++){
        buffer[i] = arr[j - i - 1];
    }

    kernel_framebuffer_print(buffer, strlen(buffer));
}

void print_bin(struct flanterm_context *ft_ctx, uint64_t num){
    char buffer[MAX_INTEGER_SIZE] = {0};

    int arr[MAX_INTEGER_SIZE] = {0};
    int j = 0;

    while(num != 0){
        int mod = num % 2;
        arr[j] = dtoc(mod);
        num /= 2;
        j++;

        if(j == MAX_INTEGER_SIZE){
            return;
        }
    }

    /* Reverse buffer */
    for(int i = 0; i < j; i++){
        buffer[i] = arr[j - i - 1];
    }

    kernel_framebuffer_print(buffer, strlen(buffer));
}

char toupper(char c) {
    if (c >= 'a' && c <= 'z')
        return c - ('a' - 'A');
    return c;
}

/* Eventually fix printf so that these print_* functions dont
   write to the framebuffer but instead return to printf */

/* Prints a char array to the framebuffer, thread safe*/
void kernel_framebuffer_print(char *buffer, size_t n){
    extern struct flanterm_context *ft_ctx;
    flanterm_write(ft_ctx, buffer, n);
}
