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

/* 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;

HANDLE Console;
HANDLE ConsoleIn;

#define NORM_ATTR (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
#define INVR_ATTR (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
#define EMPTY_LINE "                                                                                "

void text_out(short attr, int x, int y, const char *text)
{
  DWORD Dummy;
  COORD Coord;
  SetConsoleTextAttribute(Console, NORM_ATTR);
  Coord.X = x + 5;
  Coord.Y = y + 5;
  SetConsoleCursorPosition(Console, Coord);
  WriteConsole(Console, EMPTY_LINE, strlen(EMPTY_LINE), &Dummy, 0);

  SetConsoleCursorPosition(Console, Coord);
  SetConsoleTextAttribute(Console, attr);
  WriteConsole(Console, text, strlen(text), &Dummy, 0);
}

void show_status(const char *msg)
{
  text_out(NORM_ATTR, 0, 12, msg);
}

int get_key(void)
{
  INPUT_RECORD Input;
  DWORD NumRead;
  do
  { /* Wait for key release */
    ReadConsoleInput(ConsoleIn, &Input, 1, &NumRead);
  }
  while(Input.EventType != KEY_EVENT && Input.Event.KeyEvent.bKeyDown);

  do
  { /* Read The Key */
    ReadConsoleInput(ConsoleIn, &Input, 1, &NumRead);
  }
  while(Input.EventType != KEY_EVENT && !Input.Event.KeyEvent.bKeyDown);
  return Input.Event.KeyEvent.wVirtualKeyCode;
}

#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)
{
  SetConsoleTextAttribute(Console, NORM_ATTR);
  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)
{
  int i;
  for(i = 0; i < 15; i++)
  {
    text_out(NORM_ATTR, 0, i, "");
  }
}

int main(void)
{
  int i;
  DWORD Dummy;
  COORD Coord;
  Console = GetStdHandle(STD_OUTPUT_HANDLE);
  ConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
  SetConsoleMode(ConsoleIn, 0);

  SetConsoleTextAttribute(Console, NORM_ATTR);
  Coord.X = 1;
  Coord.Y = 1;
  SetConsoleCursorPosition(Console, Coord);

  for(i = 0; i < 80; i++)
    WriteConsole(Console, EMPTY_LINE, strlen(EMPTY_LINE), &Dummy, 0);

  text_out(NORM_ATTR, -4, -4, "Simple menu demo Press UP/Down, Enter to select");

  exec(main_menu);

  SetConsoleTextAttribute(Console, NORM_ATTR);
  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(INVR_ATTR, 0, i, menu[i].title);
      else
        text_out(NORM_ATTR, 0, i, 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);
}