#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef __ANDROID__
#include <SDL3/SDL.h>
#endif
#include "menu.h"
#include "widget.h"
#include "../interface/katconfig.h"
#include "../interface/util.h"
#ifndef __ANDROID__
#include "../interface/logger.h"
#endif
#include "../interface/palette.h"
#include "filebrowser.h"
#include "modal.h"
#include "guitop.h"
#include "theme.h"
#include "message.h"
#include "debugger.h"
#include "../interface/sdl_if.h"
#include "../interface/atari_ntsc.h"

extern struct theme g_theme;
extern t_sdl_if g_pc_if;

static unsigned long long mask_misc = 0;
static char palette_import_file[KAT_PATH_MAX] = "";
static char palette_import_name[128] = "";
#define PALETTE_MANAGE_MAX 24
static char *palette_manage_names[PALETTE_MANAGE_MAX];
static int palette_manage_count = 0;
static int palette_manage_index = 0;
static char palette_delete_name[128] = "";

void debugger_activate(struct option_widget *wgt, void *data);

static char *misc_get_start_with_string(struct option_widget *wgt, char *label)
{
    switch (*(int*)(wgt->data)) {
        case START_GUI_ROMS: strcpy(label, "ROM Launcher"); break;
        case START_GUI_BROWSER: strcpy(label, "File Browser"); break;
        case START_GUI_LAST_ROM: strcpy(label, "Last ROM"); break;
        default: strcpy(label, "ROM Launcher"); break;
    }
    return label;
}

static char *misc_get_throttle_string(struct option_widget *wgt, char *label)
{
    int value = *(int *)(wgt->data);
    sprintf(label, "%d%%", value);
    return label;
}

static void misc_save_config(struct option_widget *wgt, void *data)
{
    (void)data;
    (void)wgt;
    config_save_general(config_get_global_table(), mask_misc);
    mask_misc = 0;
    guitop_show_saved_toast("Settings saved.");
}

#ifndef __ANDROID__
static void misc_manual_error(const char *ui_msg, const char *log_detail)
{
    struct modal_config *cfg;
    char log_msg[KAT_PATH_MAX + 160];

    snprintf(log_msg, sizeof(log_msg), "Manual: %s",
            log_detail ? log_detail : ui_msg);
    logger_log_message(LOG_ERROR, log_msg, "");

    cfg = guitop_get_modal_cfg();
    if (!cfg)
        return;

    message_init_modal(cfg, KAT_MSG_TYPE_ERROR, "Manual", ui_msg);
}

static void misc_open_manual(struct option_widget *wgt, void *data)
{
    char *path;
    char *uri;

    (void)wgt;
    (void)data;

    path = util_create_manual_path_alloc();
    if (!path) {
        misc_manual_error("Manual PDF was not found.",
                "Manual PDF was not found in packaged resources or developer output.");
        return;
    }

    uri = util_create_file_uri_alloc(path);
    if (!uri) {
        char detail[KAT_PATH_MAX + 96];
        snprintf(detail, sizeof(detail),
                "Unable to create file URI for manual path: %s", path);
        misc_manual_error("Unable to open the manual.", detail);
        free(path);
        return;
    }

    if (!SDL_OpenURL(uri)) {
        char detail[KAT_PATH_MAX + 160];
        snprintf(detail, sizeof(detail),
                "SDL_OpenURL failed for %s: %s", uri, SDL_GetError());
        misc_manual_error("The operating system could not open the manual.",
                detail);
    }

    free(uri);
    free(path);
}
#endif

static void palette_manage_refresh(const char *preferred_name)
{
    char wanted[128] = "";

    if (preferred_name) {
        snprintf(wanted, sizeof(wanted), "%s", preferred_name);
    } else if (palette_manage_index >= 0 &&
            palette_manage_index < palette_manage_count &&
            palette_manage_names[palette_manage_index]) {
        snprintf(wanted, sizeof(wanted), "%s",
                 palette_manage_names[palette_manage_index]);
    }

    palette_manage_count = config_get_palette_names(config_get_global_table(),
            palette_manage_names, PALETTE_MANAGE_MAX);

    if (palette_manage_count <= 0) {
        palette_manage_index = 0;
        return;
    }

    if (palette_manage_index < 0)
        palette_manage_index = 0;
    if (palette_manage_index >= palette_manage_count)
        palette_manage_index = palette_manage_count - 1;

    if (wanted[0]) {
        for (int i = 0; i < palette_manage_count; i++) {
            if (palette_manage_names[i] &&
                    !strcmp(palette_manage_names[i], wanted)) {
                palette_manage_index = i;
                break;
            }
        }
    }
}

