#include "mainmenu.h"
#include "../gfx/gfx.h"
#include "../gfx/gfxutils.h"
#include "../gfx/menu.h"
#include "tools.h"
#include "../hid/hid.h"
#include "../fs/menus/explorer.h"
#include <utils/btn.h>
#include <storage/nx_sd.h>
#include "tconf.h"
#include "../keys/keys.h"
#include "../storage/mountmanager.h"
#include "../storage/gptmenu.h"
#include "../storage/emummc.h"
#include <utils/util.h>
#include "../fs/fsutils.h"
#include <soc/fuse.h>
#include "../utils/utils.h"
#include "../config.h"
#include "../fs/readers/folderReader.h"
#include <string.h>
#include <mem/heap.h>
#include "../fs/menus/filemenu.h"

#define INCLUDE_BUILTIN_SCRIPTS 1
//#define SCRIPT_ONLY 1

#ifdef INCLUDE_BUILTIN_SCRIPTS
#include "../../build/TegraExplorer/script/builtin.h"

#define MAX_BUILTIN_MENU_ENTRIES (EMBEDDED_SCRIPTS_LEN * 2)

typedef struct {
    char name[64];
    int scriptIndices[EMBEDDED_SCRIPTS_LEN];
    int scriptCount;
} EmbeddedScriptFolder_t;

static void GetScriptDisplayName(const char *fullName, char *out, size_t outSize) {
    if (!out || outSize == 0) {
        return;
    }

    const char *start = strrchr(fullName, '/');
    start = (start) ? start + 1 : fullName;

    size_t len = 0;
    while (start[len] && len < outSize - 1) {
        len++;
    }

    if (len >= 3 && start[len - 3] == '.' && start[len - 2] == 't' && start[len - 1] == 'e') {
        len -= 3;
    }

    if (len >= outSize) {
        len = outSize - 1;
    }

    memcpy(out, start, len);
    out[len] = '\0';
}

static void RunEmbeddedScriptIndex(int index) {
    if (index < 0 || index >= EMBEDDED_SCRIPTS_LEN) {
        return;
    }

    RunScriptString((char*)embedded_scripts_g[index].script, (u32)strlen(embedded_scripts_g[index].script));
}

static void ShowEmbeddedFolder(const EmbeddedScriptFolder_t *folder) {
    if (!folder || folder->scriptCount <= 0) {
        return;
    }

    Vector_t ent = newVec(sizeof(MenuEntry_t), folder->scriptCount + 1);
    MenuEntry_t backEntry = {.optionUnion = COLORTORGB(COLOR_WHITE), .name = "<- Zurück"};
    vecAdd(&ent, backEntry);

    char displayNames[EMBEDDED_SCRIPTS_LEN][64] = {{0}};
    for (int i = 0; i < folder->scriptCount; i++) {
        int scriptIndex = folder->scriptIndices[i];
        if (scriptIndex < 0 || scriptIndex >= EMBEDDED_SCRIPTS_LEN) {
            continue;
        }

        GetScriptDisplayName(embedded_scripts_g[scriptIndex].name, displayNames[i], sizeof(displayNames[i]));
        MenuEntry_t entry = {.optionUnion = COLORTORGB(COLOR_BLUE), .name = displayNames[i], .icon = 128};
        vecAdd(&ent, entry);
    }

    int selected = (ent.count > 1) ? 1 : 0;
    while (1) {
        gfx_clearscreen();
        gfx_printf("%s\n\n", folder->name);
        int res = newMenu(&ent, selected, 79, 28, ALWAYSREDRAW | ENABLEPAGECOUNT, ent.count - 1);
        if (res <= 0) {
            break;
        }

        selected = res;
        int scriptIndex = folder->scriptIndices[res - 1];
        RunEmbeddedScriptIndex(scriptIndex);
    }

    free(ent.data);
}
#endif

extern hekate_config h_cfg;

enum {
    #ifndef SCRIPT_ONLY
    MainExit = 0,
    #else 
    MainExit = 0,
    #endif
    MainPowerOff,
    MainRebootRCM,
    MainRebootNormal,
    MainRebootHekate,
    MainRebootAMS,
    MainScripts,
};

MenuEntry_t mainMenuEntries[] = {
    #ifndef SCRIPT_ONLY
    [MainExit] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "-- Exit --"},
    #else 
    [MainExit] = {.optionUnion = COLORTORGB(COLOR_WHITE), .name = "-- Exit --"},
    #endif
    [MainPowerOff] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Power off"},
    [MainRebootRCM] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to RCM"},
    [MainRebootNormal] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot normally"},
    [MainRebootHekate] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to bootloader/update.bin"},
    [MainRebootAMS] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to atmosphere/reboot_payload.bin"},
    [MainScripts] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Scripts --"}
};

