#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "menu.h"
#include "launcher.h"
#include "../kat5200.h"
#include "../interface/katconfig.h"
#include "../interface/util.h"
#include "../interface/states.h"
#include "modal.h"
#include "message.h"
#include "filebrowser.h"
#include "guitop.h"
#include "gui.h"
#include "theme.h"

extern struct theme g_theme;
extern struct option_widget g_options[MAX_OPTIONS];

void gui_request_exit(struct option_widget *, void *data);

struct option_widget *menu_find_item(unsigned long long mask)
{
    for (int i = 0; i < MAX_OPTIONS; i++) {
        if(g_options[i].mask_value == mask)
            return &g_options[i];
    }

    return NULL;
}

struct option_widget *menu_find_item_by_label(unsigned long long mask, char *label)
{
    for (int i = 0; i < MAX_OPTIONS; i++) {
        if(g_options[i].mask_value == mask && strstr(g_options[i].label, label))
            return &g_options[i];
    }

    return NULL;
}

/* Get config pointer from kat5200's config system */
static t_config *get_config(void) {
    return config_get_global_cfg();
}

void main_load_rom_result(struct modal_config *cfg)
{
    if (!cfg->result)
        return;
    util_alloc_string(&get_config()->general.rom, filebrowser_result());
    get_config()->general.rom_mapping = filebrowser_get_mapping();
    gui_launch(1);
}

void main_load_rom_from_file(struct option_widget *, void *data)
{
    struct modal_config *cfg = data;
    char *start_dir;

    memset(cfg, 0, sizeof(struct modal_config));
    start_dir = config_alloc_rom_browser_start_dir();
    cfg->data = start_dir;
    cfg->modal_type = MODAL_FB_LOAD_ROM;
    cfg->modal_active = 2;
    cfg->msg_type = 0;
    strcpy(cfg->title, "Load ROM");
    cfg->post_show = main_load_rom_result;

    filebrowser_init(cfg);
    cfg->data = NULL;
    free(start_dir);
}

/* Reset the running system in place.  Re-runs the full launch path
 * (reopen ROM -> restores default cart bank, reload OS, console_reset) so
 * bankswitching and all chips return to power-on state, then stays on the
 * menu with a confirmation toast.  Load failures (no ROM/BIOS configured or
 * a bad file) surface through the same modal error dialog as a normal launch. */
static void main_reset_task(struct option_widget *wgt, void *data)
{
    (void)wgt;
    (void)data;

    int status = kat5200_launch(KSTATUS_LAUNCH);

    if (!status) {
        guitop_show_toast_msg("System reset");
        return;
    }

    t_config *cfg = get_config();
    struct modal_config *modal = guitop_get_modal_cfg();

    memset(modal, 0, sizeof(struct modal_config));
    modal->modal_type = MODAL_MESSAGE;
    modal->modal_active = 1;
    modal->msg_type = KAT_MSG_TYPE_ERROR;
    strcpy(modal->title, "Error");
    strcpy(modal->cancel_btn_label, "OK");

    switch (status) {
        case ERR_ROM_LOAD:
            snprintf(modal->msg, sizeof(modal->msg),
                    "Failed to load ROM: %s", cfg->general.rom);
            break;
        case ERR_BASIC_LOAD:
            snprintf(modal->msg, sizeof(modal->msg),
                    "Failed to load BASIC: %s", cfg->general.basic);
            break;
        case ERR_BIOS_LOAD:
            snprintf(modal->msg, sizeof(modal->msg),
                    "Failed to load BIOS: %s", cfg->machine.bios);
            break;
        default:
            snprintf(modal->msg, sizeof(modal->msg), "Failed to reset system");
            break;
    }

    message_init(modal);
}