static char *palette_import_get_file_string(struct option_widget *wgt, char *buf)
{
    (void)wgt;
    snprintf(buf, WIDGET_VALUE_STR_SIZE, "%s",
             palette_import_file[0] ? palette_import_file : "(not set)");
    return buf;
}

static char *palette_import_get_name_string(struct option_widget *wgt, char *buf)
{
    (void)wgt;
    snprintf(buf, WIDGET_VALUE_STR_SIZE, "%s",
             palette_import_name[0] ? palette_import_name : "(not set)");
    return buf;
}

static char *palette_manage_get_string(struct option_widget *wgt, char *buf)
{
    int idx = *(int *)(wgt->data);

    if (idx >= 0 && idx < palette_manage_count && palette_manage_names[idx])
        snprintf(buf, WIDGET_VALUE_STR_SIZE, "%s", palette_manage_names[idx]);
    else
        snprintf(buf, WIDGET_VALUE_STR_SIZE, "%s", "(none)");

    return buf;
}

static const char *palette_import_basename(const char *path)
{
    const char *slash;
    const char *backslash;

    if (!path)
        return "";

    slash = strrchr(path, '/');
    backslash = strrchr(path, '\\');
    if (slash && backslash)
        return slash > backslash ? slash + 1 : backslash + 1;
    if (slash)
        return slash + 1;
    if (backslash)
        return backslash + 1;
    return path;
}

static void palette_import_default_name_from_file(const char *path)
{
    const char *base;
    char tmp[sizeof(palette_import_name)];
    char *dot;

    if (palette_import_name[0])
        return;

    base = palette_import_basename(path);
    snprintf(tmp, sizeof(tmp), "%s", base);
    dot = strrchr(tmp, '.');
    if (dot && dot != tmp)
        *dot = '\0';

    snprintf(palette_import_name, sizeof(palette_import_name), "%s", tmp);
}

static void palette_import_file_result(struct modal_config *cfg)
{
    const char *path;

    if (!cfg->result)
        return;

    path = filebrowser_result();
    if (!path || !strlen(path))
        return;

    snprintf(palette_import_file, sizeof(palette_import_file), "%s", path);
    palette_import_default_name_from_file(palette_import_file);
}

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

    snprintf(palette_import_name, sizeof(palette_import_name), "%s",
             cfg->edit_buf);
}

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

    (void)wgt;
    (void)data;

    memset(cfg, 0, sizeof(struct modal_config));
    cfg->modal_type = MODAL_FB_LOAD;
    cfg->modal_active = 2;
    cfg->data = palette_import_file[0] ? palette_import_file : NULL;
    strcpy(cfg->title, "Select Palette File");
    cfg->post_show = palette_import_file_result;
    filebrowser_init(cfg);
}

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

    (void)wgt;
    (void)data;

    memset(cfg, 0, sizeof(struct modal_config));
    cfg->modal_type = MODAL_TEXT_INPUT;
    cfg->modal_active = 1;
    strcpy(cfg->title, "Edit Palette Name");
    strcpy(cfg->cancel_btn_label, "Cancel");
    strcpy(cfg->accept_btn_label, "OK");
    snprintf(cfg->msg, sizeof(cfg->msg), "Enter palette name:");
    snprintf(cfg->edit_buf, sizeof(cfg->edit_buf), "%s", palette_import_name);
    cfg->edit_len = (int)strlen(cfg->edit_buf);
    cfg->post_show = palette_import_name_result;
    message_init(cfg);
}

static void palette_import_run(struct option_widget *wgt, void *data)
{
    int result;

    (void)wgt;
    (void)data;

    if (!palette_import_file[0]) {
        guitop_show_toast_msg("Choose a palette file first.");
        return;
    }

    if (!palette_import_name[0] || util_all_whitespace(palette_import_name)) {
        guitop_show_toast_msg("Enter a palette name.");
        return;
    }

    result = convert_palette(palette_import_file, palette_import_name);
    if (result == PALETTE_IMPORT_OK) {
        palette_manage_refresh(palette_import_name);
        palette_import_file[0] = '\0';
        palette_import_name[0] = '\0';
        guitop_show_toast_msg("Palette imported.");
    } else if (result == PALETTE_IMPORT_ERR_EXISTS) {
        guitop_show_toast_msg("Palette name already exists.");
    } else {
        guitop_show_toast_msg("Could not read palette file.");
    }
}

