/*
 * menu.c: Main Menu
 *
 * See the main source file 'xineliboutput.c' for copyright information and
 * how to reach the author.
 *
 * $Id$
 *
 */

#include "features.h"

#include <dirent.h>

#ifdef HAVE_LIBBLURAY
# include <libbluray/bluray.h>
#endif

#include <vdr/config.h>
#include <vdr/interface.h>
#include <vdr/menu.h>
#include <vdr/plugin.h>
#include <vdr/videodir.h>
#include <vdr/i18n.h>

#include "logdefs.h"
#include "config.h"
#include "menu.h"
#include "menuitems.h"
#include "tools/metainfo_menu.h"
#include "tools/playlist.h"
#include "device.h"
#include "media_player.h"
#include "equalizer.h"

#ifndef HOTKEY_START
# define HOTKEY_START        kRed

# define HOTKEY_DVD          k0    /* */
# define HOTKEY_DVD_TRACK1   k1    /* */
# define HOTKEY_RESERVED     k2    /* */

# define HOTKEY_NEXT_ASPECT  k3    /* auto, 4:3, 16:9 */
# define HOTKEY_TOGGLE_CROP  k4    /* off, force, auto */
# define HOTKEY_UPMIX        k5    /* off, on */
# define HOTKEY_DOWNMIX      k6    /* off, on */
# define HOTKEY_DEINTERLACE  k7    /* off, on */
# define HOTKEY_LOCAL_FE     k8    /* off, on */

# define HOTKEY_PLAYLIST     k9    /* Start replaying playlist or file pointed by
                                      symlink $(CONFDIR)/plugins/xineliboutput/default_playlist */
# define HOTKEY_ADELAY_UP    kUp   /* audio delay up */
# define HOTKEY_ADELAY_DOWN  kDown /* audio delay down */
# define HOTKEY_TOGGLE_VO_ASPECT kRight
#endif

//#define OLD_TOGGLE_FE

#define ISNUMBERKEY(k) (RAWKEY(k) >= k0 && RAWKEY(k) <= k9)

//----------------------------- cMenuBluray ----------------------------------

static bool BlurayMenuSupported(const cString& Path)
{
  bool result = false;
#ifdef HAVE_LIBBLURAY
  BLURAY *bdh = bd_open(Path, NULL);
  if (bdh) {
    const BLURAY_DISC_INFO *di = bd_get_disc_info(bdh);
    if (di->bluray_detected && !di->num_unsupported_titles)
      result = true;
    bd_close(bdh);
  }
#endif
  return result;
}

class cMenuBluray : public cOsdMenu
{
  private:
    cXinelibDevice *m_Dev;

    cString m_Path;

  public:
    cMenuBluray(cXinelibDevice *Dev, const char *Path);

    virtual ~cMenuBluray() {};

    virtual eOSState ProcessKey(eKeys Key);
};

cMenuBluray::cMenuBluray(cXinelibDevice *Dev, const char *Path) : cOsdMenu("BluRay")
{
  m_Dev = Dev;
  m_Path = Path;
  Add(new cOsdItem(tr("Play movie title"), osUser1));
  Add(new cOsdItem(tr("Play disc"),        osUser2));
  Display();
}

eOSState cMenuBluray::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);
  switch (state) {
    case osUser1:
      cPlayerFactory::Launch(m_Dev, pmAudioVideo,
                             cPlaylist::BuildMrl("bluray", *m_Path),
                             NULL, true);
      return osEnd;
    case osUser2:
      cPlayerFactory::Launch(m_Dev, pmAudioVideo,
                             cPlaylist::BuildMrl("bd", *m_Path),
                             NULL, true);
      return osEnd;
    case osBack:
    case osEnd:   return osEnd;
    default:      break;
  }
  return state;
}

//--------------------------- cMenuBrowseFiles -------------------------------

class cMenuBrowseFiles : public cOsdMenu
{
  protected:
    cXinelibDevice *m_Dev;

    const eMainMenuMode m_Mode;
    bool          m_OnlyQueue;
    cString       m_CurrentDir;
    char         *m_ConfigLastDir;
    const char   *help[4];

    virtual bool     ScanDir(const char *DirName);
    virtual eOSState Open(bool ForceOpen = false, bool Queue = false, bool Rewind = false);
    virtual eOSState Delete(void);
    virtual eOSState Info(void);
    virtual void     Set(void);
    virtual void     SetHelpButtons(void);
    cFileListItem   *GetCurrent(void) { return (cFileListItem *)Get(Current()); }
    void             StoreConfig(void);
    char            *GetLastDir(void);

