/*
 * menu.c
 * ----------
 * This file handles the menus.
 */

#include <string.h>
#include "kernel.h"
#include "swis.h"
#include "inttypes.h"
#include "player.h"
#include "config.h"
#include "display.h"
#include "keyboard.h"
#include "menu.h"
#include "mt.h"
#include "wimp.h"
#include "choices.h"
#include "playlist.h"
#include "msg.h"
#include "ka_stream.h"
#include "ka_codecs.h"
#include "ka_mem.h"
#include "ka_log.h"

static struct
{
  int     id;
  menu_t* menu;
  int     x;
  int     y;
  int32_t hit[10];
} context = {0, NULL, 0, 0, {-1}};

// menu structures
// note. the menu text pointers be overwritten if menu text is found in
//       the Messages file.

// icon flags
#define IC_DEF       0x07000121
// item flags
#define IT_TICKED    0x01
#define IT_SUBWARN   0x18  // Submenu Warning + Always
#define IT_LAST      0x80
#define IT_INDTITLE  0x100

#define MENU_MAIN_FILM     0
#define MENU_MAIN_STREAMS  1
#define MENU_MAIN_CONTROL  2
#define MENU_MAIN_IMAGE    3
#define MENU_MAIN_PLAYLIST 4
#define MENU_MAIN_HELP     5
#define MENU_MAIN_QUIT     6

static struct
{
  menu_block_t blk;
  menu_item_t film;
  menu_item_t streams;
  menu_item_t control;
  menu_item_t image;
  menu_item_t playlist;
  menu_item_t quit;
  menu_item_t help;
}
  main_menu =
{
  {"KinoAMP", NULL, 8, 7, 2, 7, 0, 200, 44, 0},
  {0x100 | IT_SUBWARN, -1, IC_DEF, "Film"    , NULL, 5},
  {    0             , -1, IC_DEF, "Streams" , NULL, 8},
  {    0 | IT_SUBWARN, -1, IC_DEF, "Control" , NULL, 8},
  {    0 | IT_SUBWARN, -1, IC_DEF, "Image"   , NULL, 6},
  {    0 | IT_SUBWARN, -1, IC_DEF, "Playlist", NULL, 9},
  {    0             , -1, IC_DEF, "Help..." , NULL, 8},
  { 0x80             , -1, IC_DEF, "Quit"    , NULL, 5}
};

#define MENU_FILM_INFO        0
#define MENU_FILM_SAVECHOICES 1
#define MENU_FILM_SAVEFRAME   2

static struct
{
  menu_block_t blk;
  menu_item_t info;
  menu_item_t choices;
  menu_item_t save;
}
  film_menu =
{
  {"Film", NULL, 5, 7, 2, 7, 0, 200, 44, 0},
  {0x100, -1, IC_DEF, "Info..."     , NULL,  8},
  {    0, -1, IC_DEF, "Save choices", NULL, 13},
  { 0x80, -1, IC_DEF, "Save frame"  , NULL, 11}
};

#define MENU_STREAMS_PROGRAMS  0
#define MENU_STREAMS_AUDIO     1
#define MENU_STREAMS_SUBTITLES 2
#define MENU_STREAMS_VIDEO     3

static struct
{
  menu_block_t blk;
  menu_item_t program;
  menu_item_t astream;
  menu_item_t sstream;
  menu_item_t vstream;
}
  streams_menu =
{
  {"Streams", NULL, 8, 7, 2, 7, 0, 200, 44, 0},
  {0x100 | IT_SUBWARN, -1, IC_DEF, "Programs", NULL, 9},
  {    0 | IT_SUBWARN, -1, IC_DEF, "Audio", NULL, 6},
  {    0 | IT_SUBWARN, -1, IC_DEF, "Subtitles", NULL, 10},
  { 0x80 | IT_SUBWARN, -1, IC_DEF, "Video", NULL, 6}
};

static struct
{
  menu_block_t blk;
  menu_item_t stream[KA_PROGRAM_MAX_AUDIOS];
}
  astreams_menu =
{
  {"Audio", NULL, 6, 7, 2, 7, 0, 200, 44, 0},
  {{0x180, -1, IC_DEF, "Stream 00", NULL, 8}}
};

static struct
{
  menu_block_t blk;
  menu_item_t stream[KA_PROGRAM_MAX_SUBTITLES];
}
  sstreams_menu =
{
  {"Subtitle",NULL, 6, 7, 2, 7, 0, 200, 44, 0},
  {{0x180, -1, IC_DEF, "Off", NULL, 3}}
};

static struct
{
  menu_block_t blk;
  menu_item_t stream[KA_PROGRAM_MAX_VIDEOS];
}
  vstreams_menu =
{
  {"Video",NULL, 6, 7, 2, 7, 0, 200, 44, 0},
  {{0x180, -1, IC_DEF, "Stream 00", NULL, 8}}
};

static struct
{
  menu_block_t blk;
  menu_item_t program[KA_MAX_PROGRAMS];
}
  programs_menu =
{
  {"Program",NULL, 6, 7, 2, 7, 0, 200, 44, 0},
  {{0x180, -1, IC_DEF, "Stream 00", NULL, 8}}
};

#define MENU_CONTROL_GOTO       0
#define MENU_CONTROL_ZOOM       1
#define MENU_CONTROL_FULLSCREEN 2
#define MENU_CONTROL_PAUSE      3
#define MENU_CONTROL_MUTE       4
#define MENU_CONTROL_RESTART    5
#define MENU_CONTROL_LOOP       6
#define MENU_CONTROL_AUTOEXIT   7
#define MENU_CONTROL_PANEL      8
#define MENU_CONTROLSCROLLBARS  9