static int palette_manage_selected_name(char *buf, size_t buf_size)
{
    if (!buf || !buf_size)
        return -1;

    buf[0] = '\0';
    if (palette_manage_index < 0 ||
            palette_manage_index >= palette_manage_count ||
            !palette_manage_names[palette_manage_index])
        return -1;

    snprintf(buf, buf_size, "%s", palette_manage_names[palette_manage_index]);
    return buf[0] ? 0 : -1;
}

static void palette_refresh_live_video(void)
{
    t_config *cfg = config_get_global_cfg();

    pc_set_palette(&cfg->video, g_pc_if.color_palette, g_pc_if.color_rgb);
    if (cfg->video.ntsc_artifacts && g_pc_if.ntsc_emu)
        atari_ntsc_init(g_pc_if.ntsc_emu, &cfg->video.ntsc_setup);
    pc_refresh_video_output();
}

static void palette_delete_confirmed(struct modal_config *cfg)
{
    int active_changed;
    char preferred[128] = "";

    if (!cfg->result)
        return;

    if (!palette_delete_name[0])
        return;

    if (config_delete_palette(config_get_global_table(), palette_delete_name) != 0) {
        palette_manage_refresh(NULL);
        guitop_show_toast_msg("Palette delete failed.");
        return;
    }

    active_changed = config_fallback_active_palette_after_delete(
            config_get_global_table(), palette_delete_name);
    if (active_changed) {
        t_config *gcfg = config_get_global_cfg();
        if (gcfg->video.palette_name && gcfg->video.palette_name[0])
            snprintf(preferred, sizeof(preferred), "%s", gcfg->video.palette_name);
        palette_refresh_live_video();
    }

    palette_manage_refresh(preferred[0] ? preferred : NULL);
    guitop_show_toast_msg("Palette deleted.");
}

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

    (void)wgt;
    (void)data;

    palette_manage_refresh(NULL);
    if (palette_manage_selected_name(palette_delete_name,
                sizeof(palette_delete_name)) != 0) {
        guitop_show_toast_msg("No palette selected.");
        return;
    }

    memset(cfg, 0, sizeof(struct modal_config));
    cfg->modal_type = MODAL_MESSAGE_CHOOSE;
    cfg->modal_active = 1;
    strcpy(cfg->title, "Delete Palette");
    snprintf(cfg->msg, sizeof(cfg->msg),
             "Delete palette \"%s\"? This cannot be undone.",
             palette_delete_name);
    strcpy(cfg->accept_btn_label, "Delete");
    strcpy(cfg->cancel_btn_label, "Cancel");
    cfg->post_show = palette_delete_confirmed;
    message_init(cfg);
}

int menu_options_misc(struct option_widget *wid_ary)
{
    t_config *cfg = config_get_global_cfg();
    int i;

    for (i = 0; i < MAX_OPTIONS; 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;
        wid_ary[i].accept_input = 1;
    }

    i = -1;

    /* Setup */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Setup");
    wid_ary[i].menu_next = KAT_MENU_OPTIONS_SETUP;

    /* Configuration */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Configuration");
    wid_ary[i].menu_next = KAT_MENU_OPTIONS_CONFIG;

    /* Palette */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Palette");
    wid_ary[i].menu_next = KAT_MENU_OPTIONS_PALETTE;

    /* Debugger */
    if (pc_debugger_available()) {
        wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
        strcpy(wid_ary[i].label, "Debugger");
        wid_ary[i].run_task = debugger_activate;
    }

    /* Create Media */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Create Media");
    wid_ary[i].menu_next = KAT_MENU_OPTIONS_MEDIA_CREATE;

    /* Log */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Log");
    wid_ary[i].run_task = menu_log_open;
    wid_ary[i].menu_next = KAT_MENU_OPTIONS_LOG;

#ifndef __ANDROID__
    /* Manual */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Manual");
    wid_ary[i].run_task = misc_open_manual;
#endif

    /* Start With - divider before this item */
    wid_ary[++i].widget_type = WIDGET_TYPE_SELECT;
    strcpy(wid_ary[i].label, "Start With");
    wid_ary[i].min = START_GUI_ROMS;
    wid_ary[i].max = START_GUI_LAST_ROM;
    wid_ary[i].data = &cfg->general.start_with;
    wid_ary[i].p_mask = &mask_misc;
    wid_ary[i].p_source_mask = &cfg->general_mask;
    wid_ary[i].mask_value = KCFG_GENERAL_START_WT;
    wid_ary[i].get_string = misc_get_start_with_string;
    wid_ary[i].divider = 1;

    /* Throttle */
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Throttle");
    wid_ary[i].min = 0;
    wid_ary[i].max = 200;
    wid_ary[i].data = &cfg->general.throttle;
    wid_ary[i].p_mask = &mask_misc;
    wid_ary[i].p_source_mask = &cfg->general_mask;
    wid_ary[i].mask_value = KCFG_GENERAL_THROTTLE;
    wid_ary[i].get_string = misc_get_throttle_string;

    /* Save - divider before this item */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "  Save");
    wid_ary[i].data = NULL;
    wid_ary[i].p_mask = NULL;
    wid_ary[i].run_task = misc_save_config;
    wid_ary[i].divider = 1;

    return i + 1;
}

