/*********************************************************************/
/* menu.c - user menu for rockboy                                    */
/*                                                                   */
/* Note: this file only exposes one function: do_user_menu().        */
/*********************************************************************/

#include "button.h"
#include "rockmacros.h"
#include "mem.h"

#if (CONFIG_KEYPAD == IPOD_4G_PAD)
#define MENU_BUTTON_UP BUTTON_SCROLL_BACK
#define MENU_BUTTON_DOWN BUTTON_SCROLL_FWD
#define MENU_BUTTON_LEFT BUTTON_LEFT
#define MENU_BUTTON_RIGHT BUTTON_RIGHT
#else
#define MENU_BUTTON_UP BUTTON_UP
#define MENU_BUTTON_DOWN BUTTON_DOWN
#define MENU_BUTTON_LEFT BUTTON_LEFT
#define MENU_BUTTON_RIGHT BUTTON_RIGHT
#endif

/* load/save state function declarations */
static void do_opt_menu(void);
static void do_slot_menu(bool is_load);
static void munge_name(char *buf, size_t bufsiz);

/* directory ROM save slots belong in */
#define STATE_DIR "/.rockbox/rockboy"

int getbutton(char *text)
{
    rb->lcd_putsxy(0, 0, text);
    rb->lcd_update();
    rb->sleep(30);

    while (rb->button_get(false) != BUTTON_NONE) 
        rb->yield();

    int button;
    while(true)
    {
        button = rb->button_get(true);
        button=button&0x00000FFF;

        switch(button)
        {
            case MENU_BUTTON_LEFT:
            case MENU_BUTTON_RIGHT:
            case MENU_BUTTON_UP:
            case MENU_BUTTON_DOWN:
                break;
            default:
                return button;
                break;
        }
    }
}

void setupkeys(void)
{
    options.A=getbutton("Press A");

    options.B=getbutton("Press B");

    options.START=getbutton("Press Start");

    options.SELECT=getbutton("Press Select");

    options.MENU=getbutton("Press Menu");
}

/*
 * do_user_menu - create the user menu on the screen.
 *
 * Returns USER_MENU_QUIT if the user selected "quit", otherwise 
 * returns zero.
 */
int do_user_menu(void) {
    bool done=false;
    int m, ret=0;
    int result;

    /* Clean out the button Queue */
    while (rb->button_get(false) != BUTTON_NONE) 
        rb->yield();

    static const struct menu_item items[] = {
        {"Load Game", NULL },
        {"Save Game", NULL },
        {"Options", NULL },
        {"Quit", NULL },
    };

    pcm_init();

    m = rb->menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);

    while(!done)
    {
        result=rb->menu_show(m);

        switch (result)
        {
            case 0: /* Load Game */
                do_slot_menu(true);
                break;
            case 1: /* Save Game */
                do_slot_menu(false);
                break;
            case 2: /* Options */
                do_opt_menu();
                break;
            case 3: /* Quit */
                ret = USER_MENU_QUIT;
                done=true;
                break;
            default:
                done=true;
                break;
        }
    }

    rb->menu_exit(m);

    rb->lcd_setfont(0); /* Reset the font */
    rb->lcd_clear_display(); /* Clear display for screen size changes */

    return ret;
}

/*
 * munge_name - munge a string into a filesystem-safe name
 */
static void munge_name(char *buf, const size_t bufsiz) {
    unsigned int i, max;

    /* check strlen */
    max = strlen(buf);
    max = (max < bufsiz) ? max : bufsiz;
  
    /* iterate over characters and munge them (if necessary) */
    for (i = 0; i < max; i++)
        if (!isalnum(buf[i]))
            buf[i] = '_';
}

/*
 * build_slot_path - build a path to an slot state file for this rom
 *
 * Note: uses rom.name.  Is there a safer way of doing this?  Like a ROM
 * checksum or something like that?
 */
static void build_slot_path(char *buf, size_t bufsiz, size_t slot_id) {
    char name_buf[40];

    /* munge state file name */
    strncpy(name_buf, rom.name, 40);
    name_buf[16] = '\0';
    munge_name(name_buf, strlen(name_buf));

    /* glom the whole mess together */
    snprintf(buf, bufsiz, "%s/%s-%d.rbs", STATE_DIR, name_buf, slot_id + 1);
}

/*
 * do_file - load or save game data in the given file
 *
 * Returns true on success and false on failure.
 *
 * @desc is a brief user-provided description (<20 bytes) of the state.
 * If no description is provided, set @desc to NULL.
 *
 */
