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

#include "FreeRTOS.h"

#include "boot/recovery/boot.h"
#include "boot/recovery/typedef.h"
#include "boot/recovery/table.h"
#include "boot/apios.h"



void recovery_shell(void *pvParameters) {
    vTaskDelay(pdMS_TO_TICKS(10));
    char buffer[64];
    char prm[8][16];
    int current_pos = 0;
    printf("\033[2J\033[H");
    printf("\033[32mrecovery:\033[33m> \033[0m");
    fflush(stdout);
    memset(buffer, 0, 64);
    while (1) {
        int in = getchar();

        if (in == EOF) {
            vTaskDelay(pdMS_TO_TICKS(10));
            continue;
        } 
        if (in == 0x08 || in == 0x7F) {
            if (current_pos > 0) {
                current_pos--;
                buffer[current_pos] = '\0';
                printf("\b \b"); 
                fflush(stdout);
            }
            continue;
        } 
        if (in == '\r' || in == '\n') {
            printf("\n");

            if (current_pos > 0) {
                memset(prm, 0, sizeof(prm));
                int count = 0;
                char *token = strtok(buffer, " ");
                while (token != NULL && count < 8) {
                    strncpy(prm[count], token, 15);
                    prm[count][15] = '\0';
                    count++;
                    token = strtok(NULL, " ");
                }
                if (count > 0) {
                    if (strcmp(prm[0], "load") == 0) {
                        char source[16] = "none";
                        char name_load[16] = "none";
                        for (int q = 1; q < count; q++) {
                            char *key = strtok(prm[q], "=");
                            char *val = strtok(NULL, "=");
                            
                            if (key && val) {
                                if (strcmp(key, "source") == 0) strncpy(source, val, 15);
                                else if (strcmp(key, "name") == 0) strncpy(name_load, val, 15);
                            }
                        }
                        printf("Action: Loading module [%s] from [%s]\n", name_load, source);
                        bool found = false;
                        int table_size = sizeof(moduls_table) / sizeof(moduls_table[0]);
                        for (int i = 0; i < table_size; i++) {
                            if (strcmp(moduls_table[i].module.name, name_load) == 0) {
                                printf("\033[32mFound!\033[0m Starting driver...\n");
                                int res = moduls_table[i].module.init_fn(apios_table);
                                printf("Driver exited with code: %d\n", res);
                                found = true;
                                break; 
                            }
                        }
                        if (!found && strcmp(name_load, "none") != 0) {
                            printf("\033[31mError:\033[0m Module '%s' not found\n", name_load);
                        }
                    } 
                    
                    else if (strcmp(prm[0], "init") == 0) {
                        char name_load[16] = "none";
                        for (int q = 1; q < count; q++) {
                            char *key = strtok(prm[q], "=");
                            char *val = strtok(NULL, "=");
                            
                            if (key && val) {
                                if (strcmp(key, "name") == 0) strncpy(name_load, val, 15);
                            }
                        }
                        int table_size = sizeof(service_init) / sizeof(service_init[0]);
                        bool found = false;
                        for (int i = 0; i < table_size; i++) {
                            if (strcmp(service_init[i].name, name_load) == 0) {
                                printf("\033[32mFound!\033[0m Starting service ...\n");
                                int res = service_init[i].init_fn(apios_table);
                                printf("Service exited with code: %d\n", res);
                                found = true;
                                break; 
                            }
                        }
                        if (!found) {
                            printf("\033[31mError:\033[0m Service '%s' not found \n", name_load);
                        }
                    }
                    
                    else if (strcmp(prm[0], "apios") == 0) {
                        char drv_name[16] = "none";
                        char fn_name[16] = "none";

                        for (int q = 1; q < count; q++) {
                            char *key = strtok(prm[q], "=");
                            char *val = strtok(NULL, "=");
                            if (key && val) {
                                if (strcmp(key, "driver") == 0) strncpy(drv_name, val, 15);
                                else if (strcmp(key, "func") == 0) strncpy(fn_name, val, 15);
                            }
                        }

                        if (apios_table == NULL) {
                            printf("\033[31mError:\033[0m APIOS table not initialized\n");
                        } else {
                            bool drv_found = false;
                            for (int i = 0; i < apios_table->drivers_count; i++) {
                                if (strcmp(apios_table->drivers[i]->driver_name, drv_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, fn_name) == 0) {
                                            printf("\033[32mExecuting:\033[0m %s->%s()...\n", drv_name, fn_name);
                                            int res = api->functions[f].driver_fn(NULL);
                                            
                                            printf("\n\033[32mDone.\033[0m Return code: %d\n", res);
                                            fn_found = true;
                                            break;
                                        }
                                    }
                                    if (!fn_found) printf("\033[31mError:\033[0m Function '%s' not found in driver '%s'\n", fn_name, drv_name);
                                    break;
                                }
                            }
                            if (!drv_found) printf("\033[31mError:\033[0m Driver '%s' not found\n", drv_name);
                        }
                    }


                    else if (strcmp(prm[0], "help") == 0) {
                        printf("Commands: load source=X name=Y\n");
                        printf("Commands: init name=Y, help\n");
                        printf("Commands: apios driver=Y, func\n");
                    }
                    else {
                        printf("Unknown command: %s\n", prm[0]);
                    }
                }
            }
            current_pos = 0;
            memset(buffer, 0, 64);
            printf("\033[32mrecovery:\033[33m> \033[0m");
            fflush(stdout);
        }
        else {
            if (current_pos < 63) {
                buffer[current_pos] = (char)in;
                current_pos++;
                buffer[current_pos] = '\0';
                putchar(in); 
                fflush(stdout);
            }
        }
    }
}
