#ifndef XMENU_H
#define XMENU_H
#include "xio.h"
#include "config.h"

struct uih_context;
typedef union {
    char *dstring;
    int dint;
    number_t number;
    number_t dcoord[2];
    xio_path dpath;
    void *dummy;
} dialogparam;
typedef struct dialog {
    const char *question;
    int type;
    int defint;
    const char *defstr;
    number_t deffloat;
    number_t deffloat2;
} menudialog;

typedef char *(*tokenfunc)(struct uih_context *c);
#define DIALOG_INT 1
#define DIALOG_FLOAT 2
#define DIALOG_STRING 3
#define DIALOG_KEYSTRING 4
#define DIALOG_IFILE 5
#define DIALOG_OFILE 6
#define DIALOG_CHOICE 7
#define DIALOG_ONOFF 8
#define DIALOG_COORD 9
#define DIALOG_PALSLIDER 10
#define DIALOG_PALPICKER 11
#define DIALOG_IFILES 12
#define DIALOG_LIST 13

#define DIALOGIFILE(question, filename)                                        \
    {                                                                          \
        question, DIALOG_IFILE, 0, filename                                    \
    }
#define DIALOGOFILE(question, filename)                                        \
    {                                                                          \
        question, DIALOG_OFILE, 0, filename                                    \
    }
#define DIALOGSTR(question, default)                                           \
    {                                                                          \
        question, DIALOG_STRING, 0, default                                    \
    }
#define DIALOGKEYSTR(question, default)                                        \
    {                                                                          \
        question, DIALOG_KEYSTRING, 0, default                                 \
    }
#define DIALOGINT(question, default)                                           \
    {                                                                          \
        question, DIALOG_INT, default                                          \
    }
#define DIALOGONOFF(question, default)                                         \
    {                                                                          \
        question, DIALOG_ONOFF, default                                        \
    }
#define DIALOGFLOAT(question, default)                                         \
    {                                                                          \
        question, DIALOG_FLOAT, 0, NULL, default                               \
    }
#define DIALOGCHOICE(question, table, default)                                 \
    {                                                                          \
        question, DIALOG_CHOICE, default, (const char *)table                  \
    }
#define DIALOGCOORD(question, default1, default2)                              \
    {                                                                          \
        question, DIALOG_COORD, 0, NULL, default1, default2                    \
    }
#define DIALOGPALSLIDER(question, default)                                     \
    {                                                                          \
        question, DIALOG_SLIDER, default                                       \
    }
#define DIALOGIFILES(question, filenames)                                      \
    {                                                                          \
        question, DIALOG_IFILES, 0, filenames                                  \
    }
#define DIALOGLIST(question, default)                                          \
    {                                                                          \
        question, DIALOG_LIST, 0, default                                      \
    }

#define DIALOGIFILE_I(_question, _filename)                                    \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_IFILE;                 \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = _filename;                  \
    menudialogs_i18n[no_menudialogs_i18n].deffloat = 0;                        \
    menudialogs_i18n[no_menudialogs_i18n].deffloat2 = 0;                       \
    ++no_menudialogs_i18n;

#define DIALOGOFILE_I(_question, _filename)                                    \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_OFILE;                 \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = _filename;                  \
    ++no_menudialogs_i18n;

#define DIALOGKEYSTR_I(_question, _default)                                    \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_KEYSTRING;             \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = _default;                   \
    ++no_menudialogs_i18n;

#define DIALOGSTR_I(_question, _default)                                       \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_STRING;                \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = _default;                   \
    ++no_menudialogs_i18n;

#define DIALOGINT_I(_question, _default)                                       \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_INT;                   \
    menudialogs_i18n[no_menudialogs_i18n].defint = _default;                   \
    ++no_menudialogs_i18n;

#define DIALOGONOFF_I(_question, _default)                                     \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_ONOFF;                 \
    menudialogs_i18n[no_menudialogs_i18n].defint = _default;                   \
    ++no_menudialogs_i18n;

