/* The original version of this program can be found at http://damb.dk */
#include <curses.h>
#include <stdlib.h>

enum VirtKeys
{
  VK_RETURN = 10,
  VK_UP = 65 | 0x100,
  VK_DOWN = 66 | 0x100
};

/* This is an key element, it contains elements needed to handle one menu or submen item */
/* Each item has a text (title) and a associated function OR submenu                     */
/* If both function AND submenu are NULL we will jump one level up                       */
/* So our menu structure will be build as a tree of these                                */
typedef struct menu_item
{
  const char *title;
  struct menu_item const *submenu;
  void(*function)(void);
}menu_item;

WINDOW *StatusWin;
WINDOW *MenuWin;

#define NORM_ATTR A_NORMAL
#define INVR_ATTR A_STANDOUT
#define EMPTY_LINE "                                                                                "

void text_out(WINDOW *win, int attr, int x, int y, const char *text)
{
  while(*text)
  {
    wmove(win, y, x++);
    waddch(win, attr | *text);
    text++;
  }
  wrefresh(win);
}

void show_status(const char *msg)
{
  wclear(StatusWin);
  text_out(StatusWin, NORM_ATTR, 1, 1, msg);
}

int get_key(void)
{
  int i = getch();
  if(i == 27)
  { /* Special keys return three values, 27 + 91 + the real key */
    if(getch() == 91)
      return getch() | 0x100; /* To let the receiver know a VK_UP from a 'A' */
  }
  return i;
}

#define DUMMY_FUNC(name) \
static void name(void)          \
{                        \
  show_status(#name);    \
}

DUMMY_FUNC(func_11)
DUMMY_FUNC(func_21)
DUMMY_FUNC(func_22)
DUMMY_FUNC(func_231)
DUMMY_FUNC(func_232)
DUMMY_FUNC(func_233)

void exit_func(void)
{
  exit(0);
}

/* These are the menu's, will have to start from the buttom  */
menu_item const pb_list_menu[] =
{
  {"Ole",   NULL, func_231},
  {"Peter", NULL, func_232},
  {"Poul",  NULL, func_233},
  {"Back",  NULL, NULL},
  {NULL,    NULL, NULL}
};

menu_item const phonebook_menu[] =
{
  {"AddEntry", NULL, func_11},
  {"List",     pb_list_menu, NULL},
  {"Back",     NULL, NULL},
  {NULL,       NULL, NULL}
};

menu_item const sub_menu_2[] =
{
  {"New SMS", NULL, func_21},
  {"List",    NULL, func_22},
  {"Back",    NULL, NULL},
  {NULL,      NULL, NULL}
};

menu_item const main_menu[] =
{
  {"PhoneBook", phonebook_menu, NULL},
  {"SMS",       sub_menu_2,     NULL},
  {"Exit",      NULL,           exit_func},
  {NULL,        NULL,           NULL}
};

void exec(menu_item const * menu);

void clear_menu_window(void)
{
  wclear(MenuWin);
  box(MenuWin, ACS_VLINE, ACS_HLINE);
}

int main(void)
{
  initscr();
  cbreak();
  noecho();
  intrflush(stdscr, FALSE);
  MenuWin = subwin(stdscr, 10, 20, 1, 1);
  StatusWin = subwin(stdscr, 2, 60, 20, 1);
  intrflush(MenuWin, FALSE);
  wcolor_set(MenuWin, 3, 0);
  box(MenuWin, ACS_VLINE, ACS_HLINE);
  show_status("Simple menu demo Press UP/Down, Enter to select");

  exec(main_menu);

  return EXIT_SUCCESS;
}

/* This is the main menu executer, it will call itself to execute submenus */
void exec(menu_item const * menu)
{
  int ch;
  int i;
  int state = 0;
  clear_menu_window();
  do
  {
    /* I'm lazy so I'll update the complete menu each time */
    for(i = 0; menu[i].title; i++)
    {
      if(i == state)
        text_out(MenuWin, INVR_ATTR, 1, i + 1, menu[i].title);
      else
        text_out(MenuWin, NORM_ATTR, 1, i + 1, menu[i].title);
    }
    ch = get_key();
    switch(ch)
    {
      case VK_RETURN:  /* Enter */
        if(menu[state].function)
          menu[state].function();
        else if(menu[state].submenu)
        { /* It's a submenu */
          exec(menu[state].submenu);
          clear_menu_window();
        }
        else
        { /* It is a back menu item */
          return;
        }
        break;
      case VK_UP:
        if(state != 0)
          state--;
        break;
      case VK_DOWN:
        if(menu[state + 1].title)
          state++;
        break;
    }
  }
  while(1);
}