static bool do_file(char *path, char *desc, bool is_load) {
    char buf[200], desc_buf[20];
    int fd, file_mode;
    
    /* set file mode */
    file_mode = is_load ? O_RDONLY : (O_WRONLY | O_CREAT);
  
    /* attempt to open file descriptor here */
    if ((fd = open(path, file_mode)) <= 0)
        return false;

    /* load/save state */
    if (is_load)
    {
        /* load description */
        read(fd, desc_buf, 20);
    
        /* load state */
        loadstate(fd);
    
        /* print out a status message so the user knows the state loaded */
        snprintf(buf, 200, "Loaded state from \"%s\"", path);
        rb->splash(HZ * 1, true, buf);
    }
    else
    {
        /* build description buffer */
        memset(desc_buf, 0, 20);
        if (desc)
            strncpy(desc_buf, desc, 20);

        /* save state */
        write(fd, desc_buf, 20);
        savestate(fd);
    }
    
    /* close file descriptor */
    close(fd);

    /* return true (for success) */
    return true;
}

/*
 * do_slot - load or save game data in the given slot
 *
 * Returns true on success and false on failure.
 */
static bool do_slot(size_t slot_id, bool is_load) {
    char path_buf[256], desc_buf[20];
  
    /* build slot filename, clear desc buf */
    build_slot_path(path_buf, 256, slot_id);
    memset(desc_buf, 0, 20);

    /* if we're saving to a slot, then get a brief description */
    if (!is_load)
        if (rb->kbd_input(desc_buf, 20) || !strlen(desc_buf))
        {
            memset(desc_buf, 0, 20);
            strncpy(desc_buf, "Untitled", 20);
        }

    /* load/save file */
    return do_file(path_buf, desc_buf, is_load);
}

/* 
 * get information on the given slot
 */
static void slot_info(char *info_buf, size_t info_bufsiz, size_t slot_id) {
    char buf[256];
    int fd;

    /* get slot file path */
    build_slot_path(buf, 256, slot_id);

    /* attempt to open slot */
    if ((fd = open(buf, O_RDONLY)) >= 0)
    {
        /* this slot has a some data in it, read it */
        if (read(fd, buf, 20) > 0)
        {
            buf[20] = '\0';
            snprintf(info_buf, info_bufsiz, "%d. %s", slot_id + 1, buf);
        }
        else
            snprintf(info_buf, info_bufsiz, "%d. ERROR", slot_id + 1);

        close(fd);
    }
    else
    {
        /* if we couldn't open the file, then the slot is empty */
        snprintf(info_buf, info_bufsiz, "%d. %s", slot_id + 1, "<Empty>");
    }
}

/*
 * do_slot_menu - prompt the user for a load/save memory slot
 */
static void do_slot_menu(bool is_load) {
    bool done=false;

    char buf[5][20];

    int m;
    int result;
    int i;

    struct menu_item items[] = {
        { buf[0] , NULL },
        { buf[1] , NULL },
        { buf[2] , NULL },
        { buf[3] , NULL },
        { buf[4] , NULL },
    };

    int num_items = sizeof(items) / sizeof(*items);

    /* create menu items */
    for (i = 0; i < num_items; i++)
        slot_info(buf[i], 20, i);

    m = rb->menu_init(items, num_items, NULL, NULL, NULL, NULL);

    while(!done)
    {
        result=rb->menu_show(m);
    
        if (result<num_items && result >= 0 )
            done = do_slot(result, is_load);
        else
            done = true;
    }
    rb->menu_exit(m);
}

static void do_opt_menu(void)
{
    bool done=false;
    int m;
    int result;

    static const struct opt_items onoff[2] = {
        { "Off", -1 },
        { "On" , -1 },
    };

    static const struct opt_items fullscreen[]= {
        { "Off", -1 },
        { "Fullscreen", -1 },
        { "Full - Maintain Ratio", -1 },
    };

    static const struct opt_items frameskip[]= {
        { "3 Max", -1 },
        { "4 Max", -1 },
        { "5 Max", -1 },
        { "6 Max", -1 },
    };

    static const struct menu_item items[] = {
        { "Max Frameskip", NULL },
        { "Sound"        , NULL },
        { "Stats"        , NULL },
        { "Fullscreen"   , NULL },
        { "Set Keys (Buggy)", NULL },
    };

    m = rb->menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);

    while(!done)
    {
        
        result=rb->menu_show(m);
    
        switch (result)
        {
            case 0: /* Frameskip */
                options.maxskip-=3;
                rb->set_option(items[0].desc, &options.maxskip, INT, frameskip, 4, NULL );
                options.maxskip+=3;
                break;
            case 1: /* Sound */
                if(options.sound>1) options.sound=1;
                rb->set_option(items[1].desc, &options.sound, INT, onoff, 2, NULL );
                break;
            case 2: /* Stats */
                rb->set_option(items[2].desc, &options.showstats, INT, onoff, 2, NULL );
                break;
            case 3: /* Fullscreen */
                rb->set_option(items[3].desc, &options.fullscreen, INT, fullscreen, 3, NULL );
                setvidmode(options.fullscreen);
                break;
            case 4: /* Keys */
                setupkeys();
                break;
            default:
                done=true;
                break;
        }
    }
    rb->menu_exit(m);
}
