#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "menu.h"
#include "widget.h"
#include "../interface/katconfig.h"
#include "../interface/sdl_if.h"
#include "modal.h"
#include "guitop.h"
#include "theme.h"

extern struct theme g_theme;

static unsigned long long mask_sound = 0;

/* Temporary variable for frequency selection index */
static int tmp_freq_idx;
static int tmp_buffer_auto = 1;
static int tmp_buffer_x2 = 4;

/* Frequency options in Hz */
static const int freq_options[] = { 11025, 22050, 44100, 48000, 96000 };
static const int freq_count = sizeof(freq_options) / sizeof(freq_options[0]);

static int sound_clamp_buffer_x2(int value)
{
    int min = (int)(KCFG_SND_BUFFER_MIN * 2.0f);
    int max = (int)(KCFG_SND_BUFFER_MAX * 2.0f);

    if (value < min)
        return min;
    if (value > max)
        return max;
    return value;
}

static int sound_buffer_to_x2(float buffer_size)
{
    int value = (int)(buffer_size * 2.0f + 0.5f);

    return sound_clamp_buffer_x2(value);
}

static void sound_sync_buffer_stage(t_config *cfg)
{
    if (mask_sound & KCFG_SND_BUFFER)
        return;

    tmp_buffer_auto = cfg->sound.buffer_size <= KCFG_SND_BUFFER_AUTO;
    tmp_buffer_x2 = tmp_buffer_auto
        ? sound_buffer_to_x2(pc_get_auto_sound_buffer_size())
        : sound_buffer_to_x2(cfg->sound.buffer_size);
}

static int sound_commit_buffer_stage(t_config *cfg)
{
    float old_buffer = cfg->sound.buffer_size;

    if (!(mask_sound & KCFG_SND_BUFFER))
        return 0;

    cfg->sound.buffer_size = tmp_buffer_auto
        ? KCFG_SND_BUFFER_AUTO
        : (float)sound_clamp_buffer_x2(tmp_buffer_x2) / 2.0f;
    return old_buffer != cfg->sound.buffer_size;
}

void sound_save_all_config(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();

    (void)data;
    (void)wgt;
    sound_commit_buffer_stage(cfg);
    config_save_sound(config_get_global_table(), "All", mask_sound);
    mask_sound = 0;
    guitop_show_saved_toast("Settings saved.");
}

void sound_save_rom_config(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();

    (void)data;
    (void)wgt;
    if (!config_ensure_rom_table()) {
        guitop_show_toast_msg("No ROM loaded.");
        return;
    }

    sound_commit_buffer_stage(cfg);
    config_save_sound(config_get_rom_table(), "this", mask_sound);
    mask_sound = 0;
    config_refresh_rom_source_masks();
    guitop_show_toast_msg("Saved for this ROM.");
}

static int get_freq_index(int frequency)
{
    for (int i = 0; i < freq_count; i++) {
        if (freq_options[i] == frequency)
            return i;
    }
    /* Default to 44100 Hz (index 2) if not found */
    return 2;
}

char * sound_get_freq_string(struct option_widget *wgt, char *label)
{
    int idx = *(int *)(wgt->data);
    if (idx >= 0 && idx < freq_count) {
        sprintf(label, "%d Hz", freq_options[idx]);
    } else {
        strcpy(label, "44100 Hz");
    }
    return label;
}

void sound_set_freq_value(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();
    int idx = *(int *)(data);

    if (idx >= 0 && idx < freq_count) {
        cfg->sound.frequency = freq_options[idx];
    }
}

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

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

char * sound_get_buffer_string(struct option_widget *wgt, char *label)
{
    int value = sound_clamp_buffer_x2(*(int *)(wgt->data));

    snprintf(label, WIDGET_VALUE_STR_SIZE, "%.1f", (float)value / 2.0f);
    return label;
}

void sound_set_gain_value(struct option_widget *wgt, void *data)
{
    (void)wgt;
    /* Push the new gain onto the open stream so it takes effect on return to play */
    pc_set_sound_gain(*(int *)(data));
}