extern bool sd_mounted;
extern int launch_payload(char *path);

void RebootToAMS(){
    launch_payload("sd:/atmosphere/reboot_payload.bin");
}

void RebootToHekate(){
    launch_payload("sd:/bootloader/update.bin");
}

menuPaths mainMenuPaths[] = {
    #ifndef SCRIPT_ONLY
    [MainRebootAMS] = RebootToAMS,
    [MainRebootHekate] = RebootToHekate,
    #endif
    [MainRebootRCM] = reboot_rcm,
    [MainPowerOff] = power_off,
    [MainRebootNormal] = reboot_normal,
};

void EnterMainMenu(){
    int res = 0;
    while (1){
        if (sd_get_card_removed())
            sd_unmount();

        #ifndef SCRIPT_ONLY
        mainMenuEntries[MainRebootAMS].hide = (!sd_mounted || !FileExists("sd:/atmosphere/reboot_payload.bin"));
        mainMenuEntries[MainRebootHekate].hide = (!sd_mounted || !FileExists("sd:/bootloader/update.bin"));
        mainMenuEntries[MainRebootRCM].hide = h_cfg.t210b01;
        #endif
        // -- Scripts --
        #ifndef INCLUDE_BUILTIN_SCRIPTS
        mainMenuEntries[MainScripts].hide = (!sd_mounted || !FileExists("sd:/tegraexplorer/scripts"));
        #else
        mainMenuEntries[MainScripts].hide = ((!sd_mounted || !FileExists("sd:/tegraexplorer/scripts")) && !EMBEDDED_SCRIPTS_LEN);
        #endif

        Vector_t ent = newVec(sizeof(MenuEntry_t), ARRAY_SIZE(mainMenuEntries));
        ent.count = ARRAY_SIZE(mainMenuEntries);
        memcpy(ent.data, mainMenuEntries, sizeof(MenuEntry_t) * ARRAY_SIZE(mainMenuEntries));
        Vector_t scriptFiles = {0};
        u8 hasScripts = 0;

        #ifdef INCLUDE_BUILTIN_SCRIPTS
        for (int i = 0; i < EMBEDDED_SCRIPTS_LEN; i++){
            MenuEntry_t m = {.name = embedded_scripts_g[i].name, .optionUnion = COLORTORGB(COLOR_BLUE), .icon = 128};
            vecAdd(&ent, m);
        }
        #endif

        if (sd_mounted && FileExists("sd:/tegraexplorer/scripts")){
            scriptFiles = ReadFolder("sd:/tegraexplorer/scripts", &res);
            if (!res){
                if (!scriptFiles.count){
                    FREE(scriptFiles.data);
                    mainMenuEntries[MainScripts].hide = 1;
                }
                else {
                    hasScripts = 1;
                    vecForEach(FSEntry_t*, scriptFile, (&scriptFiles)){
                        if (!scriptFile->isDir && StrEndsWith(scriptFile->name, ".te")){
                            MenuEntry_t a = MakeMenuOutFSEntry(*scriptFile);
                            vecAdd(&ent, a);
                        }
                    }

                    if (ent.count == ARRAY_SIZE(mainMenuEntries)){
                        clearFileVector(&scriptFiles);
                        hasScripts = 0;
                        mainMenuEntries[MainScripts].hide = 1;
                    }
                }
            }
        }
        

        gfx_clearscreen();
        gfx_putc('\n');
        
        res = newMenu(&ent, res, 79, 30, (ent.count == ARRAY_SIZE(mainMenuEntries)) ? ALWAYSREDRAW : ALWAYSREDRAW | ENABLEPAGECOUNT, ent.count - ARRAY_SIZE(mainMenuEntries));
        if (res < MainScripts && mainMenuPaths[res] != NULL)
            mainMenuPaths[res]();
        #ifndef INCLUDE_BUILTIN_SCRIPTS
        else if (hasScripts){
        #else
        else {
            if (res - ARRAY_SIZE(mainMenuEntries) < EMBEDDED_SCRIPTS_LEN){
                char *script = embedded_scripts_g[res - ARRAY_SIZE(mainMenuEntries)].script;
                RunScriptString(script, strlen(script));
            }
            else {
            #endif
                vecDefArray(MenuEntry_t*, entArray, ent);
                MenuEntry_t entry = entArray[res];
                FSEntry_t fsEntry = {.name = entry.name, .sizeUnion = entry.sizeUnion};
                RunScript("sd:/tegraexplorer/scripts", fsEntry);
            #ifdef INCLUDE_BUILTIN_SCRIPTS
            }
            #endif
        }

        if (hasScripts){
            clearFileVector(&scriptFiles);
        }

        free(ent.data);
    }
}