int menu_main(struct option_widget *wid_ary)
{
    for (int i = 0; i < 7; i++) {
        wid_ary[i].widget_type = WIDGET_TYPE_LABEL;
        wid_ary[i].data_type = WIDGET_DATA_TYPE_INT;
        wid_ary[i].menu_prev = 0;
        wid_ary[i].menu_next = 0;
        wid_ary[i].divider = 0;
        wid_ary[i].mask_value = 0;
        wid_ary[i].p_mask = NULL;
        wid_ary[i].p_source_mask = NULL;
        wid_ary[i].data = NULL;
        wid_ary[i].get_string = NULL;
        wid_ary[i].run_task = NULL;
    }

    strcpy(wid_ary[0].label, "Load ROM from file");
    strcpy(wid_ary[1].label, "Load ROM from Launcher");
    strcpy(wid_ary[2].label, "Options");
    strcpy(wid_ary[3].label, "Reset");
    strcpy(wid_ary[4].label, "Load State");
    strcpy(wid_ary[5].label, "Save State");
    strcpy(wid_ary[6].label, "Exit");

    wid_ary[0].data = guitop_get_modal_cfg();
    wid_ary[0].run_task = main_load_rom_from_file;

    wid_ary[3].run_task = main_reset_task;

    wid_ary[1].menu_next = KAT_MENU_LAUNCHER;
    wid_ary[2].menu_next = KAT_MENU_OPTIONS_MACHINE;
    wid_ary[4].menu_next = KAT_MENU_LOAD_STATE;
    wid_ary[5].menu_next = KAT_MENU_SAVE_STATE;

    wid_ary[6].run_task = gui_request_exit;
    return 7;
}