#define DIALOGFLOAT_I(_question, _default)                                     \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_FLOAT;                 \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = NULL;                       \
    menudialogs_i18n[no_menudialogs_i18n].deffloat = _default;                 \
    ++no_menudialogs_i18n;

#define DIALOGCHOICE_I(_question, _table, _default)                            \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_CHOICE;                \
    menudialogs_i18n[no_menudialogs_i18n].defint = _default;                   \
    menudialogs_i18n[no_menudialogs_i18n].defstr = (const char *)_table;       \
    ++no_menudialogs_i18n;

#define DIALOGCOORD_I(_question, _default1, _default2)                         \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_COORD;                 \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = NULL;                       \
    menudialogs_i18n[no_menudialogs_i18n].deffloat = _default1;                \
    menudialogs_i18n[no_menudialogs_i18n].deffloat2 = _default2;               \
    ++no_menudialogs_i18n;

#define DIALOGPALSLIDER_I(_question, _default)                                 \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_PALSLIDER;             \
    menudialogs_i18n[no_menudialogs_i18n].defint = _default;                   \
    ++no_menudialogs_i18n;

#define DIALOGPALPICKER_I(_question, _default)                                 \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_PALPICKER;             \
    menudialogs_i18n[no_menudialogs_i18n].defint = _default;                   \
    ++no_menudialogs_i18n;

#define DIALOGIFILES_I(_question, _filenames)                                  \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_IFILES;                \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = _filenames;                 \
    ++no_menudialogs_i18n;

#define DIALOGLIST_I(_question, _default)                                      \
    menudialogs_i18n[no_menudialogs_i18n].question = _question;                \
    menudialogs_i18n[no_menudialogs_i18n].type = DIALOG_LIST;                  \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = _default;                   \
    ++no_menudialogs_i18n;

#define NULL_I()                                                               \
    menudialogs_i18n[no_menudialogs_i18n].question = NULL;                     \
    menudialogs_i18n[no_menudialogs_i18n].type = 0;                            \
    menudialogs_i18n[no_menudialogs_i18n].defint = 0;                          \
    menudialogs_i18n[no_menudialogs_i18n].defstr = NULL;                       \
    menudialogs_i18n[no_menudialogs_i18n].deffloat = 0;                        \
    menudialogs_i18n[no_menudialogs_i18n].deffloat2 = 0;                       \
    ++no_menudialogs_i18n;

typedef struct menuitem {
    const char *menuname;
    const char *key;
    const char *name;
    const char *shortname;
    int type;
    int flags;
    void (*function)(void);
    int iparam;
    const void *pparam;
    int (*control)(void);
    const menudialog *(*dialog)(struct uih_context *);
} menuitem;

#define MENU_NOPARAM 1
#define MENU_SUBMENU 2
#define MENU_INT 3
#define MENU_STRING 4
#define MENU_DIALOG 6
#define MENU_CUSTOMDIALOG 7
#define MENU_SEPARATOR 8

/* Definitions for static menuitems. These items cannot be internationalized. */

#define SUBMENU(menuname, key, name, param)                                    \
    {                                                                          \
        menuname, key, name, param, MENU_SUBMENU, 0, NULL, 0, NULL, NULL, NULL \
    }

/* Definitions for internationalized menus. All of them must be defined
dynamically because TR() cannot be used within a static
variable. Usage (example):

SUBMENU_I("file", "q", "Quit", "quitmenu")

See ui/ui.c, ui_registermenus_i18n() for further details. */

#define MENUNOP_I(_menuname, _key, _name, _shortname, _flags, _function)       \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_NOPARAM;                     \
    menuitems_i18n[no_menuitems_i18n].flags = _flags;                          \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = NULL;                           \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    ++no_menuitems_i18n;