  public:
    cMenuBrowseFiles(cXinelibDevice *Dev, eMainMenuMode mode = ShowFiles, bool OnlyQueue=false);
    ~cMenuBrowseFiles();

    virtual eOSState ProcessKey(eKeys Key);
};

static cString ParentDir(const char *dir)
{
  char *result = strdup(dir);
  char *pt = strrchr(result, '/');
  if (pt) {
    *(pt+1)=0;
    if (pt != result)
      *pt = 0;
  }

  return cString(result, true);
}

static cString LastDir(const char *dir)
{
  const char *pt = strrchr(dir, '/');
  if (pt && pt[0] && pt[1])
    return cString(pt+1);
  return cString(NULL);
}

cMenuBrowseFiles::cMenuBrowseFiles(cXinelibDevice *Dev, eMainMenuMode mode, bool OnlyQueue) :
    cOsdMenu( ( mode==ShowImages ? tr("Images") :
                mode==ShowMusic ? (!OnlyQueue ? tr("Play music") : tr("Add to playlist")) :
                /*mode==ShowFiles ?*/ tr("Play file")),
              2, 4),
    m_Mode(mode)
{
  m_Dev = Dev;
  m_OnlyQueue  = OnlyQueue;

  m_ConfigLastDir = GetLastDir();
  Set();
}

cMenuBrowseFiles::~cMenuBrowseFiles()
{
  cPlugin *p = cPluginManager::GetPlugin(PLUGIN_NAME_I18N);
  if (p) {
    p->SetupStore("Media.RootDir", xc.media_root_dir);
  }

  Setup.Save();
}

char *cMenuBrowseFiles::GetLastDir(void)
{
  switch (m_Mode) {
    case ShowMusic:  return xc.browse_music_dir;
    case ShowImages: return xc.browse_images_dir;
    case ShowFiles:  return xc.browse_files_dir;
    default: break;
  }
  return xc.browse_files_dir;
}

void cMenuBrowseFiles::Set(void)
{
  Clear();

  if (!*m_CurrentDir)
    m_CurrentDir = m_ConfigLastDir;

  int RootDirLen = strlen(xc.media_root_dir);
  if (strncmp(m_CurrentDir, xc.media_root_dir, RootDirLen)) {
    LOGMSG("Not allowing browsing to %s (root is %s)", *m_CurrentDir, xc.media_root_dir);
    m_CurrentDir = xc.media_root_dir;
  }

  if (m_CurrentDir[0] != '/') {
#if defined(APIVERSNUM) && (APIVERSNUM < 20102)
    m_CurrentDir = VideoDirectory;
#else
    m_CurrentDir = cVideoDirectory::Name();
#endif
  }

  // find deepest accessible directory from path
  while (!ScanDir(m_CurrentDir) && strlen(m_CurrentDir) > 1) {
    m_CurrentDir = ParentDir(m_CurrentDir);
  }

  // add link to parent folder
  int CurrentDirLen = strlen(m_CurrentDir);
  if (CurrentDirLen > 1 && CurrentDirLen > RootDirLen)
    Add(new cFileListItem("..",true));

  Sort();

  SetCurrent(Get(Count()>1 && CurrentDirLen>1 ? 1 : 0));

  // select last selected item

  cString lastParent = ParentDir(m_ConfigLastDir);
  if (!strncmp(m_CurrentDir, lastParent, CurrentDirLen)) {
    cString item = LastDir(m_ConfigLastDir);
    if (*item) {
      for (cFileListItem *it = (cFileListItem*)First(); it; it = (cFileListItem*)Next(it))
        if (!strcmp(it->Name(), item))
          SetCurrent(it);
    }
  }

  strn0cpy(m_ConfigLastDir, m_CurrentDir, sizeof(xc.browse_files_dir));
  StoreConfig();

  SetHelpButtons();
  Display();
}