static struct
{
  menu_block_t blk;
  menu_item_t moveto;
  menu_item_t zoom;
  menu_item_t full_screen;
  menu_item_t pause;
  menu_item_t mute;
  menu_item_t restart;
  menu_item_t loop;
  menu_item_t autoexit;
  menu_item_t controls;
  menu_item_t scroll_bars;
}
  control_menu =
{
  {"Control", NULL, 8, 7, 2, 7, 0, 200, 44, 0},
  {0x100 | IT_SUBWARN, -1, IC_DEF, "Goto"         , NULL,  5},
  {    0 | IT_SUBWARN, -1, IC_DEF, "Zoom"         , NULL,  5},
  {    0, -1, IC_DEF, "Full screen"  , NULL, 12},
  {    0, -1, IC_DEF, "Pause"        , NULL,  6},
  {    0, -1, IC_DEF, "Mute"         , NULL,  5},
  {    0, -1, IC_DEF, "Restart"      , NULL,  8},
  {    0, -1, IC_DEF, "Loop"         , NULL,  5},
  {    0, -1, IC_DEF, "Auto exit"    , NULL, 10},
  {    0, -1, IC_DEF, "Control panel", NULL, 14},
  { 0x80, -1, IC_DEF, "Scroll bars"  , NULL, 12}
};

static struct
{
  menu_block_t blk;
  menu_item_t title[KA_PROGRAM_MAX_TITLES];
}
  titles_menu =
{
  {"Titles", NULL, 7, 7, 2, 7, 0, 200, 44, 0},
  {{0x180 | IT_SUBWARN, -1, IC_DEF, "Title 00", NULL, 9}}
};

static struct
{
  menu_block_t blk;
  menu_item_t chapter[KA_PROGRAM_MAX_CHAPTERS];
}
  chapters_menu =
{
  {"Chapters", NULL, 9, 7, 2, 7, 0, 200, 44, 0},
  {{0x180, -1, IC_DEF, "Chapter 00", NULL, 11}}
};

#define MENU_IMAGE_MONOCHROME  0
#define MENU_IMAGE_LOCKASPECT  1
#define MENU_IMAGE_PIXELASPECT 2
#define MENU_IMAGE_LOCKSIZE    3
#define MENU_IMAGE_DEINTERLACE 4
#define MENU_IMAGE_RESIZEMODE  5
#define MENU_IMAGE_RESIZETO    6

static struct
{
  menu_block_t blk;
  menu_item_t monochrome;
  menu_item_t lock_aspect;
  menu_item_t pixel_aspect;
  menu_item_t size;
  menu_item_t deinterlace;
  menu_item_t resizemode;
  menu_item_t resizeto;
}
  image_menu =
{
  {"Image", NULL, 6, 7, 2, 7, 0, 200, 44, 0},
  {0x100, -1, IC_DEF, "Monochrome"   , NULL, 11},
  {    0, -1, IC_DEF, "Lock aspect"  , NULL, 12},
  {    0, -1, IC_DEF, "Pixel aspect" , NULL, 13},
  {    0, -1, IC_DEF, "Lock size"    , NULL, 10},
  {    0 | IT_SUBWARN, -1, IC_DEF, "Deinterlace"  , NULL, 12},
  {    0 | IT_SUBWARN, -1, IC_DEF, "Resize mode"  , NULL, 12},
  { 0x80 | IT_SUBWARN, -1, IC_DEF, "Resize to"    , NULL, 10}
};

static struct
{
  menu_block_t blk;
  menu_item_t method[3];
}
  resizemode_menu =
{
  {"Resize", NULL, 7, 7, 2, 7, 0, 200, 44, 0},
  {{0x100 | IT_SUBWARN, -1, IC_DEF, "Source"    , NULL,  7},
   {    0 | IT_SUBWARN, -1, IC_DEF, "Pan & scan", NULL, 11},
   { 0x80 | IT_SUBWARN, -1, IC_DEF, "Stretch"   , NULL,  8}}
};

static struct
{
  menu_block_t blk;
  menu_item_t method[10];
}
  resizeto_menu =
{
  {"Aspect ratios", NULL, 14, 7, 2, 7, 0, 200, 44, 0},
  {{0x100, -1, IC_DEF, "Monitor"   , NULL,  8},
   {    0, -1, IC_DEF, "  5:   4"  , NULL,  9},
   {    0, -1, IC_DEF, "  4:   3"  , NULL,  9},
   {    0, -1, IC_DEF, " 16:  10"  , NULL,  9},
   {    0, -1, IC_DEF, " 16:   9"  , NULL,  9},
   {    0, -1, IC_DEF, "256: 135"  , NULL,  9},
   { 0x80, -1, IC_DEF, " 21:   9"  , NULL,  9}}
};

static struct
{
  menu_block_t blk;
  menu_item_t method[4];
}
  deinterlace_menu =
{
  {"Deinterlace method", NULL, 19, 7, 2, 7, 0, 200, 44, 0},
  {{0x100, -1, IC_DEF, "None"      , NULL,  5},
   {    0, -1, IC_DEF, "Blend"     , NULL,  6},
   {    0, -1, IC_DEF, "Blend (x2)", NULL, 11},
   { 0x80, -1, IC_DEF, "Fields x2" , NULL, 10}}
};

static struct
{
  menu_block_t blk;
  menu_item_t mag[5];
}
  zoom_menu =
{
  {"Zoom", NULL, 5, 7, 2, 7, 0, 200, 44, 0},
  {{0x100, -1, IC_DEF, "50%" , NULL, 4},
   {    0, -1, IC_DEF, "100%", NULL, 5},
   {    0, -1, IC_DEF, "200%", NULL, 5},
   {    0, -1, IC_DEF, "300%", NULL, 5},
   { 0x80, -1, IC_DEF, "400%", NULL, 5}}
};

#define MENU_PLAYLIST_SELECT   0
#define MENU_PLAYLIST_NEXT     1
#define MENU_PLAYLIST_PREVIOUS 2
#define MENU_PLAYLIST_RANDOM   3
#define MENU_PLAYLIST_CLOSE    4

static struct
{
  menu_block_t blk;
  menu_item_t select;
  menu_item_t next;
  menu_item_t previous;
  menu_item_t random;
  menu_item_t close;
}
  pl_main_menu =
{
  {"Playlist",NULL, 9, 7, 2, 7, 0, 200, 44, 0},
  {0x100 | IT_SUBWARN, -1, IC_DEF, "Select"  , NULL, 7},
  {    0, -1, IC_DEF, "Next"    , NULL, 5},
  {    0, -1, IC_DEF, "Previous", NULL, 9},
  {    0, -1, IC_DEF, "Random"  , NULL, 7},
  { 0x80, -1, IC_DEF, "Close"   , NULL, 6}
};

