#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include "boot/apios.h"
#include "boot/typedef.h"
#include "boot/answer_code.h"
#include "boot/entry.h"

apios_main_table_t *apios_table = NULL;

int apios_dump() {
    if (apios_table == NULL) {
        printf("\033[31mError: APIOS table is NULL (not initialized)\033[0m\n");
        return APIOS_ERR_NOT_INITIALIZED;
    }

    printf("\n--- \033[34mAPIOS System Dump\033[0m ---\n");
    printf("Main Table Address: %p\n", (void*)apios_table);
    printf("Magic: 0x%04X\n", apios_table->magic);
    printf("Total Drivers: %d\n", apios_table->drivers_count);
    printf("---------------------------\n");

    for (int i = 0; i < apios_table->drivers_count; i++) {
        driver_header_t *header = apios_table->drivers[i];
        
        if (header == NULL) {
            printf(" [%d] Header is NULL!\n", i);
            continue;
        }
        printf(" [%d] Driver Name: \033[32m%s\033[0m\n", i, header->driver_name);
        printf("     Header Addr: %p\n", (void*)header);
        driver_api_t *api = header->api;
        if (api == NULL) {
            printf("     \033[31m! API Table is NULL\033[0m\n");
            continue;
        }
        printf("     API Addr: %p (v%d)\n", (void*)api, api->driver_version);
        printf("     Functions Count: %d\n", api->fn_count);
        if (api->functions != NULL) {
            printf("     Functions List:\n");
            for (int f = 0; f < api->fn_count; f++) {
                printf("      - [%d] %-15s -> Addr: %p\n", 
                        f, 
                        api->functions[f].name, 
                        (void*)api->functions[f].driver_fn);
            }
        } else {
            printf("     \033[31m! No functions array found\033[0m\n");
        }
        printf("---------------------------\n");
    }
    return OS_OK;
}

int hendle_func(int_fn_t *func, char driver_name[16], char func_name[16]) {
    if (apios_table == NULL) return APIOS_ERR_NOT_INITIALIZED;
    bool drv_found = false;
    for (int i = 0; i < apios_table->drivers_count; i++) {
        if (strcmp(apios_table->drivers[i]->driver_name, driver_name) == 0) {
            drv_found = true;
            driver_api_t *api = apios_table->drivers[i]->api;
            bool fn_found = false;
            for (int f = 0; f < api->fn_count; f++) {
                if (strcmp(api->functions[f].name, func_name) == 0) {
                    *func = api->functions[f].driver_fn;
                    fn_found = true;
                    return OS_OK;
                }
            }
            if (!fn_found) return APIOS_ERR_DRIVER_CRASH;
        }
    }
    if (!drv_found) return APIOS_ERR_DRIVER_NOT_FOUND;
    return OS_ERR_NOT_FOUND;
}

int init_apios(apios_main_table_t *apios_table_) {
    if (apios_table == NULL) {
        if (load_log) printf("APIOS initings...\n");
        apios_table = (apios_main_table_t *)malloc(sizeof(apios_main_table_t));
        if (apios_table == NULL){
            if (load_log) printf("Failed to initialize table\n");
            return OS_ERR_NO_MEMORY;
        }
        static driver_fn_t fs_function[] = {
            {
                .name = "dump",
                .driver_fn = apios_dump,
            }
        };
        driver_api_t *fs_api = (driver_api_t *)malloc(sizeof(driver_api_t));
        fs_api->driver_version = 1;
        fs_api->fn_count = (int)(sizeof(fs_function)/sizeof(fs_function[0]));
        fs_api->functions = fs_function;
        driver_header_t *driver_header = (driver_header_t *)malloc(sizeof(driver_header_t));
        strcpy(driver_header->driver_name, "apios");
        driver_header->api = fs_api;
        apios_table->magic = 0xfa0c;
        apios_table->drivers = (driver_header_t **)malloc(sizeof(driver_header_t *) * 100);
        apios_table->drivers_count = 0;
        apios_table->drivers[apios_table->drivers_count] = driver_header;
        apios_table->drivers_count++;
    }
    else {
        if (load_log) printf("The table does not require initialization\n");
        return OS_WRN_NOT_NEED_INIT;
    }
    return OS_OK;
}