void cMenuBrowseFiles::StoreConfig(void)
{
  cPlugin *p = cPluginManager::GetPlugin(PLUGIN_NAME_I18N);
  if (p) {
    p->SetupStore("Media.BrowseMusicDir",  xc.browse_music_dir);
    p->SetupStore("Media.BrowseFilesDir",  xc.browse_files_dir);
    p->SetupStore("Media.BrowseImagesDir", xc.browse_images_dir);
#if 1
    // delete old keys (<1.0.0)
    p->SetupStore("BrowseMusicDir");
    p->SetupStore("BrowseFilesDir");
    p->SetupStore("BrowseImagesDir");
#endif
  }
}

void cMenuBrowseFiles::SetHelpButtons(void)
{
  bool isDir  = !GetCurrent() || GetCurrent()->IsDir();
  bool isFile = !isDir;
  bool bDel   = isFile && xc.media_enable_delete;

  if (isDir && !strcmp("..", GetCurrent()->Name())) {
    help[0] = help[1] = help[2] = help[3] = NULL;
  } else if (m_Mode == ShowMusic) {
    help[0] = isDir  ? trVDR("Button$Play")   : NULL;
    help[1] =          tr   ("Button$Queue");
    help[2] = bDel   ? trVDR("Button$Delete") : NULL;
    help[3] = isFile ? trVDR("Button$Info")   : NULL;
  } else if (m_Mode == ShowImages) {
    help[0] = isDir  ? trVDR("Button$Play")   : NULL;
    help[1] =                                   NULL;
    help[2] = bDel   ? trVDR("Button$Delete") : NULL;
    help[3] = isFile ? trVDR("Button$Info")   : NULL;
  } else {
    bool isDvd     = GetCurrent() && (GetCurrent()->IsDvd() || GetCurrent()->IsBluRay());
    bool hasResume = GetCurrent() && GetCurrent()->HasResume();

    help[0] = isDir && isDvd  ? trVDR("Button$Open")   : NULL;
    help[1] = hasResume       ? trVDR("Button$Rewind") : NULL;
    help[2] = bDel            ? trVDR("Button$Delete") : NULL;
    help[3] = isFile          ? trVDR("Button$Info")   : NULL;
  }

  SetHelp(help[0], help[1], help[2], help[3]);
}

eOSState cMenuBrowseFiles::Delete(void)
{
  cFileListItem *it = GetCurrent();
  if (!it->IsDir()) {
    if (Interface->Confirm(trVDR("Delete recording?"))) {
      cString name = cString::sprintf("%s/%s", (const char *)m_CurrentDir, it->Name());
      if (!unlink(name)) {
        isyslog("file %s deleted", *name);
        if (m_Mode != ShowImages) {
          name = cString::sprintf("%s.resume", *name);
          unlink(name);
        }
        cOsdMenu::Del(Current());
        SetHelpButtons();
        Display();
      } else {
        Skins.Message(mtError, trVDR("Error while deleting recording!"));
        isyslog("Error deleting file %s", *name);
      }
    }
  }
  return osContinue;
}