static struct
{
  menu_block_t blk;
  menu_item_t empty;
}
  pl_empty_menu =
{
  {"Playlist", NULL, 9, 7, 2, 7, 0, 200, 44, 0},
  {0x180, -1, IC_DEF | IC_SHADED, "(no playlist loaded)", NULL, 21}
};

#define MENU_TEXT_LEN 40

/*
 * read_menus
 * ----------
 * load menu text from Messages file if possible
 */
void read_menus(void)
{
  msg_load_menu("main_m", &main_menu.blk);
  msg_load_menu("film_sm", &film_menu.blk);
  msg_load_menu("streams_sm", &streams_menu.blk);
  msg_load_menu("programs_sm", &programs_menu.blk);
  msg_load_menu("astreams_sm", &astreams_menu.blk);
  msg_load_menu("sstreams_sm", &sstreams_menu.blk);
  msg_load_menu("vstreams_sm", &vstreams_menu.blk);
  msg_load_menu("control_sm", &control_menu.blk);
  msg_load_menu("titles_sm", &titles_menu.blk);
  msg_load_menu("chapters_sm", &chapters_menu.blk);
  msg_load_menu("image_sm", &image_menu.blk);
  msg_load_menu("zoom_sm", &zoom_menu.blk);
  msg_load_menu("resizemode_m", &resizemode_menu.blk);
  msg_load_menu("deinterlace_m", &deinterlace_menu.blk);
  msg_load_menu("resizeto_m", &resizeto_menu.blk);
  msg_load_menu("pl_main_sm", &pl_main_menu.blk);
  msg_load_menu("pl_empty_sm", &pl_empty_menu.blk);

  main_menu.film.sub         = (int) &film_menu;
  main_menu.streams.sub      = (int) &streams_menu;
  main_menu.control.sub      = (int) &control_menu;
  main_menu.image.sub        = (int) &image_menu;
  main_menu.playlist.sub     = (int) &pl_main_menu;
  film_menu.save.sub         = win_handle.xfer_send;
  streams_menu.program.sub   = (int) &programs_menu;
  streams_menu.astream.sub   = (int) &astreams_menu;
  streams_menu.sstream.sub   = (int) &sstreams_menu;
  streams_menu.vstream.sub   = (int) &vstreams_menu;
  control_menu.moveto.sub    = (int) &titles_menu;
  control_menu.zoom.sub      = (int) &zoom_menu;
  image_menu.deinterlace.sub = (int) &deinterlace_menu;
  image_menu.resizemode.sub  = (int) &resizemode_menu;
  image_menu.resizeto.sub    = (int) &resizeto_menu;
  pl_main_menu.select.sub    = (int) &pl_empty_menu;


  for (int i = 0; i < KA_MAX_PROGRAMS; i++)
  {
    programs_menu.program[i].text = ka_mem_calloc(MENU_TEXT_LEN);
    programs_menu.program[i].len = MENU_TEXT_LEN;
  }

  for (int i = 0; i < KA_PROGRAM_MAX_AUDIOS; i++)
  {
    astreams_menu.stream[i].text = ka_mem_calloc(MENU_TEXT_LEN);
    astreams_menu.stream[i].len = MENU_TEXT_LEN;
  }

  for (int i = 1; i < KA_PROGRAM_MAX_SUBTITLES; i++)
  {
    sstreams_menu.stream[i].text = ka_mem_calloc(MENU_TEXT_LEN);
    sstreams_menu.stream[i].len = MENU_TEXT_LEN;
  }

  for (int i = 0; i < KA_PROGRAM_MAX_VIDEOS; i++)
  {
    vstreams_menu.stream[i].text = ka_mem_calloc(MENU_TEXT_LEN);
    vstreams_menu.stream[i].len = MENU_TEXT_LEN;
  }
}

/*
 * icon_text_change
 * ----------------
 */
static void icon_text_change(const char *text, int win_h, int icon_h)
{
   int blk[16];

   blk[0] = win_h;
   blk[1] = icon_h;
   _swix(Wimp_GetIconState, _IN(1), blk);
   snprintf((char*) blk[7], blk[9], "%s", text);
   blk[2] = 0;
   blk[3] = 0;
   _swix(Wimp_SetIconState, _IN(1), blk);
}

/*
 * display_film_info
 * -----------------
 * Displays information about the current film. Updates and opens the
 * FilmInfo window. This code is very similar to display_film_info() in
 * the Front End file main.c. The window uses the same template.
 */

static void display_film_info(player_t* player)
{
  const player_infostats_t* is = player_buildInfoStats(player);
  int i;
  window_state_t win;

  // update text (icon numbers are 6..15)
  for (i = 0; i < 10; i++)
    icon_text_change(is->s[i], win_handle.filminfo, i + 6);

  // open window
  win.window = win_handle.filminfo;
  _swix(Wimp_GetWindowState, _IN(1), &win);
  win.behind = -1; // bring to front
  _swix(Wimp_OpenWindow, _IN(1), &win);
}

/*
 * setChapter
 * ----------
 * Go to a film title/chapter.
 */
static void setChapter(player_t* player, uint32_t title, int32_t chapter)
{
  if (player->stream && ka_stream_setChapter(player->stream, title, chapter))
  {
    player_restartPlay(player, player_status_seek);
  }
}

//-------------------------------------------
// Individual menu handlers
//-------------------------------------------

typedef void (*ka_FNmenuWarning)(player_t* player, const int* lastHit);
typedef int  (*ka_FNmenuSelect)(player_t* player, const int* lastHit);
typedef int  (*ka_FNmenuHelp)(const int* lastHit, char* dst, int dstsize);

typedef struct
{
  const void*      menu;
  ka_FNmenuWarning FNmenuWarning;
  ka_FNmenuSelect  FNmenuSelect;
  ka_FNmenuHelp    FNmenuHelp;
} menu_handlers_t;

//-------------------------------------------