#define MENUNOPCB_I(_menuname, _key, _name, _shortname, _flags, _function,     \
                    _checkbutton)                                              \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_NOPARAM;                     \
    menuitems_i18n[no_menuitems_i18n].flags = (_flags) | MENUFLAG_CHECKBOX;    \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = NULL;                           \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    menuitems_i18n[no_menuitems_i18n].control = (int (*)(void))_checkbutton;   \
    ++no_menuitems_i18n;

#define MENUINT_I(_menuname, _key, _name, _shortname, _flags, _function,       \
                  _param)                                                      \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_INT;                         \
    menuitems_i18n[no_menuitems_i18n].flags = _flags;                          \
    menuitems_i18n[no_menuitems_i18n].iparam = _param;                         \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    ++no_menuitems_i18n;

#define MENUINTRB_I(_menuname, _key, _name, _shortname, _flags, _function,     \
                    _param, _checkbutton)                                      \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_INT;                         \
    menuitems_i18n[no_menuitems_i18n].flags = (_flags) | MENUFLAG_RADIO;       \
    menuitems_i18n[no_menuitems_i18n].iparam = _param;                         \
    menuitems_i18n[no_menuitems_i18n].pparam = NULL;                           \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    menuitems_i18n[no_menuitems_i18n].control = (int (*)(void))_checkbutton;   \
    ++no_menuitems_i18n;

#define SUBMENU_I(_menuname, _key, _name, _param)                              \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _param;                      \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_SUBMENU;                     \
    menuitems_i18n[no_menuitems_i18n].flags = 0;                               \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = NULL;                           \
    menuitems_i18n[no_menuitems_i18n].function = NULL;                         \
    ++no_menuitems_i18n;

#define MENUDIALOG_I(_menuname, _key, _name, _shortname, _flags, _function,    \
                     _param)                                                   \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_DIALOG;                      \
    menuitems_i18n[no_menuitems_i18n].flags = _flags;                          \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = (const void *)_param;           \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    ++no_menuitems_i18n;

#define MENUDIALOGCB_I(_menuname, _key, _name, _shortname, _flags, _function,  \
                       _param, _check)                                         \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_DIALOG;                      \
    menuitems_i18n[no_menuitems_i18n].flags = (_flags) | MENUFLAG_CHECKBOX;    \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = (const void *)_param;           \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    menuitems_i18n[no_menuitems_i18n].control = (int (*)(void))_check;         \
    ++no_menuitems_i18n;

#define MENUCDIALOG_I(_menuname, _key, _name, _shortname, _flags, _function,   \
                      _param)                                                  \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_CUSTOMDIALOG;                \
    menuitems_i18n[no_menuitems_i18n].flags = _flags;                          \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = NULL;                           \
    menuitems_i18n[no_menuitems_i18n].control = NULL;                          \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    menuitems_i18n[no_menuitems_i18n].dialog =                                 \
        (const menudialog *(*)(struct uih_context *))_param;                   \
    ++no_menuitems_i18n;

#define MENUCDIALOGCB_I(_menuname, _key, _name, _shortname, _flags, _function, \
                        _param, _check)                                        \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_CUSTOMDIALOG;                \
    menuitems_i18n[no_menuitems_i18n].flags = (_flags) | MENUFLAG_CHECKBOX;    \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = (const void *)_param;           \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    menuitems_i18n[no_menuitems_i18n].control = (int (*)(void))_check;         \
    menuitems_i18n[no_menuitems_i18n].dialog =                                 \
        (const menudialog *(*)(struct uih_context *))_param;                   \
    ++no_menuitems_i18n;

#define MENUSEPARATOR_I(_menuname)                                             \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = NULL;                        \
    menuitems_i18n[no_menuitems_i18n].key = 0;                                 \
    menuitems_i18n[no_menuitems_i18n].type = MENU_SEPARATOR;                   \
    menuitems_i18n[no_menuitems_i18n].flags = 0;                               \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = "";                               \
    menuitems_i18n[no_menuitems_i18n].pparam = NULL;                           \
    menuitems_i18n[no_menuitems_i18n].function = NULL;                         \
    ++no_menuitems_i18n;