eOSState cMenuBrowseFiles::Open(bool ForceOpen, bool Queue, bool Rewind)
{
  if (!GetCurrent()) {
    return osContinue;
  }

  /* parent directory */
  if (!strcmp("..", GetCurrent()->Name())) {
    m_CurrentDir = ParentDir(m_CurrentDir);
    Set();
    return osContinue;

  /* directory */
  } else if (GetCurrent()->IsDir()) {

    if (!ForceOpen && GetCurrent()->IsDvd()) {
      /* play dvd */
      cPlayerFactory::Launch(m_Dev, pmAudioVideo,
                             cPlaylist::BuildMrl("dvd", *m_CurrentDir, "/", GetCurrent()->Name()),
                             NULL, true);
      return osEnd;
    }
    if (!ForceOpen && GetCurrent()->IsBluRay()) {
      cString bd_path = cString::sprintf("%s/%s/", *m_CurrentDir, GetCurrent()->Name());
      if (BlurayMenuSupported(bd_path)) {
        AddSubMenu(new cMenuBluray(m_Dev, bd_path));
        return osContinue;
      }
      /* play BluRay disc/image */
      cPlayerFactory::Launch(m_Dev, pmAudioVideo,
                             cPlaylist::BuildMrl("bluray", *m_CurrentDir, "/", GetCurrent()->Name(), "/"),
                             NULL, true);
      return osEnd;
    }
    if (ForceOpen && GetCurrent()->IsDir() &&
        !GetCurrent()->IsDvd() &&
        !GetCurrent()->IsBluRay()) {
      /* play all files */
      if (m_Mode != ShowImages) {

        if (m_OnlyQueue && !Queue)
          return osContinue;

        cString f = cString::sprintf("%s/%s/", *m_CurrentDir, GetCurrent()->Name());

        if (!Queue || !cPlayerFactory::IsOpen())
          cControl::Shutdown();
        if (Queue)
          cPlayerFactory::Queue(m_Dev, f);
        else
          cPlayerFactory::Launch(m_Dev, m_Mode == ShowFiles ? pmAudioVideo : pmAudioOnly, f, NULL, true);
        return Queue ? osContinue : osEnd;

      } else {
        // TODO: show all images
      }
    }

    /* go to directory */
    const char *d = GetCurrent()->Name();
    char *buffer = NULL;
    if (asprintf(&buffer, "%s/%s", *m_CurrentDir, d) >= 0) {
      while (buffer[0] == '/' && buffer[1] == '/')
        memmove(buffer, buffer+1, strlen(buffer));
      m_CurrentDir = cString(buffer, true);
    }
    Set();
    return osContinue;

  /* regular file */
  } else {
    cString f = cString::sprintf("%s/%s", *m_CurrentDir, GetCurrent()->Name());

    strn0cpy(m_ConfigLastDir, f, sizeof(xc.browse_files_dir));
    StoreConfig();

    if (m_Mode != ShowImages) {
      /* video/audio */
      if (m_OnlyQueue && !Queue)
        return osContinue;
      if (!Queue || !cPlayerFactory::IsOpen())
        cControl::Shutdown();
      if (Queue)
        cPlayerFactory::Queue(m_Dev, f);
      if (!cPlayerFactory::IsOpen()) {
        if (Rewind)
          unlink(cString::sprintf("%s.resume", *f));

        if (GetCurrent()->IsBluRay()) {
          AddSubMenu(new cMenuBluray(m_Dev, f));
          return osContinue;
        }
        if (GetCurrent()->IsDvd())
          cPlayerFactory::Launch(m_Dev, pmAudioVideo, cPlaylist::BuildMrl("dvd", f), NULL, true);
        else
          cPlayerFactory::Launch(m_Dev, m_Mode == ShowFiles ? pmAudioVideo : pmAudioOnly,
                                 f, GetCurrent()->SubFile(), true);
      }
      if (Queue)
        return osContinue;
    } else {
      /* image */
      cPlaylist *Playlist = new cPlaylist();
      for (cFileListItem *it = (cFileListItem*)First(); it; it=(cFileListItem*)Next(it)) {
        if (!it->IsDir())
          Playlist->Read(cString::sprintf("%s/%s", *m_CurrentDir, it->Name()));
        if (it == Get(Current()))
          Playlist->SetCurrent(Playlist->Last());
      }
      cPlayerFactory::Launch(m_Dev, pmVideoOnly, Playlist, true);
    }
    return osEnd;
  }
  return osContinue;
}

eOSState cMenuBrowseFiles::Info(void)
{
  if (GetCurrent() && !GetCurrent()->IsDir()) {
    cString filename = cString::sprintf("%s/%s", *m_CurrentDir, GetCurrent()->Name());
    return AddSubMenu(new cMetainfoMenu(filename));
  }
  return osContinue;
}