static void ignore_menu_warning(player_t* player, const int* lastHit)
{
  player = player;
  lastHit = lastHit;
}

static int ignore_menu_select(player_t* player, const int* lastHit)
{
  player = player;
  lastHit = lastHit;

  return 0;
}

//-------------------------------------------

static int main_menu_select(player_t* player, const int* lastHit)
{
  player = player;

  switch (*lastHit)
  {
    case MENU_MAIN_HELP:
      _kernel_oscli("Filer_Run <Kino$Dir>.!Help");
    break;

    case MENU_MAIN_QUIT:
      return -1;
    break;
  }

  return 0;
}

static int main_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("main_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static void film_menu_warning(player_t* player, const int* lastHit)
{
  lastHit = lastHit;

  saveas_init(player);
}

static int film_menu_select(player_t* player, const int* lastHit)
{
  switch (*lastHit)
  {
    case MENU_FILM_INFO:
      display_film_info(player);
    break;

    case MENU_FILM_SAVECHOICES:
      do_command(player, 0x183); // F3
    break;
  }

  return 0;
}

static int film_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("film_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static int streams_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("streams_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static void programs_menu_warning(player_t* player, const int* lastHit)
{
  ka_stack_t* stack = player->stream->stack;
  int i;

  lastHit = lastHit;

  // Programs list
  int max = stack->nr_programs;

  menu_item_t* item = programs_menu.program;

  if (max > 0)
  {
    for (i = 0; i < max; i++, item++)
    {
      report_string(item->text, item->len, "programs_sm_nr: %d", i + 1);
      item->icon_flags = IC_DEF;
      if (!ka_stream_programHasValidStreams(player->stream, i))
        item->icon_flags |= IC_SHADED;
      item->item_flags = 0;
      item->sub = NULL;
    }

    if (player->stream->program < max)
      programs_menu.program[player->stream->program].item_flags |= IT_TICKED;

    programs_menu.program[max - 1].item_flags |= IT_LAST;
  }
  else
  {
    report_string(item->text, item->len, "stream_none:");
    item->icon_flags = IC_DEF | IC_SHADED;
    item->item_flags = IT_LAST;
    item->sub = NULL;
  }

  programs_menu.program[0].item_flags |= IT_INDTITLE;
}

static int programs_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if (player->stream && (hit >= 0) && (player->stream->program != hit))
  {
    config.program = ka_stream_selectProgram
                         ( player->stream
                         , hit
                         , config.video.stream
                         , config.audio.stream
                         , config.subtitle.stream
                         );
    config.video.stream = player->stream->video.channel;
    config.audio.stream = player->stream->audio.channel;
    config.subtitle.stream = player->stream->subtitle.channel;
    player_restartPlay(player, player_status_seek);
  }

  return 0;
}

static int programs_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("avstream_mh", lastHit[-1], dst, dstsize);
}

//-------------------------------------------

static void astreams_menu_warning(player_t* player, const int* lastHit)
{
  int i;

  lastHit = lastHit;

  menu_item_t* item = astreams_menu.stream;

  // Audios list
  for (i = 0; i < KA_PROGRAM_MAX_AUDIOS; i++, item++)
  {
    ka_demux_audio_info_t info;
    ka_stream_getAudioInfo(player->stream, i + 1, &info);

    if (i >= info.nrStreams)
      break;

    if (info.channels)
      report_string( item->text, item->len
                   , "audios_sm_ch_nr:%d, %s %d Ch. %s"
                   , i + 1, info.type, info.channels, info.lang
                   );
    else
      report_string( item->text, item->len
                   , "audios_sm_nr:%d, %s %s"
                   , i + 1, info.type, info.lang
                   );

    item->icon_flags = IC_DEF;
    item->item_flags = 0;
    item->sub = NULL;
  }

  if (i > 0)
  {
    if (player->stream->audio.channel <= i)
      astreams_menu.stream[player->stream->audio.channel-1].item_flags |= IT_TICKED;

    astreams_menu.stream[i - 1].item_flags |= IT_LAST;
  }
  else
  {
    report_string(item->text, item->len, "stream_none:");
    item->icon_flags = IC_DEF | IC_SHADED;
    item->item_flags = IT_LAST;
    item->sub = NULL;
  }
  astreams_menu.stream[0].item_flags |= IT_INDTITLE;
}

static int astreams_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if (player->stream && (hit >= 0) && (player->stream->audio.channel != hit))
  {
    config.audio.stream = ka_stream_selectAudioStream(player->stream, hit + 1);
  }

  return 0;
}

static int astreams_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("avstream_mh", lastHit[-1], dst, dstsize);
}

//-------------------------------------------

static void sstreams_menu_warning(player_t* player, const int* lastHit)
{
  int i;

  lastHit = lastHit;

  // Subtitles list, has at least "None"
  menu_item_t* item = sstreams_menu.stream;

  item->icon_flags = IC_DEF;
  item->item_flags = 0;
  item->sub = NULL;
  item++;

  for (i = 1; i < KA_PROGRAM_MAX_SUBTITLES; i++, item++)
  {
    ka_demux_subtitle_info_t info;
    ka_stream_getSubtitleInfo(player->stream, i, &info);

    if (i > info.nrStreams)
      break;

    report_string( item->text, item->len, "subtitles_sm_nr:%d, %s %s", i, info.type, info.lang);
    item->icon_flags = IC_DEF;
    item->item_flags = 0;
    item->sub = NULL;
  }

  if (player->stream->subtitle.channel <= i)
    sstreams_menu.stream[player->stream->subtitle.channel].item_flags |= IT_TICKED;

  sstreams_menu.stream[i - 1].item_flags |= IT_LAST;
  sstreams_menu.stream[0].item_flags |= IT_INDTITLE;
}

static int sstreams_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if (player->stream && (hit >= 0) && (player->stream->subtitle.channel != hit))
  {
    config.subtitle.stream = ka_stream_selectSubtitleStream(player->stream, hit);
  }

  return 0;
}

static int sstreams_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("avstream_mh", lastHit[-1], dst, dstsize);
}

//-------------------------------------------