#define SUBMENUNOOPT_I(_menuname, _key, _name, _param)                         \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _param;                      \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_SUBMENU;                     \
    menuitems_i18n[no_menuitems_i18n].flags = MENUFLAG_NOOPTION;               \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = NULL;                           \
    menuitems_i18n[no_menuitems_i18n].function = NULL;                         \
    ++no_menuitems_i18n;

#define MENUSTRING_I(_menuname, _key, _name, _shortname, _flags, _function,    \
                     _param)                                                   \
    menuitems_i18n[no_menuitems_i18n].menuname = _menuname;                    \
    menuitems_i18n[no_menuitems_i18n].shortname = _shortname;                  \
    menuitems_i18n[no_menuitems_i18n].key = _key;                              \
    menuitems_i18n[no_menuitems_i18n].type = MENU_STRING;                      \
    menuitems_i18n[no_menuitems_i18n].flags = _flags;                          \
    menuitems_i18n[no_menuitems_i18n].iparam = 0;                              \
    menuitems_i18n[no_menuitems_i18n].name = _name;                            \
    menuitems_i18n[no_menuitems_i18n].pparam = (const void *)_param;           \
    menuitems_i18n[no_menuitems_i18n].function = (void (*)(void))_function;    \
    ++no_menuitems_i18n;

/* End of i18n definitions. */

#define MENUFLAG_CHECKBOX 1
#define MENUFLAG_RADIO 2
#define MENUFLAG_INTERRUPT 4
#define MENUFLAG_INCALC 8
#define MENUFLAG_NOMENU 16
#define MENUFLAG_NOOPTION 32
#define MENUFLAG_NOPLAY 64
#define MENUFLAG_ATSTARTUP 128
#define MENUFLAG_DIALOGATDISABLE 256

#define NITEMS(n) (sizeof(n) / sizeof(menuitem))
#define menu_getdialog(context, m)                                             \
    ((m)->type == MENU_DIALOG ? (const menudialog *)(m)->pparam                \
                              : (m)->dialog(context))

void menu_add(const menuitem *item, int n);
void menu_insert(const menuitem *item, const char *before, int n);
const menuitem *menu_findkey(const char *key, const char *root);
const menuitem *menu_findcommand(const char *name);
const char *menu_fullname(const char *menu);
const menuitem *menu_item(const char *menu, int n);
void menu_delete(const menuitem *items, int n);
int menu_enabled(const menuitem *item, struct uih_context *c);
void menu_activate(const menuitem *item, struct uih_context *c, dialogparam *d);
const menuitem *
menu_genernumbered(int n, const char *menuname, const char *const *const names,
                   const char *keys, int type, int flags,
                   void (*fint)(struct uih_context *context, int),
                   int (*cint)(struct uih_context *context, int),
                   const char *prefix);
void menu_delnumbered(int n, const char *name);
void menu_addqueue(const menuitem *item, dialogparam *d);
const menuitem *menu_delqueue(dialogparam **d);
void menu_destroydialog(const menuitem *item, dialogparam *d,
                        struct uih_context *uih);
int menu_havedialog(const menuitem *item, struct uih_context *c);
int menu_available(const menuitem *item, const char *root);
const char *menu_processcommand(struct uih_context *uih, tokenfunc f,
                                int scheme, int mask, const char *root);
void menu_printhelp(void);

number_t menu_getfloat(const char *s, const char **error);
const char *menu_fillparam(struct uih_context *uih, tokenfunc f,
                           const menudialog *d, dialogparam *p);
int menu_processargs(int n, int argc, char **argv);
void menu_forall(struct uih_context *c,
                 void (*callback)(struct uih_context *c, const menuitem *item));
#endif