bool cMenuBrowseFiles::ScanDir(const char *DirName)
{
  DIR *d = opendir(DirName);
  if (d) {
    struct dirent *e;
    while ((e = readdir(d)) != NULL) {
      if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..") &&
          (e->d_name[0] != '.' || xc.show_hidden_files)) {
        cString buffer = cString::sprintf("%s/%s", DirName, e->d_name);
        struct stat st;
        if (stat(buffer, &st) == 0) {

          // check symlink destination
          if (S_ISLNK(st.st_mode)) {
            buffer = ReadLink(buffer);
            if (!*buffer || stat(buffer, &st))
              continue;
          }

          // folders
          if (S_ISDIR(st.st_mode)) {

            if (m_Mode == ShowImages || m_Mode == ShowMusic)
              Add(new cFileListItem(e->d_name, true));
            else
              Add(new cFileListItem(e->d_name, true, false, NULL,
                                    xc.IsDvdFolder(buffer), xc.IsBluRayFolder(buffer)));

          // regular files
          } else if (e->d_name[0] != '.') {

            // audio
            if (m_Mode == ShowMusic && xc.IsAudioFile(buffer)) {
              Add(new cFileListItem(e->d_name, false));

            // images
            } else if (m_Mode == ShowImages && xc.IsImageFile(buffer)) {
              Add(new cFileListItem(e->d_name, false));

            // BluRay image (.iso)
            } else if (m_Mode == ShowFiles && xc.IsBluRayImage(buffer)) {
              Add(new cFileListItem(e->d_name, false, false, NULL, false, true));

            // DVD image (.iso)
            } else if (m_Mode == ShowFiles && xc.IsDvdImage(buffer)) {
              Add(new cFileListItem(e->d_name, false, false, NULL, true));

            // video
            } else if (m_Mode == ShowFiles && xc.IsVideoFile(buffer)) {
              cString subfile;
              cString resumefile;

              // separate subtitles ?
              cString basename = cString::sprintf("%s/%s", DirName, e->d_name);
              const char *p = strrchr(basename, '.');
              if (p)
                basename.Truncate(p - basename);
              int i;
              for (i=0; xc.s_subExts[i] && !*subfile; i++) {
                cString tmp = cString::sprintf("%s%s", *basename, xc.s_subExts[i]);
                if (stat(tmp, &st) == 0)
                  subfile = tmp;
              }

              // resume file ?
              resumefile = cString::sprintf("%s/%s.resume", DirName, e->d_name);
              if (stat(resumefile, &st) != 0)
                resumefile = NULL;

              Add(new cFileListItem(e->d_name, false, *resumefile, subfile));
            }
          }
        }
      }
    }
    closedir(d);
    return true;
  }
  return false;
}

eOSState cMenuBrowseFiles::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);

  if (state == osUnknown) {
    switch (Key) {
      case kPlay:
      case kOk:     return Open(false, m_OnlyQueue);
      case kRed:    if (help[0])
                      return Open(true);
                    break;
      case kGreen:  if (help[1])
                      return Open(true,
                                  m_Mode == ShowMusic ? (m_OnlyQueue=true) : false,
                                  m_Mode != ShowMusic);
                    break;
      case kYellow: if (help[2])
                      return Delete();
                    break;
      case kBlue:   if (help[3])
                      return Info();
                    break;
      default: break;
    }
  }

  if (state == osUnknown)
    state = osContinue;

  if (!HasSubMenu() && Key != kNone)
    SetHelpButtons();

  return state;
}


//----------------------------- cMenuXinelib ---------------------------------


#include "tools/display_message.h"

cMenuXinelib::cMenuXinelib(cXinelibDevice *Dev)
{
  m_Dev = Dev;
  compression = xc.audio_compression;
  autocrop = xc.autocrop;
  overscan = xc.overscan;

  hotkey_state = hkInit;

  novideo = m_Dev->GetPlayMode() == pmAudioOnlyBlack ? 1 : 0;

  audio_ctrl_compress = NULL;
  ctrl_autocrop = NULL;
  ctrl_overscan = NULL;
  ctrl_novideo = NULL;

  Add(SeparatorItem(tr("Media")));
  if (xc.media_menu_items & MEDIA_MENU_FILES)
    Add(SubMenuItem(tr("Play file"),        osUser1));
  if (xc.media_menu_items & MEDIA_MENU_MUSIC)
    Add(SubMenuItem(tr("Play music"),       osUser2));
  if (xc.media_menu_items & MEDIA_MENU_IMAGES)
    Add(SubMenuItem(tr("View images"),      osUser3));
  if (xc.media_menu_items & MEDIA_MENU_DVD)
    Add(SubMenuItem(tr("Play DVD disc"),    osUser4));
  if (xc.media_menu_items & MEDIA_MENU_BLURAY)
    Add(SubMenuItem(tr("Play BluRay disc"), osUser5));
  if (xc.media_menu_items & MEDIA_MENU_CD)
    Add(SubMenuItem(tr("Play audio CD"),    osUser6));

  if (xc.media_menu_items & MEDIA_MENU_VIDEO_SETUP) {
    Add(SeparatorItem(tr("Video settings")));
    Add(ctrl_novideo = new cMenuEditBoolItem(tr("Play only audio"),
                                             &novideo));
    Add(ctrl_autocrop = new cMenuEditBoolItem(tr("Crop letterbox 4:3 to 16:9"),
                                              &autocrop));
    Add(ctrl_overscan = new cMenuEditTypedIntItem(tr("Overscan (crop image borders)"), "%",
                                                  &overscan, 0, 10,
                                                  tr("Off")));
  }

  if (xc.media_menu_items & MEDIA_MENU_AUDIO_SETUP) {
    Add(SeparatorItem(tr("Audio settings")));
    Add(audio_ctrl_compress = new cMenuEditTypedIntItem(tr("Audio Compression"), "%",
                                                        &compression, 100, 500, NULL, tr("Off")));
    Add(SubMenuItem(tr("Audio equalizer"), osUser7));
  }

  switch (xc.main_menu_mode) {
    case ShowFiles:
    case ShowMusic:
    case ShowImages: AddSubMenu(new cMenuBrowseFiles(m_Dev, xc.main_menu_mode)); break;
    default: break;
  }

  xc.main_menu_mode = ShowMenu;
}