static void vstreams_menu_warning(player_t* player, const int* lastHit)
{
  ka_stack_t* stack = player->stream->stack;
  int i;

  lastHit = lastHit;

  ka_program_t* program = &stack->programs[player->stream->program];

  menu_item_t* item = vstreams_menu.stream;

  // Videos list
  int max = program->nr_videos;

  if (max > 0)
  {
    for (i = 0; i < max; i++, item++)
    {
      snprintf(item->text, item->len, "%s", ka_block_to_name(program->videos[i]));
      item->icon_flags = IC_DEF;
      item->item_flags = 0;
      item->sub = NULL;
    }

    if (player->stream->video.channel < max)
      vstreams_menu.stream[player->stream->video.channel].item_flags |= IT_TICKED;

    vstreams_menu.stream[max - 1].item_flags |= IT_LAST;
  }
  else
  {
    report_string(item->text, item->len, "stream_none:");
    item->icon_flags = IC_DEF | IC_SHADED;
    item->item_flags = IT_LAST;
    item->sub = NULL;
  }
  vstreams_menu.stream[0].item_flags |= IT_INDTITLE;
}

static int vstreams_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if (player->stream && (hit >= 0) && (player->stream->video.channel != hit))
  {
    config.video.stream = ka_stream_selectVideoStream(player->stream, hit);
    player_restartPlay(player, player_status_seek);
  }

  return 0;
}

static int vstreams_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("avstream_mh", lastHit[-1], dst, dstsize);
}

//-------------------------------------------

static void control_menu_warning(player_t* player, const int* lastHit)
{
  player = player;
  lastHit = lastHit;

  if (config.control & cfg_ctrl_scroll_bars)
    control_menu.scroll_bars.item_flags |= IT_TICKED;
  else
    control_menu.scroll_bars.item_flags &= ~(IT_TICKED);

  if (config.control & cfg_ctrl_loop)
    control_menu.loop.item_flags |= IT_TICKED;
  else
    control_menu.loop.item_flags &= ~(IT_TICKED);

  if (player_getPlayStatus(player) == play_status_pause)
    control_menu.pause.item_flags |= IT_TICKED;
  else
    control_menu.pause.item_flags &= ~(IT_TICKED);

  if (config.audio.cfg & cfg_audio_mute)
    control_menu.mute.item_flags |= IT_TICKED;
  else
    control_menu.mute.item_flags &= ~(IT_TICKED);

  if (config.audio.cfg & cfg_audio_play)
    control_menu.mute.icon_flags &= ~(IC_SHADED);
  else
    control_menu.mute.icon_flags |= IC_SHADED;

  if (config.control & cfg_ctrl_controls)
    control_menu.controls.item_flags |= IT_TICKED;
  else
    control_menu.controls.item_flags &= ~(IT_TICKED);

  if (config.control & cfg_ctrl_autoexit)
    control_menu.autoexit.item_flags |= IT_TICKED;
  else
    control_menu.autoexit.item_flags &= ~(IT_TICKED);

  if (config.control & cfg_ctrl_loop)
    control_menu.autoexit.icon_flags |= IC_SHADED;
  else
    control_menu.autoexit.icon_flags &= ~(IC_SHADED);
}

static int control_menu_select(player_t* player, const int* lastHit)
{
  switch (*lastHit)
  {
    case MENU_CONTROL_ZOOM:
      do_command(player, CMD_ZOOM_IN);
    break;

    case MENU_CONTROL_FULLSCREEN:
      do_command(player, CMD_SCREEN_TOGGLE);
    break;

    case MENU_CONTROL_PAUSE:
      do_command(player, CMD_REPLAY_TOGGLE);
    break;

    case MENU_CONTROL_MUTE:
      do_command(player, CMD_AUDIO_ON_OFF);
    break;

    case MENU_CONTROL_RESTART:
      do_command(player, CMD_REPLAY_RESTART);
    break;

    case MENU_CONTROL_LOOP:
      do_command(player, CMD_LOOP_TOGGLE);
    break;

    case MENU_CONTROL_AUTOEXIT:
      do_command(player, CMD_AUTOEXIT_TOGGLE);
    break;

    case MENU_CONTROL_PANEL:
      config.control ^= cfg_ctrl_controls;
      open_controls(player, NULL, (config.control & cfg_ctrl_controls) ? PANEL_OPEN : PANEL_CLOSE);
    break;

    case MENU_CONTROLSCROLLBARS:
      config.control ^= cfg_ctrl_scroll_bars;
      mt_setscrolls(player);
    break;
  }

  return 0;
}

static int control_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("control_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static void titles_menu_warning(player_t* player, const int* lastHit)
{
  ka_demux_pos_t pos;
  int i;

  lastHit = lastHit;

  ka_stream_getPosInfo(player->stream, &pos);

  // Titles list
  for (i = 0; i < KA_PROGRAM_MAX_TITLES; i++)
  {
    // Null name signals end of list
    const char* name = ka_stream_getTitleName(player->stream, player->stream->program, i);
    if (name == NULL)
      break;

    titles_menu.title[i].icon_flags = IC_DEF & ~(IC_SHADED);
    titles_menu.title[i].item_flags = 0;
    titles_menu.title[i].text = (char*) name;
    titles_menu.title[i].len = strlen(titles_menu.title[i].text) + 1;
    titles_menu.title[i].sub = NULL;

    // check if chapters
    name = ka_stream_getChapterName(player->stream, player->stream->program, i, 0);
    if (name != NULL)
    {
      titles_menu.title[i].sub = (int) &chapters_menu;
      titles_menu.title[i].item_flags |= IT_SUBWARN;
      titles_menu.title[i].icon_flags &= ~(IC_SHADED);
    }
    else
    {
      titles_menu.title[i].sub = NULL;
      titles_menu.title[i].icon_flags |= IC_SHADED;
    }
  }

  if (i > 0)
  {
    control_menu.moveto.sub = (int) &titles_menu;
    control_menu.moveto.icon_flags &= ~(IC_SHADED);

    if (pos.titleNr <= i)
      titles_menu.title[pos.titleNr].item_flags |= IT_TICKED;
    titles_menu.title[i - 1].item_flags |= IT_LAST;
  }
  else
  {
    control_menu.moveto.sub = NULL;
    control_menu.moveto.icon_flags |= IC_SHADED;

    titles_menu.title[0].icon_flags = IC_DEF | IC_SHADED;
    titles_menu.title[0].item_flags = IT_LAST;
  }
  titles_menu.title[0].item_flags |= IT_INDTITLE;
}

static int titles_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if (hit >= 0)
    setChapter(player, hit, -1);

  return 0;
}