static int state_slot_nums[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

static void load_state_quick_task(struct option_widget *wgt, void *data)
{
    (void)wgt;
    get_config()->general.quick_state = *(int *)data;
    gui_launch(1);
}

static void load_state_from_file_result(struct modal_config *cfg)
{
    if (!cfg->result)
        return;
    util_alloc_string(&get_config()->general.state, filebrowser_result());
    gui_launch(1);
}

static void load_state_from_file_task(struct option_widget *wgt, void *data)
{
    (void)wgt;
    (void)data;
    struct modal_config *cfg = guitop_get_modal_cfg();

    memset(cfg, 0, sizeof(struct modal_config));
    cfg->modal_type = MODAL_FB_LOAD;
    cfg->modal_active = 2;
    strcpy(cfg->title, "Load State");
    cfg->post_show = load_state_from_file_result;

    filebrowser_init(cfg);
}

int menu_load_state(struct option_widget *wid_ary)
{
    int exists[9] = {0};
    states_get_quick_state_existance(exists);

    int num_items = 0;

    for (int i = 0; i < 10; i++) {
        wid_ary[i].widget_type = WIDGET_TYPE_LABEL;
        wid_ary[i].data_type = WIDGET_DATA_TYPE_INT;
        wid_ary[i].menu_prev = KAT_MENU_MAIN;
        wid_ary[i].menu_next = 0;
        wid_ary[i].divider = 0;
        wid_ary[i].mask_value = 0;
        wid_ary[i].p_mask = NULL;
        wid_ary[i].p_source_mask = NULL;
        wid_ary[i].data = NULL;
        wid_ary[i].get_string = NULL;
        wid_ary[i].run_task = NULL;
    }

    strcpy(wid_ary[num_items].label, "From File");
    wid_ary[num_items].run_task = load_state_from_file_task;
    num_items++;

    for (int i = 0; i < 9; i++) {
        if (!exists[i])
            continue;
        sprintf(wid_ary[num_items].label, "Quick State %d", i + 1);
        wid_ary[num_items].data = &state_slot_nums[i];
        wid_ary[num_items].run_task = load_state_quick_task;
        num_items++;
    }

    return num_items;
}

static void save_state_to_file_result(struct modal_config *cfg)
{
    if (!cfg->result)
        return;

    const char *path = filebrowser_result();
    struct modal_config *msg = guitop_get_modal_cfg();

    memset(msg, 0, sizeof(struct modal_config));
    msg->modal_type = MODAL_MESSAGE;
    msg->modal_active = 1;
    strcpy(msg->title, "Save State");
    strcpy(msg->cancel_btn_label, "OK");

    int status = states_save(path, console_get_ptr());
    if (status)
        snprintf(msg->msg, sizeof(msg->msg), "Failed to save state");
    else
        snprintf(msg->msg, sizeof(msg->msg), "State saved");

    message_init(msg);
}

static void save_state_to_file_task(struct option_widget *wgt, void *data)
{
    (void)wgt;
    (void)data;
    struct modal_config *cfg = guitop_get_modal_cfg();

    memset(cfg, 0, sizeof(struct modal_config));
    cfg->modal_type = MODAL_FB_SAVE;
    cfg->modal_active = 2;
    strcpy(cfg->title, "Save State");
    strcpy(cfg->accept_btn_label, "Save");
    strcpy(cfg->cancel_btn_label, "Cancel");
    cfg->post_show = save_state_to_file_result;

    filebrowser_init(cfg);
}

static void save_state_quick_task(struct option_widget *wgt, void *data)
{
    (void)wgt;
    int slot = *(int *)data;
    struct modal_config *cfg = guitop_get_modal_cfg();

    memset(cfg, 0, sizeof(struct modal_config));
    cfg->modal_type = MODAL_MESSAGE;
    cfg->modal_active = 1;
    strcpy(cfg->title, "Save State");
    strcpy(cfg->cancel_btn_label, "OK");

    int status = states_save_quick_state(slot, console_get_ptr());
    if (status)
        snprintf(cfg->msg, sizeof(cfg->msg), "Failed to save Quick State %d", slot);
    else
        snprintf(cfg->msg, sizeof(cfg->msg), "Quick State %d saved", slot);

    message_init(cfg);
}

int menu_save_state(struct option_widget *wid_ary)
{
    for (int i = 0; i < 10; i++) {
        wid_ary[i].widget_type = WIDGET_TYPE_LABEL;
        wid_ary[i].data_type = WIDGET_DATA_TYPE_INT;
        wid_ary[i].menu_prev = KAT_MENU_MAIN;
        wid_ary[i].menu_next = 0;
        wid_ary[i].divider = 0;
        wid_ary[i].mask_value = 0;
        wid_ary[i].p_mask = NULL;
        wid_ary[i].p_source_mask = NULL;
        wid_ary[i].data = NULL;
        wid_ary[i].get_string = NULL;
        wid_ary[i].run_task = NULL;
    }

    strcpy(wid_ary[0].label, "To File");
    wid_ary[0].run_task = save_state_to_file_task;

    for (int i = 1; i <= 9; i++) {
        sprintf(wid_ary[i].label, "Quick State %d", i);
        wid_ary[i].data = &state_slot_nums[i - 1];
        wid_ary[i].run_task = save_state_quick_task;
    }

    return 10;
}

int menu_set(struct nk_context *ctx, struct menu *p_menu, struct option_widget *wid_ary)
{
    int num_items = 0;

    p_menu->alignment = KM_ALIGN_V_UP | KM_ALIGN_H_LEFT;
    p_menu->allow_scroll = 1;
    *p_menu->title = '\0';
    ctx->style.font = &(g_theme.font_normal->handle);

    switch (p_menu->id) {
        case KAT_MENU_MAIN:
            p_menu->alignment = KM_ALIGN_V_BOTTOM | KM_ALIGN_H_LEFT;
            p_menu->allow_scroll = 0;
            ctx->style.font = &(g_theme.font_big->handle);
            num_items =  menu_main(wid_ary);
            break;
        case KAT_MENU_OPTIONS_MACHINE:
            num_items =  menu_options_machine(wid_ary);
            break;
        case KAT_MENU_OPTIONS_MACHINE_POKEY_POTS:
            strcpy(p_menu->title, "Pokey POT Values");
            num_items = menu_options_machine_pokey_pots(wid_ary);
            break;
        case KAT_MENU_OPTIONS_VIDEO:
            num_items =  menu_options_video(wid_ary);
            break;
        case KAT_MENU_OPTIONS_VIDEO_NTSC:
            strcpy(p_menu->title, "NTSC Artifact Setup");
            num_items =  menu_options_video_ntsc(wid_ary);
            break;
        case KAT_MENU_OPTIONS_VIDEO_SHADER:
            strcpy(p_menu->title, "Shader Setup");
            num_items =  menu_options_video_shader(wid_ary);
            break;
        case KAT_MENU_OPTIONS_SOUND:
            num_items =  menu_options_sound(wid_ary);
            break;
        case KAT_MENU_OPTIONS_MISC:
            num_items =  menu_options_misc(wid_ary);
            break;
        case KAT_MENU_OPTIONS_SETUP:
            strcpy(p_menu->title, "Setup");
            num_items =  menu_options_setup(wid_ary);
            break;
        case KAT_MENU_OPTIONS_CONFIG:
            strcpy(p_menu->title, "Configuration");
            num_items =  menu_options_config(wid_ary);
            break;
        case KAT_MENU_OPTIONS_PALETTE:
            strcpy(p_menu->title, "Palette");
            num_items = menu_options_palette(wid_ary);
            break;
        case KAT_MENU_OPTIONS_LOG:
            strcpy(p_menu->title, "Log");
            num_items = menu_options_log(wid_ary);
            break;
        case KAT_MENU_OPTIONS_INPUT:
            num_items = menu_options_input(wid_ary);
            break;
        case KAT_MENU_OPTIONS_INPUT_P1:
            strcpy(p_menu->title, "Player 1 Input");
            num_items = menu_options_input_player(wid_ary, 0);
            break;
        case KAT_MENU_OPTIONS_INPUT_P2:
            strcpy(p_menu->title, "Player 2 Input");
            num_items = menu_options_input_player(wid_ary, 1);
            break;
        case KAT_MENU_OPTIONS_INPUT_P3:
            strcpy(p_menu->title, "Player 3 Input");
            num_items = menu_options_input_player(wid_ary, 2);
            break;
        case KAT_MENU_OPTIONS_INPUT_P4:
            strcpy(p_menu->title, "Player 4 Input");
            num_items = menu_options_input_player(wid_ary, 3);
            break;
        case KAT_MENU_OPTIONS_INPUT_UI:
            strcpy(p_menu->title, "UI Input");
            num_items = menu_options_input_ui(wid_ary);
            break;
        case KAT_MENU_OPTIONS_INPUT_A800_KEYS:
            strcpy(p_menu->title, "A800 Keys");
            num_items = menu_options_input_a800_keys(wid_ary);
            break;
        case KAT_MENU_OPTIONS_MEDIA_CREATE:
            strcpy(p_menu->title, "Create Media");
            num_items = menu_options_media_create(wid_ary);
            break;
        case KAT_MENU_OPTIONS_MEDIA_DISK:
            strcpy(p_menu->title, "Create Disk");
            num_items = menu_options_media_disk(wid_ary);
            break;
        case KAT_MENU_OPTIONS_MEDIA_CASSETTE:
            strcpy(p_menu->title, "Create Cassette");
            num_items = menu_options_media_cassette(wid_ary);
            break;
        case KAT_MENU_INPUT_ACTION:
            //printf("menu_set: KAT_MENU_INPUT_ACTION case hit\n");
            strncpy(p_menu->title, menu_input_action_get_title(), sizeof(p_menu->title) - 1);
            p_menu->title[sizeof(p_menu->title) - 1] = '\0';
            num_items = menu_input_action(wid_ary);
            //printf("menu_set: num_items=%d title='%s'\n", num_items, p_menu->title);
            break;
        case KAT_MENU_OPTIONS_INPUT_JOY_SELECT:
            strcpy(p_menu->title, "Select Joystick");
            num_items = menu_options_input_joy_select(wid_ary);
            break;
        case KAT_MENU_LOAD_STATE:
            strcpy(p_menu->title, "Load State");
            num_items = menu_load_state(wid_ary);
            break;
        case KAT_MENU_SAVE_STATE:
            strcpy(p_menu->title, "Save State");
            num_items = menu_save_state(wid_ary);
            break;
        case KAT_MENU_LAUNCHER:
            strcpy(p_menu->title, "");
            num_items = launcher_build_menu(wid_ary);
            break;
        default:
            p_menu->alignment = KM_ALIGN_V_BOTTOM | KM_ALIGN_H_LEFT;
            num_items =  menu_main(wid_ary);
            break;
    }

    theme_update_dimensions(ctx);

    return num_items;
}