cMenuXinelib::~cMenuXinelib()
{
  if (xc.audio_compression != compression)
    m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay,
                                   xc.audio_compression, xc.audio_equalizer,
                                   xc.audio_surround, xc.speaker_type);

  if (xc.overscan != overscan)
    m_Dev->ConfigureVideo(xc.hue, xc.saturation, xc.brightness, xc.sharpness,
                          xc.noise_reduction, xc.contrast, xc.overscan,
                          xc.vo_aspect_ratio);

  if (xc.autocrop != autocrop)
    m_Dev->ConfigurePostprocessing("autocrop",
                                   xc.autocrop ? true : false,
                                   xc.AutocropOptions());

  int dev_novideo = m_Dev->GetPlayMode() == pmAudioOnlyBlack ? 1 : 0;
  if (dev_novideo != novideo)
    m_Dev->SetPlayMode(novideo ? pmAudioOnlyBlack : pmNone);
}

cOsdMenu *cMenuXinelib::CreateMenuBrowseFiles(cXinelibDevice *Dev, eMainMenuMode mode, bool Queue)
{
  return new cMenuBrowseFiles(Dev, mode, true);
}

eOSState cMenuXinelib::ProcessKey(eKeys Key)
{
  /* Hot key support */
  if (hotkey_state == hkInit && Key == kNone)
    return osContinue;
  if (hotkey_state == hkInit && Key == HOTKEY_START) {
    hotkey_state = hkSeen;
    return osContinue;
  } else if (hotkey_state == hkSeen && Key != kNone) {
    hotkey_state = hkNone;
    return ProcessHotkey(Key);
  }
  hotkey_state = hkNone;

  cOsdItem *item = Get(Current());

  eOSState state = cMenuSetupPage::ProcessKey(Key);

  if (HasSubMenu())
    return state;

  switch (state) {
    case osUser1:
      AddSubMenu(new cMenuBrowseFiles(m_Dev, ShowFiles));
      return osUnknown;
    case osUser2:
      AddSubMenu(new cMenuBrowseFiles(m_Dev, ShowMusic));
      return osUnknown;
    case osUser3:
      AddSubMenu(new cMenuBrowseFiles(m_Dev, ShowImages));
      return osContinue;
    case osUser4:
      cPlayerFactory::Launch(m_Dev, "dvd:/");
      return osEnd;
    case osUser5:
      AddSubMenu(new cMenuBluray(m_Dev, NULL));
      return osContinue;
    case osUser6:
      cPlayerFactory::Launch(m_Dev, "cdda:/");
      return osEnd;
    case osUser7:
      if (!xc.pending_menu_action) {
        xc.pending_menu_action = new cEqualizer(m_Dev);
        return osPlugin;
      }
      return osContinue;
    default: ;
  }

  Key = NORMALKEY(Key);

  if (Key == kLeft || Key == kRight || ISNUMBERKEY(Key)) {
    if (item == audio_ctrl_compress)
      m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay,
                                     compression, xc.audio_equalizer,
                                     xc.audio_surround, xc.speaker_type);
    else if (item == ctrl_overscan)
      m_Dev->ConfigureVideo(xc.hue, xc.saturation, xc.brightness, xc.sharpness,
                            xc.noise_reduction, xc.contrast, overscan,
                            xc.vo_aspect_ratio);
  }
  if (Key == kLeft || Key == kRight) {
    if (item == ctrl_autocrop)
      m_Dev->ConfigurePostprocessing("autocrop", autocrop?true:false,
                                     xc.AutocropOptions());
    else if (item == ctrl_novideo)
      m_Dev->SetPlayMode(novideo ? pmAudioOnlyBlack : pmNone);
  }

  return state;
}

