#include "smp.h"
#include <stdatomic.h>
#include <uacpi/kernel_api.h>
#include <uacpi/log.h>
#include <uacpi/status.h>
#include <uacpi/types.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <uacpi/uacpi.h>
#include <sys/pci.h>
#include <mm/vmm.h>
#include <mm/kmalloc.h>
#include <limine.h>
#include <kprint.h>
#include <neobbo.h>
#include <arch/amd64/io.h>
#include <sys/time.h>

extern uint64_t hhdmoffset;

uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr *out_rsdp_address){
    extern struct limine_rsdp_request rsdp_request;
    assert(rsdp_request.response != NULL);
    out_rsdp_address = (uacpi_phys_addr*)rsdp_request.response->address;
    return UACPI_STATUS_OK;
}

void uacpi_kernel_log(uacpi_log_level l, const uacpi_char* str){
    char* level;

    switch(l){
        case UACPI_LOG_ERROR:
            kprintf("{k}[uACPI] {sk}\n", ANSI_COLOR_RED, str, ANSI_COLOR_RESET);
            break;
        case UACPI_LOG_WARN:
            kprintf("{k}[uACPI] {sk}\n", ANSI_COLOR_YELLOW, str, ANSI_COLOR_RESET);
            break;
        case UACPI_LOG_INFO:
            kprintf("{k}[uACPI]{k} {s}\n", ANSI_COLOR_MAGENTA, ANSI_COLOR_RESET, str);
            break;
        default:
           kprintf("[uACPI] {s}\n", str); 
    }
    
}

uacpi_status uacpi_kernel_pci_device_open(uacpi_pci_address address, uacpi_handle *out_handle){

    *out_handle = kzalloc(sizeof(uacpi_pci_address));

    memcpy(*out_handle, &address, sizeof(uacpi_pci_address));

    return UACPI_STATUS_OK;
}

void uacpi_kernel_pci_device_close(uacpi_handle handle){
    free(handle);
    return;
}

uacpi_status uacpi_kernel_pci_read8(uacpi_handle device, uacpi_size offset, uacpi_u8 *value){
    *value = *(uint8_t*)((uint64_t)device + offset);
    return UACPI_STATUS_OK;
}

uacpi_status uacpi_kernel_pci_read16(uacpi_handle device, uacpi_size offset, uacpi_u16 *value){
    *value = *(uint16_t*)((uint64_t)device + offset);
    return UACPI_STATUS_OK;
}

uacpi_status uacpi_kernel_pci_read32(uacpi_handle device, uacpi_size offset, uacpi_u32 *value){
    *value = *(uint32_t*)((uint64_t)device + offset);
    return UACPI_STATUS_OK;
}

uacpi_status uacpi_kernel_pci_write8(uacpi_handle device, uacpi_size offset, uacpi_u8 value){
     *(uint8_t*)((uint64_t)device + offset + get_kinfo()->hhdmoffset) = value;
     return UACPI_STATUS_OK;
}

uacpi_status uacpi_kernel_pci_write16(uacpi_handle device, uacpi_size offset, uacpi_u16 value){
     *(uint16_t*)((uint64_t)device + offset + get_kinfo()->hhdmoffset) = value;
     return UACPI_STATUS_OK;
}

uacpi_status uacpi_kernel_pci_write32(uacpi_handle device, uacpi_size offset, uacpi_u32 value){
     *(uint32_t*)((uint64_t)device + offset + get_kinfo()->hhdmoffset) = value;
     return UACPI_STATUS_OK;
}

uacpi_status uacpi_kernel_io_map(uacpi_io_addr base, uacpi_size len, uacpi_handle *out_handle){
    return UACPI_STATUS_UNIMPLEMENTED;
}

void uacpi_kernel_io_unmap(uacpi_handle handle){
    asm("nop");
}

uacpi_status uacpi_kernel_io_read8(uacpi_handle handle, uacpi_size offset, uacpi_u8 *out_value){
    *out_value = inb(((uint16_t)handle + offset));
    return UACPI_STATUS_OK;
}


uacpi_status uacpi_kernel_io_read16(uacpi_handle handle, uacpi_size offset, uacpi_u16 *out_value){
    *out_value = inw(((uint16_t)handle + offset));
    return UACPI_STATUS_OK;
}


uacpi_status uacpi_kernel_io_read32(uacpi_handle handle, uacpi_size offset, uacpi_u32 *out_value){
    *out_value = inl(((uint16_t)handle + offset));
    return UACPI_STATUS_OK;
}


uacpi_status uacpi_kernel_io_write8(uacpi_handle handle, uacpi_size offset, uacpi_u8 in_value){
    outb(((uint16_t)handle + offset), in_value);
    return UACPI_STATUS_OK;
}

uacpi_status uacpi_kernel_io_write16(uacpi_handle handle, uacpi_size offset, uacpi_u16 in_value){
    outw(((uint16_t)handle + offset), in_value);
    return UACPI_STATUS_OK;
}

uacpi_status uacpi_kernel_io_write32(uacpi_handle handle, uacpi_size offset, uacpi_u32 in_value){
    outb(((uint16_t)handle + offset), in_value);
    return UACPI_STATUS_OK;
}


uacpi_status uacpi_kernel_io_write(uacpi_handle handle, uacpi_size offset, uacpi_u8 byte_width, uacpi_u64 value){
    if(byte_width == 1){
        outb((uint16_t)offset, value);
    }else if(byte_width == 2){
        outw((uint16_t)offset, value);
    }else if(byte_width == 4){
        outl((uint16_t)offset, value);
    }else{
        return UACPI_STATUS_INTERNAL_ERROR;
    }

    return UACPI_STATUS_OK;
}

void *uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len){
    uint64_t offset = addr % PAGE_SIZE;
    kmap_pages((void*)addr, len, 0);
    return (void*)addr + get_kinfo()->hhdmoffset + offset;
}

void uacpi_kernel_unmap(void *addr, uacpi_size len){
    kunmap_pages(addr, len);
}

void *uacpi_kernel_alloc(uacpi_size size){
    void *ret = kmalloc(size);

    if(ret == NULL){
        klog(__func__, "Unable to kmalloc!");
    }

    return ret;

}

void uacpi_kernel_free(void *mem){

    if(mem == NULL){
        return;
    }

    kfree(mem);
}

uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot(void){
    return get_timestamp_us() * 1000;
}

void uacpi_kernel_stall(uacpi_u8 usec){
    sleep(usec / 1000);
}

void uacpi_kernel_sleep(uacpi_u64 msec){
    sleep(msec);
}

uacpi_handle uacpi_kernel_create_mutex(){
    return kmalloc(sizeof(atomic_flag));
}

void uacpi_kernel_free_mutex(uacpi_handle handle){
    free(handle);
    return;
}

uacpi_handle uacpi_kernel_create_event(void){
    return kmalloc(sizeof(uint64_t));
}

void uacpi_kernel_free_event(uacpi_handle handle){
    kfree(handle);
}

uacpi_thread_id uacpi_kernel_get_thread_id(void){
    return get_current_cpu_struct();
}

uacpi_status uacpi_kernel_acquire_mutex(uacpi_handle handle, uacpi_u16 t){
    atomic_flag *flg = handle;
    
}