static int titles_menu_help(const int* lastHit, char* dst, int dstsize)
{
  lastHit = lastHit;
  dst = dst;
  dstsize = dstsize;

  return 0;
}

//-------------------------------------------

static void chapters_menu_warning(player_t* player, const int* lastHit)
{
  int title = *lastHit;
  ka_demux_pos_t pos;
  int i;

  ka_stream_getPosInfo(player->stream, &pos);

  // Chapters list
  for (i = 0; i < KA_PROGRAM_MAX_CHAPTERS; i++)
  {
    // Null name signals end of list
    const char* name = ka_stream_getChapterName(player->stream, player->stream->program, title, i);
    if (name == NULL)
      break;

    chapters_menu.chapter[i].icon_flags = IC_DEF & ~(IC_SHADED);
    if ((title == pos.titleNr) && (i == pos.chapterNr))
      chapters_menu.chapter[i].icon_flags |= IC_SELECTED;
    chapters_menu.chapter[i].item_flags = 0;
    chapters_menu.chapter[i].text = (char*) name;
    chapters_menu.chapter[i].len = strlen(chapters_menu.chapter[i].text) + 1;
    chapters_menu.chapter[i].sub = NULL;
  }

  if (i > 0)
  {
    if ((pos.titleNr == title)
    &&  (pos.chapterNr <= i))
      chapters_menu.chapter[pos.chapterNr].item_flags |= IT_TICKED;
    chapters_menu.chapter[i - 1].item_flags |= IT_LAST;
  }
  else
  {
    chapters_menu.chapter[0].icon_flags = IC_DEF | IC_SHADED;
    chapters_menu.chapter[0].item_flags = IT_LAST;
  }
  chapters_menu.chapter[0].item_flags |= IT_INDTITLE;
}

static int chapters_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if (hit >= 0)
    setChapter(player, lastHit[-1], hit);

  return 0;
}

static int chapters_menu_help(const int* lastHit, char* dst, int dstsize)
{
  lastHit = lastHit;
  dst = dst;
  dstsize = dstsize;

  return 0;
}

//-------------------------------------------

static void zoom_menu_warning(player_t* player, const int* lastHit)
{
  lastHit = lastHit;

  for (int i = 0; i < 5; i++)
    zoom_menu.mag[i].item_flags &= ~(IT_TICKED); // clear any ticks

  // tick current setting
  int x_zoom = (100<<FRAC)/player->x_mag;
  int y_zoom = (100<<FRAC)/player->y_mag;
  if ((x_zoom == y_zoom)
  ||  (config.video.cfg & cfg_video_lock_aspect))
  {
    switch (x_zoom)
    {
      case  50: zoom_menu.mag[0].item_flags |= IT_TICKED; break;
      case 100: zoom_menu.mag[1].item_flags |= IT_TICKED; break;
      case 200: zoom_menu.mag[2].item_flags |= IT_TICKED; break;
      case 300: zoom_menu.mag[3].item_flags |= IT_TICKED; break;
      case 400: zoom_menu.mag[4].item_flags |= IT_TICKED; break;
    }
  }
}

static int zoom_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if (hit == 0)
    set_zoom(player, 50, 0);
  else if (hit > 0)
    set_zoom(player, hit * 100, 0);

  return 0;
}

static int zoom_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("zoom_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static void image_menu_warning(player_t* player, const int* lastHit)
{
  const video_mode* vmode = &config.video.modes[player->mode];

  lastHit = lastHit;

  if (vmode->cfg & cfg_video_mono)
    image_menu.monochrome.item_flags |= IT_TICKED;
  else
    image_menu.monochrome.item_flags &= ~(IT_TICKED);

  if (config.video.cfg & cfg_video_lock_aspect)
  {
    image_menu.lock_aspect.item_flags |= IT_TICKED;
    image_menu.pixel_aspect.icon_flags &= ~(IC_SHADED);
  }
  else
  {
    image_menu.lock_aspect.item_flags &= ~(IT_TICKED);
    image_menu.pixel_aspect.icon_flags |= IC_SHADED;
  }

  if (config.video.cfg & cfg_video_pixel_aspect)
    image_menu.pixel_aspect.item_flags |= IT_TICKED;
  else
    image_menu.pixel_aspect.item_flags &= ~(IT_TICKED);

  if (vmode->cfg & cfg_video_fit_screen)
    image_menu.size.item_flags |= IT_TICKED;
  else
    image_menu.size.item_flags &= ~(IT_TICKED);
}

static int image_menu_select(player_t* player, const int* lastHit)
{
  video_mode* vmode = &config.video.modes[player->mode];

  switch (*lastHit)
  {
    case MENU_IMAGE_MONOCHROME: // Mono/Colour
      do_command(player, CMD_COLOR_TOGGLE);
    break;

    case MENU_IMAGE_LOCKASPECT:
      config.video.cfg ^= cfg_video_lock_aspect;
      if (config.video.cfg & cfg_video_lock_aspect)
      { // may need resizing
        mt_open(player, NULL);
      }
    break;

    case MENU_IMAGE_PIXELASPECT:
      config.video.cfg ^= cfg_video_pixel_aspect;
      // may need resizing
      mt_open(player, NULL);
    break;

    case MENU_IMAGE_LOCKSIZE:
      vmode->cfg ^= cfg_video_fit_screen;
      if (vmode->cfg & cfg_video_fit_screen)
      { // may need resizing
        mt_open(player, NULL);
      }
    break;
  }

  return 0;
}

static int image_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("image_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static void deinterlace_menu_warning(player_t* player, const int* lastHit)
{
  player = player;

  lastHit = lastHit;

  for (int i = 0; i < KA_DEINTERLACE_MAX; i++)
    deinterlace_menu.method[i].item_flags &= ~(IT_TICKED); // clear any ticks

  deinterlace_menu.method[config.video.deinterlace].item_flags |= IT_TICKED;
}

static int deinterlace_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  player = player;

  if (hit >= 0)
    config.video.deinterlace = hit;

  return 0;
}

static int deinterlace_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("deinterlace_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static void resizemode_menu_warning(player_t* player, const int* lastHit)
{
  player = player;
  lastHit = lastHit;

  for (int i = 0; i < KA_RESIZE_MAX; i++)
    resizemode_menu.method[i].item_flags &= ~(IT_TICKED); // clear any ticks

  resizemode_menu.method[config.video.resizemode].item_flags |= IT_TICKED;
}

static int resizemode_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if ((hit >= 0) && (config.video.resizemode != hit))
  {
    config.video.resizemode = hit;

    player_adaptDisplay(player, player->mode, REDIM_CHANGE);
  }

  return 0;
}

static int resizemode_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("resizemode_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static void resizeto_menu_warning(player_t* player, const int* lastHit)
{
  player = player;
  lastHit = lastHit;

  for (int i = 0; i < KA_RESIZETO_MAX; i++)
    resizeto_menu.method[i].item_flags &= ~(IT_TICKED); // clear any ticks

  resizeto_menu.method[config.video.resizeto].item_flags |= IT_TICKED;
}

static int resizeto_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if ((hit >= 0) && (config.video.resizeto != hit))
  {
    config.video.resizeto = hit;

    if (config.video.resizemode != KA_RESIZE_NONE)
      player_adaptDisplay(player, player->mode, REDIM_CHANGE);
  }

  return 0;
}

static int resizeto_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("resizeto_mh", *lastHit ? 1 : 0, dst, dstsize);
}