void sound_buffer_auto_update(struct option_widget *wgt, void *data)
{
    (void)wgt;
    (void)data;

    if (!tmp_buffer_auto)
        tmp_buffer_x2 = sound_buffer_to_x2(pc_get_auto_sound_buffer_size());
    tmp_buffer_x2 = sound_clamp_buffer_x2(tmp_buffer_x2);
}

void sound_buffer_update(struct option_widget *wgt, void *data)
{
    (void)wgt;
    (void)data;

    tmp_buffer_x2 = sound_clamp_buffer_x2(tmp_buffer_x2);
}

int menu_options_sound(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 = &mask_sound;
        wid_ary[i].p_source_mask = &cfg->sound_mask;
        wid_ary[i].data = NULL;
        wid_ary[i].get_string = NULL;
        wid_ary[i].run_task = NULL;
    }

    i = -1;
    sound_sync_buffer_stage(cfg);

    /* Enable Sound */
    wid_ary[++i].widget_type = WIDGET_TYPE_TOGGLE;
    strcpy(wid_ary[i].label, "Enable Sound");
    wid_ary[i].data = &cfg->sound.enable;
    wid_ary[i].mask_value = KCFG_SND_EN;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = NULL;

    /* Frequency */
    tmp_freq_idx = get_freq_index(cfg->sound.frequency);
    wid_ary[++i].widget_type = WIDGET_TYPE_SELECT;
    strcpy(wid_ary[i].label, "Frequency");
    wid_ary[i].min = 0;
    wid_ary[i].max = freq_count - 1;
    wid_ary[i].data = &tmp_freq_idx;
    wid_ary[i].mask_value = KCFG_SND_FREQ;
    wid_ary[i].get_string = sound_get_freq_string;
    wid_ary[i].run_task = sound_set_freq_value;

    /* Samples */
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Samples");
    wid_ary[i].min = 256;
    wid_ary[i].max = 4096;
    wid_ary[i].data = &cfg->sound.samples;
    wid_ary[i].mask_value = KCFG_SND_SAMPLES;
    wid_ary[i].get_string = sound_get_samples_string;
    wid_ary[i].run_task = NULL;

    /* Buffer Size Auto */
    wid_ary[++i].widget_type = WIDGET_TYPE_TOGGLE;
    strcpy(wid_ary[i].label, "Auto Buffer Size");
    wid_ary[i].data = &tmp_buffer_auto;
    wid_ary[i].mask_value = KCFG_SND_BUFFER;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = sound_buffer_auto_update;

    if (!tmp_buffer_auto) {
        /* Buffer Size */
        wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
        strcpy(wid_ary[i].label, "Buffer Size");
        wid_ary[i].data = &tmp_buffer_x2;
        wid_ary[i].min = (int)(KCFG_SND_BUFFER_MIN * 2.0f);
        wid_ary[i].max = (int)(KCFG_SND_BUFFER_MAX * 2.0f);
        wid_ary[i].mask_value = KCFG_SND_BUFFER;
        wid_ary[i].get_string = sound_get_buffer_string;
        wid_ary[i].run_task = sound_buffer_update;
    }

    /* Gain (Volume) */
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Volume");
    wid_ary[i].min = 0;
    wid_ary[i].max = KCFG_SND_GAIN_MAX;
    wid_ary[i].data = &cfg->sound.gain;
    wid_ary[i].mask_value = KCFG_SND_GAIN;
    wid_ary[i].get_string = sound_get_gain_string;
    wid_ary[i].run_task = sound_set_gain_value;

    /* Save */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "  Save");
    wid_ary[i].data = NULL;
    wid_ary[i].divider = 1;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = sound_save_all_config;

    /* Save for ROM */
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "  Save for ROM only [*]");
    wid_ary[i].data = NULL;
    wid_ary[i].divider = 0;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = sound_save_rom_config;

    return i + 1;
}