int menu_options_palette(struct option_widget *wid_ary)
{
    int i;

    palette_manage_refresh(NULL);

    for (i = 0; i < MAX_OPTIONS; 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_OPTIONS_MISC;
        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;
        wid_ary[i].accept_input = 1;
    }

    i = -1;

    /* File */
    wid_ary[++i].widget_type = WIDGET_TYPE_STRING;
    strcpy(wid_ary[i].label, "File");
    wid_ary[i].data = palette_import_file;
    wid_ary[i].get_string = palette_import_get_file_string;
    wid_ary[i].run_task = palette_import_choose_file;

    /* Name */
    wid_ary[++i].widget_type = WIDGET_TYPE_STRING;
    strcpy(wid_ary[i].label, "Name");
    wid_ary[i].data = palette_import_name;
    wid_ary[i].get_string = palette_import_get_name_string;
    wid_ary[i].run_task = palette_import_edit_name;

    /* Import */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "\xef\x83\x87  Import");
    wid_ary[i].run_task = palette_import_run;

    /* Palette selector */
    wid_ary[++i].widget_type = WIDGET_TYPE_SELECT;
    strcpy(wid_ary[i].label, "Palette");
    wid_ary[i].min = 0;
    wid_ary[i].max = palette_manage_count > 0 ? palette_manage_count - 1 : 0;
    wid_ary[i].data = &palette_manage_index;
    wid_ary[i].p_mask = NULL;
    wid_ary[i].mask_value = 0;
    wid_ary[i].get_string = palette_manage_get_string;
    wid_ary[i].run_task = NULL;
    wid_ary[i].divider = 1;

    /* Delete */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "\xef\x87\xb8  Delete");
    wid_ary[i].run_task = palette_delete_run;

    return i + 1;
}

static char *config_get_dir_string(struct option_widget *wgt, char *buf)
{
    (void)wgt;
    strncpy(buf, util_get_program_path(), 127);
    buf[127] = '\0';
    return buf;
}

static void config_factory_reset_confirmed(struct modal_config *cfg)
{
    if (!cfg->result)
        return;
    if (util_reset_config(NULL) != 0)
        guitop_show_toast_msg("Factory reset failed. See log for details.");
    else if (!util_first_use_seed_pending())
        /* Conf restored and no profile copy was queued: report now. */
        guitop_show_toast_msg("Configuration reset. Restart to apply.");
    /* else: the profile copy is running behind the wait splash; the success
     * message is shown when the seed worker finishes (see gui.c). */
}

static void config_factory_reset(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_MESSAGE_CHOOSE;
    cfg->modal_active = 1;
    strcpy(cfg->title, "Factory Reset");
    strcpy(cfg->msg, "Reset configuration to defaults? This cannot be undone.");
    strcpy(cfg->accept_btn_label, "OK");
    strcpy(cfg->cancel_btn_label, "Cancel");
    cfg->post_show = config_factory_reset_confirmed;
    message_init(cfg);
}

int menu_options_config(struct option_widget *wid_ary)
{
    int i;

    for (i = 0; i < MAX_OPTIONS; 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_OPTIONS_MISC;
        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;
        wid_ary[i].accept_input = 1;
    }

    i = -1;

    /* Directory (read-only) */
    wid_ary[++i].widget_type = WIDGET_TYPE_STRING;
    strcpy(wid_ary[i].label, "Directory");
    wid_ary[i].get_string = config_get_dir_string;
    wid_ary[i].accept_input = 0;

    /* Factory Reset */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Factory Reset");
    wid_ary[i].run_task = config_factory_reset;

    return i + 1;
}