//-------------------------------------------

static void pl_main_menu_warning(player_t* player, const int* lastHit)
{
  player = player;
  lastHit = lastHit;

  if (config.control & cfg_ctrl_random)
    pl_main_menu.random.item_flags |= IT_TICKED;
  else
    pl_main_menu.random.item_flags &= ~IT_TICKED;

  if (playlist_getmenu())
  {
    pl_main_menu.select.icon_flags &= ~(IC_SHADED);
    pl_main_menu.next.icon_flags &= ~(IC_SHADED);
    pl_main_menu.previous.icon_flags &= ~(IC_SHADED);
    pl_main_menu.random.icon_flags &= ~(IC_SHADED);
    pl_main_menu.close.icon_flags &= ~(IC_SHADED);

    pl_main_menu.select.sub = (int) playlist_getmenu();
  }
  else
  {
    pl_main_menu.select.icon_flags |= IC_SHADED;
    pl_main_menu.next.icon_flags |= IC_SHADED;
    pl_main_menu.previous.icon_flags |= IC_SHADED;
    pl_main_menu.random.icon_flags |= IC_SHADED;
    pl_main_menu.close.icon_flags |= IC_SHADED;

    pl_main_menu.select.sub = (int) &pl_empty_menu;
  }
}

static int pl_main_menu_select(player_t* player, const int* lastHit)
{
  switch (*lastHit)
  {
    case MENU_PLAYLIST_NEXT:
      playlist_play(player, PL_NEXT);
    break;

    case MENU_PLAYLIST_PREVIOUS:
      playlist_play(player, PL_PREVIOUS);
    break;

    case MENU_PLAYLIST_RANDOM:
      config.control ^= cfg_ctrl_random;
    break;

    case MENU_PLAYLIST_CLOSE:
      playlist_close();
    break;
  }

  return 0;
}

static int pl_main_menu_help(const int* lastHit, char* dst, int dstsize)
{
  return msg_lookup("playlist_mh", *lastHit, dst, dstsize);
}

//-------------------------------------------

static int playlist_menu_select(player_t* player, const int* lastHit)
{
  int hit = *lastHit;

  if (hit >= 0)
    playlist_play(player, hit);

  return 0;
}

static int playlist_menu_help(const int* lastHit, char* dst, int dstsize)
{
  lastHit = lastHit;

  return msg_lookup("playlist_mh", playlist_getmenu() ? 10 : 11, dst, dstsize);
}

//-------------------------------------------

static const menu_handlers_t handlers[] =
{ {       &main_menu,      ignore_menu_warning,        main_menu_select,        main_menu_help}
, {       &film_menu,        film_menu_warning,        film_menu_select,        film_menu_help}
, {    &streams_menu,      ignore_menu_warning,      ignore_menu_select,     streams_menu_help}
, {   &programs_menu,    programs_menu_warning,    programs_menu_select,    programs_menu_help}
, {   &astreams_menu,    astreams_menu_warning,    astreams_menu_select,    astreams_menu_help}
, {   &sstreams_menu,    sstreams_menu_warning,    sstreams_menu_select,    sstreams_menu_help}
, {   &vstreams_menu,    vstreams_menu_warning,    vstreams_menu_select,    vstreams_menu_help}
, {    &control_menu,     control_menu_warning,     control_menu_select,     control_menu_help}
, {     &titles_menu,      titles_menu_warning,      titles_menu_select,      titles_menu_help}
, {   &chapters_menu,    chapters_menu_warning,    chapters_menu_select,    chapters_menu_help}
, {       &zoom_menu,        zoom_menu_warning,        zoom_menu_select,        zoom_menu_help}
, {      &image_menu,       image_menu_warning,       image_menu_select,       image_menu_help}
, {&deinterlace_menu, deinterlace_menu_warning, deinterlace_menu_select, deinterlace_menu_help}
, { &resizemode_menu,  resizemode_menu_warning,  resizemode_menu_select,  resizemode_menu_help}
, {   &resizeto_menu,    resizeto_menu_warning,    resizeto_menu_select,    resizeto_menu_help}
, {    &pl_main_menu,     pl_main_menu_warning,     pl_main_menu_select,     pl_main_menu_help}
, {   &pl_empty_menu,      ignore_menu_warning,    playlist_menu_select,    playlist_menu_help}
, {NULL, NULL, NULL, NULL}
};