void cMenuXinelib::Store(void)
{
  xc.audio_compression = compression;
  xc.autocrop = autocrop;
  xc.overscan = overscan;
}

eOSState cMenuXinelib::ProcessHotkey(eKeys Key)
{
  eOSState NewState = osEnd;
  cString  Message;
  time_t   now      = time(NULL);
  bool     OnlyInfo = ((xc.last_hotkey_time < now-3) || xc.last_hotkey != Key);

  switch (Key) {
    case HOTKEY_DVD:
      cPlayerFactory::Launch(m_Dev, "dvd:/");
      break;

    case HOTKEY_DVD_TRACK1:
      cPlayerFactory::Launch(m_Dev, "dvd:/1");
      break;

    case HOTKEY_LOCAL_FE:
      /* off, on */
      {
        int local_frontend = strstra(xc.local_frontend, xc.s_frontends, 0);

#ifndef OLD_TOGGLE_FE
        if (local_frontend==FRONTEND_NONE)
          // no need to show current frontend if there is no output device ...
          OnlyInfo = false;
#endif
        if (!OnlyInfo) {
#ifndef OLD_TOGGLE_FE
          static int orig_frontend = -1;
          if (orig_frontend < 0)
            orig_frontend = local_frontend;

          if (orig_frontend == FRONTEND_NONE) {
            // no frontends were loaded at startup -> loop thru all frontends
            local_frontend++;
          } else {
            // frontend was loaded at startup -> toggle it on/off
            if (local_frontend == FRONTEND_NONE)
              local_frontend = orig_frontend;
            else
              local_frontend = FRONTEND_NONE;
          }
#else
          local_frontend++;
#endif
          if (local_frontend >= FRONTEND_count)
            local_frontend = 0;
          strn0cpy(xc.local_frontend, xc.s_frontends[local_frontend], sizeof(xc.local_frontend));
          m_Dev->ConfigureWindow(
              xc.fullscreen, xc.width, xc.height, xc.modeswitch, xc.modeline,
              xc.display_aspect, xc.scale_video);
        }
        Message = cString::sprintf("%s %s %s", tr("Local Frontend"),
                                   OnlyInfo ? ":" : "->",
                                   xc.s_frontendNames[local_frontend]);
      }
      break;

    case HOTKEY_NEXT_ASPECT:
      /* auto, 4:3, 16:9, ... */
      if (!OnlyInfo) {
        xc.display_aspect = (xc.display_aspect < ASPECT_count-1) ? xc.display_aspect+1 : 0;
        m_Dev->ConfigureWindow(xc.fullscreen, xc.width, xc.height,
                               xc.modeswitch, xc.modeline, xc.display_aspect,
                               xc.scale_video);
      }
      Message = cString::sprintf("%s %s %s", tr("Aspect ratio"),
                                 OnlyInfo ? ":" : "->",
                                 tr(xc.s_aspects[xc.display_aspect]));
      break;

    case HOTKEY_TOGGLE_VO_ASPECT:
      /* auto, square, 4:3, anamorphic or DVB */
      if (!OnlyInfo) {
        xc.vo_aspect_ratio = (xc.vo_aspect_ratio < VO_ASPECT_count-1) ? xc.vo_aspect_ratio + 1 : 0;
        m_Dev->ConfigureVideo(xc.hue, xc.saturation, xc.brightness, xc.sharpness,
                              xc.noise_reduction, xc.contrast, xc.overscan,
                              xc.vo_aspect_ratio);
      }
      Message = cString::sprintf("%s %s %s", tr("Video aspect ratio"),
                                 OnlyInfo ? ":" : "->",
                                 tr(xc.s_vo_aspects[xc.vo_aspect_ratio]));
      break;

    case HOTKEY_TOGGLE_CROP:
      /* off, force, auto */
      if (!OnlyInfo) {
        if (!xc.autocrop) {
          xc.autocrop = 1;
          xc.autocrop_autodetect = 1;
        } else if (xc.autocrop_autodetect) {
          xc.autocrop_autodetect = 0;
        } else {
          xc.autocrop = 0;
        }
        m_Dev->ConfigurePostprocessing("autocrop",
                                       xc.autocrop ? true : false,
                                       xc.AutocropOptions());
      }

      Message = cString::sprintf("%s %s %s", tr("Crop letterbox 4:3 to 16:9"),
                                 OnlyInfo ? ":" : "->",
                                 !xc.autocrop ? tr("Off") : xc.autocrop_autodetect ? tr("automatic") : tr("On"));
      break;

    case HOTKEY_DEINTERLACE:
      {
        /* off, on */
        int off = !strcmp(xc.deinterlace_method, "none");
        if (!OnlyInfo) {
          off = !off;
          if (off)
            strcpy(xc.deinterlace_method, "none");
          else
            strcpy(xc.deinterlace_method, "tvtime");
          m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay,
                                         compression, xc.audio_equalizer,
                                         xc.audio_surround, xc.speaker_type);
        }
        Message = cString::sprintf("%s %s %s", tr("Deinterlacing"),
                                   OnlyInfo ? ":" : "->",
                                   tr(off ? "Off":"On"));
      }
      break;

    case HOTKEY_UPMIX:
      /* off, on */
      if (!OnlyInfo) {
        xc.audio_upmix = xc.audio_upmix ? 0 : 1;
        m_Dev->ConfigurePostprocessing("upmix", xc.audio_upmix ? true : false, NULL);
      }
      Message = cString::sprintf("%s %s %s",
                                 tr("Upmix stereo to 5.1"),
                                 OnlyInfo ? ":" : "->",
                                 tr(xc.audio_upmix ? "On" : "Off"));
      break;

    case HOTKEY_DOWNMIX:
      /* off, on */
      if (!OnlyInfo) {
        xc.audio_surround = xc.audio_surround ? 0 : 1;
        m_Dev->ConfigurePostprocessing(
            xc.deinterlace_method, xc.audio_delay, xc.audio_compression,
            xc.audio_equalizer, xc.audio_surround, xc.speaker_type);
      }
      Message = cString::sprintf("%s %s %s",
                                 tr("Downmix AC3 to surround"),
                                 OnlyInfo ? ":" : "->",
                                 tr(xc.audio_surround ? "On":"Off"));
      break;

      case HOTKEY_PLAYLIST:
        /* Start replaying playlist or file pointed by
           symlink $(CONFDIR)/plugins/xineliboutput/default_playlist */
        {
          struct stat st;
          cString file = cString::sprintf("%s%s", cPlugin::ConfigDirectory("xineliboutput"), "/default_playlist");
          if (lstat(file, &st) == 0) {
            if (S_ISLNK(st.st_mode)) {
              cString buffer(ReadLink(file), true);
              if (!*buffer || stat(buffer, &st)) {
                Message = tr("Default playlist not found");
              } else {
                LOGDBG("Replaying default playlist: %s", *file);
                cPlayerFactory::Launch(m_Dev, buffer);
              }
            } else {
              Message = tr("Default playlist is not symlink");
            }
          } else {
            Message = tr("Default playlist not defined");
          }
        }
        break;

    case HOTKEY_ADELAY_UP:
      /* audio delay up */
      if (!OnlyInfo) {
        xc.audio_delay++;
        m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay,
                                       xc.audio_compression, xc.audio_equalizer,
                                       xc.audio_surround, xc.speaker_type);
      }
      Message = cString::sprintf("%s %s %d %s", tr("Delay"),
                                 OnlyInfo ? ":" : "->",
                                 xc.audio_delay, tr("ms"));
      break;

    case HOTKEY_ADELAY_DOWN:
      /* audio delay up */
      if (!OnlyInfo) {
        xc.audio_delay--;
        m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay,
                                       xc.audio_compression, xc.audio_equalizer,
                                       xc.audio_surround, xc.speaker_type);
      }
      Message = cString::sprintf("%s %s %d %s", tr("Delay"),
                                 OnlyInfo ? ":" : "->",
                                 xc.audio_delay, tr("ms"));
      break;

    default:
      Message = cString::sprintf(tr("xineliboutput: hotkey %s not binded"), cKey::ToString(Key));
      break;
  }

  if (*Message) {
    if (!xc.pending_menu_action &&
        !cRemote::HasKeys() &&
        cRemote::CallPlugin("xineliboutput"))
      xc.pending_menu_action = new cDisplayMessage(Message);
  }

  xc.last_hotkey_time = now;
  xc.last_hotkey = Key;

  return NewState;
}