/**
 * Select the handlers associated to a menu.
 *
 * @param  menu   Pointer to menu.
 *
 * @returns The handlers associated to the menu.
 */
static const menu_handlers_t* menu_selectHandler(void* menu)
{
  const menu_handlers_t* h = handlers;
  if (menu == playlist_getmenu())
    menu = &pl_empty_menu;

  for (; h != NULL; h++)
  {
    if (h->menu == menu)
      return h;;
  }

  return NULL;
}

/**
 * Find (sub)menu concerned by EVENT_MENUSELECT, MESSAGE_HELPREQUEST or MESSAGE_MENUWARNING
 *
 * @param  hit    hit list like returned by
 * @param  mode   EVENT_MENUSELECT or EMSG_MENUWARNING.
 * @param  pmenu  Adress of menu pointer to fill in.
 *
 * @returns  last hit.
 */
static int* menu_parse_hit(int* hit, int mode, menu_t** pmenu)
{
  menu_t* menu = context.menu;

  if (mode == MESSAGE_MENUWARNING)
  {
    hit += 3;

    // find the (sub)menu of the item refered by the hit list
    for (; *hit != -1; hit++)
    {
      const menu_item_t* item = menu->items + *hit;
      menu = (menu_t*) item->sub;
    }
  }
  else
  {
    // find the parent (sub)menu of the item refered by the before last hit
    if (*hit != -1)
    {
      for (; hit[1] != -1; hit++)
      {
        const menu_item_t* item = menu->items + *hit;
        if (item->sub != -1)
          menu = (menu_t*) item->sub;
      }

      hit++;
    }
  }

  *pmenu = menu;

  return hit - 1;
}

//-------------------------------------------
// External functions
//-------------------------------------------

/*
 * Force the refresh of the content of the current menu.
 *
 * @param  player  Pointer to player.
 * @param  b       Pointer to selection block (hit list) or NULL.
 */
void menu_refresh(player_t* player, const int* hit)
{
  if (context.menu == NULL)
    return;

  if (hit == NULL)
  {
    context.hit[0] = -1;

    _swix(Wimp_GetMenuState, _INR(0,1), 0, context.hit);
  }
  else
  {
    for (int i = 0; i < 10; i++)
    {
      context.hit[i] = hit[i];
      if (hit[i] == -1)
        break;
    }
  }

  if (context.hit[0] == -1)
  {
    menu_warning(player, (int*) &context.menu);
  }
  else
  {
    for (int i = 0; i < 10; i++)
    {
      int sel = context.hit[i];
      if (sel == -1)
        break;

      context.hit[i] = -1;
      menu_warning(player, (int*) &context.menu);
      context.hit[i] = sel;
    }
  }

  _swix(Wimp_CreateMenu, _INR(1,3), context.menu, context.x, context.y);
}

/*
 * A (sub)menu is about to be opened and its contents must be updated.
 *
 * @param  player  Pointer to player.
 * @param  b       Pointer to selection block (menu, x, y, hit list)
 */
void menu_warning(player_t* player, int* b)
{
  menu_t* menu;
  int* lastHit = menu_parse_hit(b, MESSAGE_MENUWARNING, &menu);
  const menu_handlers_t* h = menu_selectHandler(menu);

  h->FNmenuWarning(player, lastHit);

  if (b[3] == -1)
  {
    context.menu = menu;
    _swix(Wimp_CreateMenu, _INR(1,3), menu, b[1], b[2]);
  }
  else
    _swix(Wimp_CreateSubMenu, _INR(1,3), menu, b[1], b[2]);
}

/*
 * Provide help on the menu under the pointer.
 *
 * @param  player  Pointer to player.
 * @param  dst     Pointer to string to fill in.
 * @param  dstlen  Max size of string.
 */
int menu_help(int w, int i, char* dst, int dstlen)
{
  if (context.menu == NULL)
    return 0;

  context.hit[0] = -1;

  _swix(Wimp_GetMenuState, _INR(0,3), 1, context.hit, w, i);

  menu_t* menu;
  int* lastHit = menu_parse_hit(context.hit, MESSAGE_HELPREQUEST, &menu);
  const menu_handlers_t* h = menu_selectHandler(menu);

  return h->FNmenuHelp(lastHit, dst, dstlen);
}

/*
 * A menu selection has been made and must be handled.
 *
 * @param  player  Pointer to player.
 * @param  b       Pointer to selection block (hit list)
 *
 * @returns  Normally 0, but -1 for Quit.
 */
int menu_select(player_t* player, int* b)
{
  int blk[5];
  menu_t* menu;
  int* lastHit = menu_parse_hit(b, 9, &menu);
  const menu_handlers_t* h = menu_selectHandler(menu);

  int ret = h->FNmenuSelect(player, lastHit);
  if (ret) return ret;

  update_controls(player);

  _swix(Wimp_GetPointerInfo, _IN(1), blk);

  if (blk[2] == 1) // adjust
    menu_refresh(player, b);

  return 0;
}

/**
 * Opens a menu.
 *
 * @param  player  Pointer to player.
 * @param  menuId  Id of menu to open.
 */
void menu_open(player_t* player, int menuId, int x, int y)
{
  context.id = menuId;
  switch (context.id)
  {
    case MENU_MAIN: context.menu = (menu_t*) &main_menu; break;
    case MENU_PLAYLIST:
      context.menu = (playlist_getmenu()) ? (menu_t*) playlist_getmenu() : (menu_t*) &pl_empty_menu;
    break;
  }

  context.x = x;
  context.y = y;
  context.hit[0] = -1;

  menu_warning(player, (int*) &context.menu);

  _swix(Wimp_CreateMenu, _INR(1,3), context.menu, x, y);
}

/**
 * Signal that menus are closed.
 */
void menu_deleted(void)
{
  context.id = 0;
  context.menu = NULL;
}

/**
 * Get text for deinterlace mode from menus.
 */
const char* menu_getdeinterlace(void)
{
  return deinterlace_menu.method[config.video.deinterlace].text;
}
