/*
 * statusandquestion: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: statusandquestion.c 0.2 2004/07/13 19:48:34 hflor Exp $
 */

#include <getopt.h>
#include <vdr/plugin.h>
#include <vdr/config.h>
#include <vdr/remote.h>
#include <vdr/svdrp.h>
#include "statusandquestion.h"
#include "controls.h"
#include "display.h"
#include "i18n.h"
#include "menumessages.h"
#include "menusetup.h"
#include "messages.h"
#include "vdrtools.h"

static const char *VERSION           = "0.0.2";
static const char *DESCRIPTION       = "status & question";

// Global variables that control the overall behaviour:

// var pos2
tParamFile statusconfigfile       = { false, false, false, "statusandquestion.conf", "statusandquestion.conf" };
tParamInt  commandline_preference = { false, false, false, true };
tParamInt  delete_b_e             = { false, false, false, 0x0, 0x0 }; // none
tParamFile mainmenu_name          = { false, false, false, "Status" };
tParamInt  mainmenu_visible       = { false, false, false, true };
tParamInt  message_in_queue       = { false, false, false, 20 };
#if VDRVERSNUM >= 10307
tParamInt  num_entrys             = { false, false, false, 10 };
#else
tParamInt  num_entrys             = { false, false, false, MAXOSDITEMS };
#endif
tParamInt  response_in_queue      = { false, false, false, 10 };
tParamInt  sortdirection          = { false, false, false, true };
tParamInt  verbose                = { false, false, false, false, true };

char    plugin_name[MaxFileName]  = "Status";

int        timeout  = 0;
const char *menu[5] = { NULL, NULL, NULL, NULL, NULL };
int        nextid   = 0;
int        dispid   = 0;
int        controlstate = ControlState_Normal;
const char *DeleteLine[4];

void DisplaySetings(void)
{
  #define WriteSource(T)   plugin_name, T.r ? 'r' : ' ', T.c ? 'c' : ' '
  #define IsDefault(T)     WriteSource(T), T.u == T.d ? '*' : ' '
  #define IsDefaultChar(T) WriteSource(T), strcmp(T.u, T.d) ? ' ' : '*'
  #define BoolValue(T)     IsDefault(T), T.u ? "yes" : "no"

  // var pos3
  if (verbose.u)
  {
    isyslog("%s: commandline_preference = [ %c%c%c ] %s", BoolValue(commandline_preference));
    isyslog("%s: mainmenu_visible       = [ %c%c%c ] %s", BoolValue(mainmenu_visible));
    if (mainmenu_visible.u)
      isyslog("%s: mainmenu_name          = [ %c%c%c ] %s", IsDefaultChar(mainmenu_name), mainmenu_name.u);
    isyslog("%s: response hold time     = [ %c%c%c ] %d", IsDefault(response_in_queue), response_in_queue.u);
    isyslog("%s: message-hist hold time = [ %c%c%c ] %d", IsDefault(message_in_queue), message_in_queue.u);
    isyslog("%s: delete all message pos = [ %c%c%c ] %s", IsDefault(delete_b_e), DeleteLine[delete_b_e.u]);
    if (delete_b_e.u == 0x3)
      isyslog("%s: num_entrys             = [ %c%c%c ] %d", IsDefault(num_entrys), num_entrys.u);
    isyslog("%s: sortdirection          = [ %c%c%c ] %s", IsDefault(sortdirection), sortdirection.u ? tr("Choise$ascending") : tr("Choise$descending"));
  }
#ifdef SAQ_Debug
  dsyslog("%s: commandline_preference = [ %c%c%c ] %s", BoolValue(commandline_preference));
  dsyslog("%s: mainmenu_visible       = [ %c%c%c ] %s", BoolValue(mainmenu_visible));
  if (mainmenu_visible.u)
    dsyslog("%s: mainmenu_name          = [ %c%c%c ] %s", IsDefaultChar(mainmenu_name), mainmenu_name.u);
  dsyslog("%s: response hold time     = [ %c%c%c ] %d", IsDefault(response_in_queue), response_in_queue.u);
  dsyslog("%s: message-hist hold time = [ %c%c%c ] %d", IsDefault(message_in_queue), message_in_queue.u);
  dsyslog("%s: delete all message pos = [ %c%c%c ] %s", IsDefault(delete_b_e), DeleteLine[delete_b_e.u]);
  if (delete_b_e.u == 0x3)
    dsyslog("%s: num_entrys             = [ %c%c%c ] %d", IsDefault(num_entrys), num_entrys.u);
  dsyslog("%s: sortdirection          = [ %c%c%c ] %s", IsDefault(sortdirection), sortdirection.u ? tr("Choise$ascending") : tr("Choise$descending"));
#endif
  #undef WriteSource
  #undef IsDefault
  #undef IsDefaultChar
  #undef BoolValue
}

class cPluginStatusAndQuestion : public cPlugin {
private:
  // Add any member variables or functions you may need here.
  cDisplayThread *displaythread;
  cStatusControl *statuscontrol;
  cWaitControl   *waitcontrol;

  int OSDLanguage;
  void TestAndSetOSDLanguage(void);
  bool ProcessArg(int argc, char *argv[]);

  void HelpSAQ(cSVDRP *SVDRP, const char *Option, eMessageTyp MessageTyp);
  
  void CmdSAQTIME(cSVDRP *SVDRP, const char *Option);
  void CmdSAQMESS(cSVDRP *SVDRP, const char *Option);
  void CmdSAQMESI(cSVDRP *SVDRP, const char *Option);
  void CmdSAQMESW(cSVDRP *SVDRP, const char *Option);
  void CmdSAQMESE(cSVDRP *SVDRP, const char *Option);
  void CmdSAQPARA(cSVDRP *SVDRP, const char *Option);
  void CmdSAQTEXT(cSVDRP *SVDRP, const char *Option);
  void CmdSAQLSTQ(cSVDRP *SVDRP, const char *Option);
  void CmdSAQDELQ(cSVDRP *SVDRP, const char *Option);
  void CmdSAQRESP(cSVDRP *SVDRP, const char *Option);
  void CmdSAQLSTR(cSVDRP *SVDRP, const char *Option);
  void CmdSAQDELR(cSVDRP *SVDRP, const char *Option);
  void CmdSAQTEST(cSVDRP *SVDRP, const char *Option);
#ifdef SAQ_Debug
  void CmdSAQLOCK(cSVDRP *SVDRP, const char *Option);
#endif
public:
  cPluginStatusAndQuestion(void);
  virtual ~cPluginStatusAndQuestion();
  virtual const char *Version(void)                             { return VERSION; }
  virtual const char *Description(void)                         { return tr(DESCRIPTION); }
  virtual const char *CommandLineHelp(void);
  virtual const char **SVDRPHelpPages(void);
  virtual bool ProcessArgs(int argc, char *argv[]);
  virtual bool ParseSVDRP(cSVDRP *SVDRP, const char *Cmd, const char *Option);
  virtual bool Start(void);
  virtual const char *MainMenuEntry(void)                       { return controlstate == ControlState_MenuSelect ? "Select this line" : mainmenu_visible.u ? mainmenu_name.u : NULL; }
  virtual cOsdObject *MainMenuAction(void);
  virtual cMenuSetupPage *SetupMenu(void)                       { TestAndSetOSDLanguage(); return new cMenuSetupStatusAndQuestion; }
  virtual bool SetupParse(const char *Name, const char *Value);
  };

// --- cPluginStatusAndQuestion ----------------------------------------------------------

cPluginStatusAndQuestion::cPluginStatusAndQuestion(void)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::cPluginStatusAndQuestion", plugin_name);
#endif
  // Initialize any member variables here.
  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
  OSDLanguage = -1;
  displaythread = NULL;
  statuscontrol = NULL;
  waitcontrol = NULL;
}

cPluginStatusAndQuestion::~cPluginStatusAndQuestion()
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::~cPluginStatusAndQuestion", plugin_name);
#endif
  // Clean up after yourself!
  if (displaythread)
    DELETENULL(displaythread);
  if (statuscontrol)
    DELETENULL(statuscontrol);
  if (waitcontrol)
    DELETENULL(waitcontrol);
}

void cPluginStatusAndQuestion::TestAndSetOSDLanguage(void)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::TestAndSetOSDLanguage OSDLanguage=%d", plugin_name, Setup.OSDLanguage);
#endif
  if (OSDLanguage != Setup.OSDLanguage) {
    OSDLanguage = Setup.OSDLanguage;
    DeleteLine[0] =         tr("Choise$none");
    DeleteLine[1] =         tr("Choise$top");
    DeleteLine[2] =         tr("Choise$bottom");
    DeleteLine[3] =         tr("Choise$top and bottom");
  }
}

bool cPluginStatusAndQuestion::ProcessArg(int argc, char *argv[])
{
  int c;
  static struct option long_options[] = {
    { "delete_begin",              no_argument,       NULL, 'b' },
    { "nodelete_begin",            no_argument,       NULL, 'B' },
    { "min_entrys",                required_argument, NULL, 'd' },
    { "delete_end",                no_argument,       NULL, 'e' },
    { "nodelete_end",              no_argument,       NULL, 'E' },
    { "holdtime_history",          required_argument, NULL, 'h' },
    { "visible_in_mainmenu",       no_argument,       NULL, 'm' },
    { "hide_in_mainmenu",          no_argument,       NULL, 'M' },
    { "mainmenu_name",             required_argument, NULL, 'n' },
    { "holdtime_respones",         required_argument, NULL, 'r' },
    { "sort_ascending",            no_argument,       NULL, 's' },
    { "sort_descending",           no_argument,       NULL, 'S' },
    { "verbose",                   no_argument,       NULL, 'v' },
    { "noverbose",                 no_argument,       NULL, 'V' },
    { "nosetup_commandline",       no_argument,       NULL, 1   },
    { "ns_commandline",            no_argument,       NULL, 1   },
    { NULL }
  };

  if (argc >= 1)
    strn0cpy(plugin_name, argv[0], sizeof(plugin_name));

  for (c = 1; c < argc; c++) {
    dsyslog("%s: parameter%d=%s", plugin_name, c, argv[c]);
  }

  #define Setvalue(T) T.c = true; T.u
  #define SetvalueChar(T) T.c = true; strn0cpy(T.u, optarg, sizeof(T.u))

  while ((c = getopt_long(argc, argv, "bBd:eEh:mMn:r:sSvV", long_options, NULL)) != -1) {
    // var pos4
    switch (c) {
      case 1:   commandline_preference.h = true;
                break;
      case 'b': Setvalue(delete_b_e) |= 0x1;
                break;
      case 'B': Setvalue(delete_b_e) &= ~0x1;
                break;
      case 'd': if (isnumber(optarg)) {
                  Setvalue(num_entrys) = atoi(optarg);
                  Setvalue(delete_b_e) = 0x3;
                  if (num_entrys.u > 0) break;
                }
                esyslog("%s: invalid parameter for -d option: %s", plugin_name, optarg);
                return false;
      case 'e': Setvalue(delete_b_e) |= 0x2;
                break;
      case 'E': Setvalue(delete_b_e) &= ~0x2;
                break;
      case 'h': if (isnumber(optarg)) {
                  Setvalue(message_in_queue) = atoi(optarg);
                  if (message_in_queue.u >=5 && message_in_queue.u <= 999) break;
                }
                esyslog("%s: invalid parameter for -h option: %s range 5-999", plugin_name, optarg);
                return false;
      case 'm': Setvalue(mainmenu_visible) = true;
                break;
      case 'M': Setvalue(mainmenu_visible) = false;
                break;
      case 'n': SetvalueChar(mainmenu_name);
                Setvalue(mainmenu_visible) = true;
                break;
      case 'r': if (isnumber(optarg)) {
                  Setvalue(response_in_queue) = atoi(optarg);
                  if (response_in_queue.u >= 2 && response_in_queue.u <= 199) break;
                }
                esyslog("%s: invalid parameter for -r option: %s range 2-199", plugin_name, optarg);
                return false;
      case 's': Setvalue(sortdirection) = true;
                break;
      case 'S': Setvalue(sortdirection) = false;
                break;
      case 'v': Setvalue(verbose) = true;
                break;
      case 'V': Setvalue(verbose) = false;
                break;
      default:  return false;
    }
  }

  #undef Setvalue
  #undef SetvalueChar

  if (optind < argc && argv[optind][0] == '@') {
    strn0cpy(statusconfigfile.u, &argv[optind][1], sizeof(statusconfigfile.u));
    optind++;
  }
  return optind >= argc;
}

const char *cPluginStatusAndQuestion::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.
  return "  -m           --visible_in_mainmenu          Show the plugin in the mainmenu\n"
         "  -M           --hide_in_mainmenu             Hide the plugin in the mainmenu\n"
         "  -n Name      --mainmenu_name=Name           Select Name for entry in the mainmenu (set also -m)\n"
         "  -b           --delete_begin                 Show the delete all messages line at begin of messagelist\n"
         "  -B           --nodelete_begin               Hide the delete all messages line at begin of messagelist\n"
         "  -e           --delete_end                   Show the delete all messages line at end of messagelist\n"
         "  -E           --nodelete_end                 Hide the delete all messages line at end of messagelist\n"
         "  -d xx        --min_entrys=xx                minimum entrys for display delete all messages line at begin and end (include -b and -e)\n"
         "  -h xx        --holdtime_history=xx          minimum time (min) for message in historyqueue (OSD-list) 5-999\n"           
         "  -r xx        --holdtime_respones=xx         minumum time (min) for responses in queue (readable by SAQRESP) 2-199\n"
         "  -s           --sort_ascending               sortoption for messagelist (OSD)\n"
         "  -S           --sort_descending              sortoption for messagelist (OSD)\n"
         "  -v           --verbose                      Enable more logging\n"
         "  -V           --noverbose                    Disable more loggig\n"
         "               --nosetup_commandline          Hide the Preferr Command Line Parameter form setup-menu\n"
         "               --ns_commandline               same as --nosetup_commandline";
         // free: a c f g i j k l o p q t u v w x y z
}

bool cPluginStatusAndQuestion::ProcessArgs(int argc, char *argv[])
{
  // Implement command line argument processing here if applicable.
  return ProcessArg(argc, argv);
}

const char ** cPluginStatusAndQuestion::SVDRPHelpPages(void)
{
  static const char *HelpPages[] = {
  "SAQTIME [ timeout ]\n"
  "    Set timeout for all messages (overwrite OSD-setting while display message).",
  "SAQMESS [ <message> ]\n"
  "    Displays the given message (status) on the OSD. If message is omitted,\n"
  "    the currently pending message (if any) will be returned. The message\n"
  "    will be displayed for a few seconds as soon as the OSD has become\n"
  "    idle. If a new SAQMESI command is entered while the previous message\n"
  "    has not yet been displayed, the new message will be stored.\n"
  "    For more info for choise color and other andere sachen HELP SAQTEST",
  "SAQMESI [ <message> ]\n"
  "    Displays the given message (info) on the OSD. If message is omitted,\n"
  "    the currently pending message (if any) will be returned. The message\n"
  "    will be displayed for a few seconds as soon as the OSD has become\n"
  "    idle. If a new SAQMESI command is entered while the previous message\n"
  "    has not yet been displayed, the new message will be stored.\n"
  "    For more info for choise color and other andere sachen HELP SAQTEST",
  "SAQMESW [ <message> ]\n"
  "    Displays the given message (warning) on the OSD. If message is omitted,\n"
  "    the currently pending message (if any) will be returned. The message\n"
  "    will be displayed for a few seconds as soon as the OSD has become\n"
  "    idle. If a new SAQMESI command is entered while the previous message\n"
  "    has not yet been displayed, the new message will be stored.\n"
  "    For more info for choise color and other andere sachen HELP SAQTEST",
  "SAQMESE [ <message> ]\n"
  "    Displays the given message (error) on the OSD. If message is omitted,\n"
  "    the currently pending message (if any) will be returned. The message\n"
  "    will be displayed for a few seconds as soon as the OSD has become\n"
  "    idle. If a new SAQMESE command is entered while the previous message\n"
  "    has not yet been displayed, the new message will be stored.\n"
  "    For more info for choise color and other andere sachen HELP SAQTEST",
  "SAQPARA [ title, red, green, yellow, blue ]\n"
  "    This command sets the parameter for display the menu with a long text\n"
  "    from the SAQTEXT",
  "SAQTEXT [ long text ]\n"
  "    Displays the given text as full screen text on the OSD. If message\n"
  "    is omitted, the currently pending message (if any) will be returned.\n"
  "    The message will be displayed for a few seconds as soon as the OSD has\n"
  "    become idle. If a new SAQMESE command is entered while the previous\n"
  "    message has not yet been displayed, the new message will be stored.\n"
  "    A new line is the spezial char | by more line than the screen, scrolling\n"
  "    is enabled with the up/down (line) or left/rigth (page) keys.\n"
  "    For Display a short message use SAQMESS, SAQMESI, SAQMESWW or SAQMESWE",
  "SAQLSTQ\n"
  "    List all Messages in the Queue",
  "SAQDELQ\n"
  "    Delete all Messages in the Queue",
  "SAQRESP [ id ]\n"
  "    return then response (key) of message.\n"
  "    Is the message in the queue then is a '514 currently in queue' returned\n"
  "    Is the message on screen then is a '515 currently display' returned",
  "SAQLSTR\n"
  "    List all response (keys) in the Queue",
  "SAQDELR\n"
  "    Delete all response (keys) in the Queue"
  "    without this command the response deleted after 10 minutes",
  "SAQTEST\n"
  "    return version of plugin, when the plugin not runnig\n"
  "    vdr returned 500 Command unrecognized:\n"
  "    returncodes: 250 - Requested action okay, completed\n"
  "                 501 - Syntax error in arguments\n"
  "                 510 - lock for messagequeue failed\n"
  "                 511 - lock for responsequeue failed\n"
  "                 512 - lock for histroryqueue failed\n"
  "                 513 - no data available\n"
  "                 514 - currently in queue\n"
  "                 515 - currently display\n"
  "                 550 - Requested action not taken\n"
  "    For Display a short message use the commands SAQMESS, SAQMESI, SAQMESW\n"
  "    or SAQMESWE (Status, Info, Warning, Error) the colors for display the\n"
  "    text are from the vdr-settings dependent.\n"
  "    To show a long text use the SAQTEXT-command and set with the SAQPARAM\n"
  "    the title and the buttons for display the long text.\n"
  "    The first parameter of a 250-answer for store a message in the queue is\n"
  "    the id, that use by the SAQRESP-command. Range for ID is 1 to 9999\n"
  "    All responses from user is for 10 minutet stored to read by the script.\n"
  "    The vdr 1.2.6 allowed a multi-line-text for SAQMESx with the '|' as new\n"
  "    line, for a new line before or after the text use the character '@' at\n"
  "    begin or/and end of text.",
#ifdef SAQ_Debug
  "SAQLOCK [ lm | lr | lh | um | ur | uh | s[0..6]]\n"
  "   show or set status for queuelock\n",
#endif
  NULL
  };
  return HelpPages;
}

bool cPluginStatusAndQuestion::ParseSVDRP(cSVDRP *SVDRP, const char *Cmd, const char *Option)
{
#define CMD(c) (strcasecmp(Cmd, c) == 0)

#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::ParseSVDRP %s%s%s", plugin_name, Cmd, *Option ? " with Option " : "", Option);
#endif
  if (verbose.u)
    isyslog("%s: ParseSVDRP %s%s%s", plugin_name, Cmd, *Option ? " with Option " : "", Option);
  if      (CMD("SAQTIME"))  CmdSAQTIME(SVDRP, Option);
  else if (CMD("SAQMESS"))  CmdSAQMESS(SVDRP, Option);
  else if (CMD("SAQMESI"))  CmdSAQMESI(SVDRP, Option);
  else if (CMD("SAQMESW"))  CmdSAQMESW(SVDRP, Option);
  else if (CMD("SAQMESE"))  CmdSAQMESE(SVDRP, Option);
  else if (CMD("SAQPARA"))  CmdSAQPARA(SVDRP, Option);
  else if (CMD("SAQTEXT"))  CmdSAQTEXT(SVDRP, Option);
  else if (CMD("SAQLSTQ"))  CmdSAQLSTQ(SVDRP, Option);
  else if (CMD("SAQDELQ"))  CmdSAQDELQ(SVDRP, Option);
  else if (CMD("SAQRESP"))  CmdSAQRESP(SVDRP, Option);
  else if (CMD("SAQLSTR"))  CmdSAQLSTR(SVDRP, Option);
  else if (CMD("SAQDELR"))  CmdSAQDELR(SVDRP, Option);
  else if (CMD("SAQTEST"))  CmdSAQTEST(SVDRP, Option);
#ifdef SAQ_Debug
  else if (CMD("SAQLOCK"))  CmdSAQLOCK(SVDRP, Option);
#endif
  else                      return false;
  return true;
  
#undef CMD
}

bool cPluginStatusAndQuestion::Start(void)
{
  // Start any background activities the plugin shall perform.
  RegisterI18n(Phrases);
  TestAndSetOSDLanguage();
  
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::Start statusconfigfile=%s", plugin_name, statusconfigfile.u);
#endif

  char *p;
  char *q = strdup(ConfigDirectory(""));
  asprintf(&p, "%s%s%s", q, *(q + strlen(q) - 1) == '/' ? "" : "/", statusconfigfile.u);
  if (!access(statusconfigfile.u, F_OK) && !access(statusconfigfile.u, R_OK) || !access(p, F_OK) && !access(p, R_OK)) {
    #define MAXARGS 100
    int fargc = 1;
    char *fargv[MAXARGS];
    char buffer[10000];
    bool done;
    FILE *f;

    if (!access(statusconfigfile.u, F_OK)) {
      f = fopen(statusconfigfile.u, "r");
#ifdef ATE_Debug
      dsyslog("%s: cPluginStatusAndQuestion::Start open statusconfigfile=%s", plugin_name, statusconfigfile.u);
#endif
    } else {
      f = fopen(p, "r");
#ifdef ATE_Debug
      dsyslog("%s: cPluginStatusAndQuestion::Start open statusconfigfile=%s", plugin_name, p);
#endif
    }
    free(p);
    free(q);
    if (!f) {
      esyslog("%s: ERROR: cannot open config file: [%s]%s", plugin_name, ConfigDirectory(""), statusconfigfile.u);
      return false;
    }
    while (fgets(buffer, sizeof(buffer), f) > 0) {
      p = skipspace(stripspace(buffer));
      q = NULL;
      done = false;
      while (!done) {
        if (!q)
          q = p;
        switch (*p) {
          case '\\': strcpy(p, p + 1);
                     if (*p)
                       p++;
                     else {
                       esyslog("%s: ERROR: missing character after \\", plugin_name);
                       return false;
                     }
                     break;
          case '"':
          case '\'': if ((p = SkipQuote(p)) == NULL)
                       return false;
                     break;
          default:   if (!*p || isspace(*p)) {
                       done = !*p;
                       *p = 0;
                       if (q) {
                         if (fargc < MAXARGS - 1) {
                           if (*q != '#')
                             fargv[fargc++] = strdup(q);
                         } else {
                           esyslog("%s: ERROR: plugin argument list too long", plugin_name);
                           return false;
                         }
                         q = NULL;
                       }
                     }
                     if (!done)
                       p = *p ? p + 1 : skipspace(p + 1);
        }
      }
    }
    fclose(f);
    fargv[0] = strdup(plugin_name);
    fargv[fargc] = NULL;
    optind = 0; // to reset the getopt() data
    if (fargc > 1)
      if (!ProcessArg(fargc, fargv)) {
        esyslog("%s: ERROR: cannot parse config file: [%s]%s", plugin_name, ConfigDirectory(""), statusconfigfile.u);
        return false;
      }
      while(fargc) free(fargv[--fargc]);
  } else {
    free(p);
    free(q);
    if (strcmp(statusconfigfile.u, statusconfigfile.d)) {
      esyslog("%s: ERROR: config file not found: [%s]%s", plugin_name, ConfigDirectory(""), statusconfigfile.u);
      return false;
    } else if (verbose.u)
      isyslog("%s: INFO: config file not found: [%s]%s", plugin_name, ConfigDirectory(""), statusconfigfile.u);
  }

  // default parameter                --> d_
  // read parameter from commandline  --> c_  (data in u_)
  // value parameter from config-file --> s_
  // read parameter from config-file  --> r_
  // paramater used                   --> u_

  #define SetParam(T) if (!T.c || (!commandline_preference.u && T.r)) T.u = (!T.c && !T.r) ? T.d : T.s
  #define SetParamChar(T) if (!T.c || (!commandline_preference.u && T.r)) strcpy(T.u, (!T.c && !T.r) ? T.d : T.s)

  commandline_preference.u = commandline_preference.r ? commandline_preference.s : commandline_preference.d;

  // var pos5
  SetParamChar (statusconfigfile);
  SetParam     (delete_b_e);
  SetParamChar (mainmenu_name);
  SetParam     (mainmenu_visible);
  SetParam     (message_in_queue);
  SetParam     (num_entrys);
  SetParam     (response_in_queue);
  SetParam     (sortdirection);
  SetParam     (verbose);

  #undef SetParam
  #undef SetParamChar

  if (verbose.u)
    isyslog("%s: Start", plugin_name);
  DisplaySetings();
 
  displaythread = new cDisplayThread;
  displaythread->Start();
  statuscontrol = new cStatusControl;
  return true;
}

cOsdObject *cPluginStatusAndQuestion::MainMenuAction(void)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::MainMenuAction %d", plugin_name, controlstate);
#endif
  if (controlstate == ControlState_MenuSelect)
  {
    waitcontrol = new cWaitControl;
    return waitcontrol;
  }
  TestAndSetOSDLanguage();
  if (mainmenu_visible.u)
    return new cMenuMessages;
  else
    return NULL;
}

bool cPluginStatusAndQuestion::SetupParse(const char *Name, const char *Value)
{
  #define SetParam(T) { T.r = true; T.s = strtol(Value, NULL, 0); }
  #define SetParamChar(T) { T.r = true; strn0cpy(T.s, Value, sizeof(T.s)); }

  // Parse your own setup parameters and store their values.
  dsyslog("%s: Setupparameter %s=%s", plugin_name, Name, Value);
  // var pos6
  if      (!strcasecmp(Name, "commandline"))   SetParam     (commandline_preference)
  else if (!strcasecmp(Name, "deletelines"))   SetParam     (delete_b_e)
  else if (!strcasecmp(Name, "name"))          SetParamChar (mainmenu_name)
  else if (!strcasecmp(Name, "visible"))       SetParam     (mainmenu_visible)
  else if (!strcasecmp(Name, "message"))       SetParam     (message_in_queue)
  else if (!strcasecmp(Name, "entrys"))        SetParam     (num_entrys)
  else if (!strcasecmp(Name, "response"))      SetParam     (response_in_queue)
  else if (!strcasecmp(Name, "sortdirection")) SetParam     (sortdirection)
  else if (!strcasecmp(Name, "verbose"))       SetParam     (verbose)
  else
    return false;
  return true;

  #undef SetParam
  #undef SetParamChar
}

void cPluginStatusAndQuestion::HelpSAQ(cSVDRP *SVDRP, const char *Option, eMessageTyp MessageTyp)
{
  if (!Messages.SetLock(true, true))
  {
    SVDRP->Reply(510, "lock for queue failed");
    if (verbose.u)
      isyslog("%s: Reply 510 lock for queue failed", plugin_name);
  } else
  {
    if (!History.SetLock(true, true))
    {
      SVDRP->Reply(512, "lock for queue failed");
      if (verbose.u)
        isyslog("%s: Reply 512 lock for queue failed", plugin_name);
    } else
    {
      if (*Option) {
        if (Messages.Count() > 99)
        {
          SVDRP->Reply(550, "to many messages in queue current=%d", Messages.Count());
          if (verbose.u)
            isyslog("%s: Reply 550 to many messages in queue current current=%d", plugin_name, Messages.Count());
        } else
        {
          cMessageData *messagedata = new cMessageData(MessageTyp);
          messagedata->SetText(Option);
          Messages.Add(new cMessage(messagedata));
          History.Add(new cMessage(messagedata));
          SVDRP->Reply(250, "%d new %s '%s' is stored in queue at pos %d with id=%d", messagedata->ID(), MessageTypName(MessageTyp), Option, Messages.Count(), messagedata->ID());
          if (verbose.u)
            isyslog("%s: Reply 250 new %s '%s' is stored in queue at pos %d with id=%d", plugin_name, MessageTypName(MessageTyp), Option, Messages.Count(), messagedata->ID());
        }
      } else {
        cMessage *message = Messages.First();
        while (message)
        {
          if (message->MessageTyp() == MessageTyp)
          {
            SVDRP->Reply(250, "next %s is '%s' at pos %d with id=%d", MessageTypName(message->MessageTyp()), message->Textorg(), message->Index() + 1, message->ID());
            if (verbose.u)
              isyslog("%s: Reply 250 next %s is '%s' at pos %d with id=%d", plugin_name, MessageTypName(message->MessageTyp()), message->Textorg(), message->Index() + 1, message->ID());
            break;
          }
          message = Messages.Next(message);
        }
        if (!message)
        {
          SVDRP->Reply(513, "no queued %s found", MessageTypName(MessageTyp));
          if (verbose.u)
            isyslog("%s: Reply 513 no queued %s found", plugin_name, MessageTypName(MessageTyp));
        }
      }
      History.SetLock(false);
    }
    Messages.SetLock(false);
  }  
}

void cPluginStatusAndQuestion::CmdSAQTIME(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQTIME %s", plugin_name, Option);
#endif
  if (*Option)
  {
    if (isnumber(Option))
    {
      int i = atoi(Option);
      if (i >= 0 && i <= 99)
      {
        if (Messages.Count() > 0)
        {
          if (!Messages.SetLock(true, true))
          {
            SVDRP->Reply(510, "lock for queue failed");
            if (verbose.u)
              isyslog("%s: Reply 510 lock for queue failed", plugin_name);
          } else
          {
            if (Messages.Count() > 199)
            {
              SVDRP->Reply(550, "to many messages in queue current=%d", Messages.Count());
              if (verbose.u)
                isyslog("%s: Reply 550 to many messages in queue current=%d", plugin_name, Messages.Count());
            } else
            {
              cMessageData *messagedata = new cMessageData(MessageTyp_SetTime);
              messagedata->SetTimeout(i);
              Messages.Add(new cMessage(messagedata));
              SVDRP->Reply(250, "change for timeout to %s%d seconds is stored in queue at pos %d", i == 0 ? "default of " : "", i == 0 ? Setup.OSDMessageTime : i, Messages.Count());
              if (verbose.u)
                isyslog("%s: Reply 250 change for timeout to %s%d seconds is stored in queue at pos %d", plugin_name, i == 0 ? "default of " : "", i == 0 ? Setup.OSDMessageTime : i, Messages.Count());
            }
            Messages.SetLock(false);
          }  
        } else
        {
          timeout = i;
          if (i == 0)
          {
            SVDRP->Reply(250, "timeout is set to default of %d seconds", Setup.OSDMessageTime);
            if (verbose.u)
              isyslog("%s: Reply 250 timeout is set to default of %d seconds", plugin_name, Setup.OSDMessageTime);
          } else
          {
            SVDRP->Reply(250, "timeout is set to %d seconds", i);
            if (verbose.u)
              isyslog("%s: Reply 250 timeout is set to %d seconds", plugin_name, i);
          }
        }  
      } else
      {
        SVDRP->Reply(501, "range for option only from 1 to 99 seconds or 0 for default (%d)", Setup.OSDMessageTime);
        if (verbose.u)
          isyslog("%s: Reply 501 range for option only from 1 to 99 seconds or 0 for default (%d)", plugin_name, Setup.OSDMessageTime);
      }
    } else
    {
      SVDRP->Reply(501, "only numbers allowed");
      if (verbose.u)
        isyslog("%s: Reply 501 only numbers allowed", plugin_name);
    }
  } else
  {
    if (!Messages.SetLock(true, true))
    {
      SVDRP->Reply(510, "lock for queue failed");
      if (verbose.u)
        isyslog("%s: Reply 510 lock for queue failed", plugin_name);
    } else
    {
      cMessage *message = Messages.Last();
      while (message)
      {
        if (message->MessageTyp() == MessageTyp_SetTime)
        {
          SVDRP->Reply(250, "%d seconds form %s at pos %d", message->Timeout() == 0 ? Setup.OSDMessageTime : message->Timeout(), message->Timeout() == 0 ? "setupmenu" : "last SAQTIME-command", message->Index() + 1);
          if (verbose.u)
            isyslog("%s: Reply 250 %d seconds form %s at pos %d", plugin_name, message->Timeout() == 0 ? Setup.OSDMessageTime : message->Timeout(), message->Timeout() == 0 ? "setupmenu" : "last SAQTIME-command", message->Index() + 1);
          break;
        }
        message = Messages.Prev(message);
      }
      if (!message)
      {
        SVDRP->Reply(250, "%d seconds form %s", timeout == 0 ? Setup.OSDMessageTime : timeout, timeout == 0 ? "setupmenu" : "last SAQTIME-command");
        if (verbose.u)
          isyslog("%s: Reply 250 %d seconds form %s", plugin_name, timeout == 0 ? Setup.OSDMessageTime : timeout, timeout == 0 ? "setupmenu" : "last SAQTIME-command");
      }
    }
    Messages.SetLock(false);
  }
}

void cPluginStatusAndQuestion::CmdSAQPARA(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQPARA %s", plugin_name, Option);
#endif
  if (*Option)
  {
    int pos = MenuTyp_Title;
    char buffer[1024];
    char *m[5] = { NULL, NULL, NULL, NULL, NULL };
    const char *k = Option;
    while (*k)
    {
      k = strchr(k, ',');
      if (k)
        k++;
      else
        k = Option + strlen(Option) + 1;
      if ((size_t) (k - Option) > sizeof(buffer))
        k = Option + sizeof(buffer); 
      strn0cpy(buffer, Option, k - Option);
      if (pos > MenuTyp_Blue)
      {
        SVDRP->Reply(-501, "only 5 parameter allow '%s' ignored.", buffer);
        if (verbose.u)
          isyslog("%s: Reply 501 only 5 parameter allow '%s' ignored.", plugin_name, buffer);
      } else  
        m[pos++] = strlen(buffer) ? strdup(buffer) : NULL;
      Option = k;
    }  
    if (Messages.Count() > 0)
    {
      if (!Messages.SetLock(true, true))
      {
        SVDRP->Reply(510, "lock for queue failed");
        if (verbose.u)
          isyslog("%s: Reply 510 lock for queue failed", plugin_name);
      } else
      {
        if (Messages.Count() > 199)
        {
          SVDRP->Reply(550, "to many messages in queue current=%d", Messages.Count());
          if (verbose.u)
            isyslog("%s: Reply 550 to many messages in queue current=%d", plugin_name, Messages.Count());
        } else
        {
          cMessageData *messagedata = new cMessageData(MessageTyp_SetTime);
          for (pos = MenuTyp_Title; pos <= MenuTyp_Blue; pos++)
            messagedata->SetMenu((eMenuTyp) pos, m[pos]);
          Messages.Add(new cMessage(messagedata));
          SVDRP->Reply(250, "change for parameter is stored in queue at pos %d", Messages.Count());
          if (verbose.u)
            isyslog("%s: Reply 250 change for parameter is stored in queue at pos %d", plugin_name, Messages.Count());
        }
        Messages.SetLock(false);
      }  
    } else
    {
      for (pos = MenuTyp_Title; pos <= MenuTyp_Blue; pos++)
        menu[pos] = m[pos] ? strdup(m[pos]) : NULL;
      SVDRP->Reply(250, "new title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c", DispText(menu[MenuTyp_Title]), DispText(menu[MenuTyp_Red]), DispText(menu[MenuTyp_Green]), DispText(menu[MenuTyp_Yellow]), DispText(menu[MenuTyp_Blue]));
      if (verbose.u)
        isyslog("%s: Reply 250 new title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c", plugin_name, DispText(menu[MenuTyp_Title]), DispText(menu[MenuTyp_Red]), DispText(menu[MenuTyp_Green]), DispText(menu[MenuTyp_Yellow]), DispText(menu[MenuTyp_Blue]));
    }
    for (pos = MenuTyp_Title; pos <= MenuTyp_Blue; pos++)
      if (m[pos])
        free(m[pos]);
  } else
  {
    if (!Messages.SetLock(true, true))
    {
      SVDRP->Reply(510, "lock for queue failed");
      if (verbose.u)
        isyslog("%s: Reply 510 lock for queue failed", plugin_name);
    } else
    {
      cMessage *message = Messages.Last();
      while (message)
      {
        if (message->MessageTyp() == MessageTyp_Param)
        {
          SVDRP->Reply(250, "new title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c at pos %d", DispText(message->Menu(MenuTyp_Title)), DispText(message->Menu(MenuTyp_Red)), DispText(message->Menu(MenuTyp_Green)), DispText(message->Menu(MenuTyp_Yellow)), DispText(message->Menu(MenuTyp_Blue)), message->Index() + 1);
          if (verbose.u)
            isyslog("%s: Reply 250 new title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c at pos %d", plugin_name, DispText(message->Menu(MenuTyp_Title)), DispText(message->Menu(MenuTyp_Red)), DispText(message->Menu(MenuTyp_Green)), DispText(message->Menu(MenuTyp_Yellow)), DispText(message->Menu(MenuTyp_Blue)), message->Index() + 1);
          break;
        }
        message = Messages.Prev(message);
      }
      if (!message)
      {
        SVDRP->Reply(250, "title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c", DispText(menu[MenuTyp_Title]), DispText(menu[MenuTyp_Red]), DispText(menu[MenuTyp_Green]), DispText(menu[MenuTyp_Yellow]), DispText(menu[MenuTyp_Blue]));
        if (verbose.u)
          isyslog("%s: Reply 250 title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c", plugin_name, DispText(menu[MenuTyp_Title]), DispText(menu[MenuTyp_Red]), DispText(menu[MenuTyp_Green]), DispText(menu[MenuTyp_Yellow]), DispText(menu[MenuTyp_Blue]));
      }
    }
    Messages.SetLock(false);
  }
}

void cPluginStatusAndQuestion::CmdSAQMESS(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQMESS %s", plugin_name, Option);
#endif
  HelpSAQ(SVDRP, Option, MessageTyp_Status);
}

void cPluginStatusAndQuestion::CmdSAQMESI(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQMESI %s", plugin_name, Option);
#endif
  HelpSAQ(SVDRP, Option, MessageTyp_Info);
}

void cPluginStatusAndQuestion::CmdSAQMESW(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQMESW %s", plugin_name, Option);
#endif
  HelpSAQ(SVDRP, Option, MessageTyp_Warning);
}

void cPluginStatusAndQuestion::CmdSAQMESE(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQMESE %s", plugin_name, Option);
#endif
  HelpSAQ(SVDRP, Option, MessageTyp_Error);
}

void cPluginStatusAndQuestion::CmdSAQTEXT(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQTEXT %s", plugin_name, Option);
#endif
  HelpSAQ(SVDRP, Option, MessageTyp_Text);
}

void cPluginStatusAndQuestion::CmdSAQLSTQ(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQLSTQ %s", plugin_name, Option);
#endif
  if (!Messages.SetLock(true, true))
  {
    SVDRP->Reply(510, "lock for queue failed");
    if (verbose.u)
      isyslog("%s: Reply 510 lock for queue failed", plugin_name);
  } else
  {
    if (Messages.Count())
    {
      cMessage *message = Messages.First();
      while (message)
      {
        cMessage *nextmessage = Messages.Next(message);
        if (message->MessageTyp() == MessageTyp_SetTime)
        {
          SVDRP->Reply(nextmessage ? -250 : 250, "%03d set timeout to %s%d seconds", message->Index() + 1, message->Timeout() == 0 ? "default of " : "", message->Timeout() == 0 ? Setup.OSDMessageTime : message->Timeout());
          if (verbose.u)
            isyslog("%s: Reply 250 %03d set timeout to %s%d seconds", plugin_name, message->Index() + 1, message->Timeout() == 0 ? "default of " : "", message->Timeout() == 0 ? Setup.OSDMessageTime : message->Timeout());
        } else if (message->MessageTyp() == MessageTyp_Param)
        {
          SVDRP->Reply(nextmessage ? -250 : 250, "%03d title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c", message->Index() + 1, DispText(message->Menu(MenuTyp_Title)), DispText(message->Menu(MenuTyp_Red)), DispText(message->Menu(MenuTyp_Green)), DispText(message->Menu(MenuTyp_Yellow)), DispText(message->Menu(MenuTyp_Blue)));
          if (verbose.u)
            isyslog("%s: Reply 250 %03d title=%c%s%c red=%c%s%c green=%c%s%c yellow=%c%s%c blue=%c%s%c", plugin_name, message->Index() + 1, DispText(message->Menu(MenuTyp_Title)), DispText(message->Menu(MenuTyp_Red)), DispText(message->Menu(MenuTyp_Green)), DispText(message->Menu(MenuTyp_Yellow)), DispText(message->Menu(MenuTyp_Blue)));
        } else
        {
          SVDRP->Reply(nextmessage ? -250 : 250, "%03d %s with text '%s' with ID=%d", message->Index() + 1, MessageTypName(message->MessageTyp()), message->Textorg(), message->ID());
          if (verbose.u)
            isyslog("%s: Reply 250 %03d %s with text '%s' with ID=%d", plugin_name, message->Index() + 1, MessageTypName(message->MessageTyp()), message->Textorg(), message->ID());
        }
        message = nextmessage;
      }
    } else
    {
      SVDRP->Reply(513, "no queued messages");
      if (verbose.u)
        isyslog("%s: Reply 513 no queued messages", plugin_name);
    }
    Messages.SetLock(false);
  }  
}

void cPluginStatusAndQuestion::CmdSAQDELQ(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQDELQ %s", plugin_name, Option);
#endif
  if (!Messages.SetLock(true, true))
  {
    SVDRP->Reply(510, "lock for queue failed");
    if (verbose.u)
      isyslog("%s: Reply 510 lock for queue failed", plugin_name);
  } else
  {
    if (Messages.Count()) {
      SVDRP->Reply(250, "delete %d message%s from queue", Messages.Count(), Messages.Count() == 1 ? "" : "s");
      if (verbose.u)
        isyslog("%s: Reply 250 delete %d message%s from queue", plugin_name, Messages.Count(), Messages.Count() == 1 ? "" : "s");
      Messages.Clear();
    } else
    {
      SVDRP->Reply(513, "no queued messages");
      if (verbose.u)
        isyslog("%s: Reply 513 no queued messages", plugin_name);
    }
    Messages.SetLock(false);
  }  
}

void cPluginStatusAndQuestion::CmdSAQRESP(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQRESP %s", plugin_name, Option);
#endif
  if (!Messages.SetLock(true, true))
  {
    SVDRP->Reply(510, "lock for queue failed");
    if (verbose.u)
      isyslog("%s: Reply 510 lock for queue failed", plugin_name);
  } else
  {
    if (!Responses.SetLock(true, true))
    {
      SVDRP->Reply(511, "lock for queue failed");
      if (verbose.u)
        isyslog("%s: Reply 511 lock for queue failed", plugin_name);
    } else
    {
      cMessage *message = NULL;
      if (*Option)
      {
        if (isnumber(Option))
        {
          int i = atoi(Option);
          if (i <= 0 || i > 9999)
          {
            SVDRP->Reply(501, "range for id only from 1 to 9999");
            if (verbose.u)
              isyslog("%s: Reply 501 range for id only from 1 to 9999", plugin_name);
          } else
          {
            if (i == dispid)
            {
              SVDRP->Reply(515, "currently display");
              if (verbose.u)
                isyslog("%s: Reply 515 currently display", plugin_name);
            } else
            {
              message = Messages.First();
              while (message)
              {
                if (message->ID() == i)
                  break;
                else
                  message = Messages.Next(message);
              }
              if (message)
              {
                SVDRP->Reply(514, "currently in queue at pos %d", message->Index() + 1);
                if (verbose.u)
                  isyslog("%s: Reply 514 currently in queue at pos %d", plugin_name, message->Index() + 1);
                message = NULL;
              } else
              {
                message = Responses.First();
                while (message)
                {
                  if (message->ID() == i)
                    break;
                  else
                    message = Responses.Next(message);
                }
                if (!message)
                {
                  SVDRP->Reply(513, "response with ID=%d not found in any queue", i);
                  if (verbose.u)
                    isyslog("%s: Reply 513 response with ID=%d not found in any queue", plugin_name, i);
                }
              }
            }
          }
        } else
        {
          SVDRP->Reply(501, "only numbers allowed");
          if (verbose.u)
            isyslog("%s: Reply 501 only numbers allowed", plugin_name);
        }
      } else
      {
        message = Responses.First();
        if (!message)
        {
          SVDRP->Reply(513, "no queued responses");
          if (verbose.u)
            isyslog("%s: Reply 513 no queued responses", plugin_name);
        }
      }
      if (message)
      {
        SVDRP->Reply(250, "response = %d (%s) ID=%d for %s with text '%s' is %d seconds old", message->Key(), KeyName(message->Key()), message->ID(), MessageTypName(message->MessageTyp()), message->Textorg(), time(NULL) - message->ResponseTime());
        if (verbose.u)
          isyslog("%s: Reply 250 response = %d (%s) ID=%d for %s with text '%s' is %d seconds old", plugin_name, message->Key(), KeyName(message->Key()), message->ID(), MessageTypName(message->MessageTyp()), message->Textorg(), int(time(NULL) - message->ResponseTime()));
        Responses.Del(message);
      }
      Responses.SetLock(false);
    }
    Messages.SetLock(false);
  }
}

void cPluginStatusAndQuestion::CmdSAQLSTR(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQLSTR %s", plugin_name, Option);
#endif
  if (!Responses.SetLock(true, true))
  {
    SVDRP->Reply(510, "lock for queue failed");
    if (verbose.u)
      isyslog("%s: Reply 510 lock for queue failed", plugin_name);
  } else
  {
    if (Responses.Count())
    {
      cMessage *message = Responses.First();
      while (message)
      {
        cMessage *nextmessage = Responses.Next(message);
        SVDRP->Reply(nextmessage ? -250 : 250, "%03d response = %d (%s) ID=%d for %s with text '%s' is %d seconds old", message->Index() + 1, message->Key(), KeyName(message->Key()), message->ID(), MessageTypName(message->MessageTyp()), message->Textorg(), time(NULL) - message->ResponseTime());
        if (verbose.u)
          isyslog("%s: Reply 250 %03d response = %d (%s) ID=%d for %s with text '%s' is %d seconds old", plugin_name, message->Index() + 1, message->Key(), KeyName(message->Key()), message->ID(), MessageTypName(message->MessageTyp()), message->Textorg(), int(time(NULL) - message->ResponseTime()));
        message = nextmessage;
      }
    } else
    {
      SVDRP->Reply(513, "no queued responses");
      if (verbose.u)
        isyslog("%s: Reply 513 no queued responses", plugin_name);
    }
    Responses.SetLock(false);
  }  
}

void cPluginStatusAndQuestion::CmdSAQDELR(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQDELR %s", plugin_name, Option);
#endif
  if (!Responses.SetLock(true, true))
  {
    SVDRP->Reply(510, "lock for queue failed");
    if (verbose.u)
      isyslog("%s: Reply 510 lock for queue failed", plugin_name);
  } else
  {
    if (Responses.Count()) {
      SVDRP->Reply(250, "delete %d Response%s from queue", Responses.Count(), Responses.Count() == 1 ? "" : "s");
      if (verbose.u)
        isyslog("%s: Reply 250 delete %d Response%s from queue", plugin_name, Responses.Count(), Responses.Count() == 1 ? "" : "s");
      Responses.Clear();
    } else
    {
      SVDRP->Reply(513, "no queued responses");
      if (verbose.u)
        isyslog("%s: Reply 513 no queued responses", plugin_name);
    }
    Responses.SetLock(false);
  }  
}

void cPluginStatusAndQuestion::CmdSAQTEST(cSVDRP *SVDRP, const char *Option)
{
#ifdef SAQ_Debug
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQTEST %s", plugin_name, Option);
#endif
  SVDRP->Reply(250, "%s (VDR: %s) from %s %s", VERSION, VDRVERSION, __DATE__, __TIME__);
  if (verbose.u)
    isyslog("%s: Reply 250 %s (VDR: %s) from %s %s", plugin_name, VERSION, VDRVERSION, __DATE__, __TIME__);
}

#ifdef SAQ_Debug
void cPluginStatusAndQuestion::CmdSAQLOCK(cSVDRP *SVDRP, const char *Option)
{
  dsyslog("%s: cPluginStatusAndQuestion::CmdSAQLOCK %s messages lock=%s count=%d responses lock=%s count=%d", plugin_name, Option, Messages.GetLock() ? "true" : "false", Messages.Count(), Responses.GetLock() ? "true" : "false", Responses.Count());
  if (*Option)
  {
    if (Option[0] == 'l' || Option[0] == 'L')
      if (Option[1] == 'm' || Option[1] == 'M')
        Messages.SetLock(true);
      else if (Option[1] == 'r' || Option[1] == 'R')
        Responses.SetLock(true);
      else if (Option[1] == 'h' || Option[1] == 'H')
        History.SetLock(true);
      else
        SVDRP->Reply(-550, "only [l|u][m|r|h] and s[0..6] allowed!");
    else if (Option[0] == 'u' || Option[0] == 'U')
      if (Option[1] == 'm' || Option[1] == 'M')
        Messages.SetLock(false);
      else if (Option[1] == 'r' || Option[1] == 'R')
        Responses.SetLock(false);
      else if (Option[1] == 'h' || Option[1] == 'H')
        History.SetLock(false);
      else
        SVDRP->Reply(-550, "only [l|u][m|r|h] and s[0..6] allowed!");
    else if (Option[0] == 's' || Option[0] == 'S')
      if (Option[1] <= '0' || Option[1] <= '6')
        controlstate = Option[1] - 48;
      else
        SVDRP->Reply(-550, "only [l|u][m|r|h] and s[0..6] allowed!");
    else
      SVDRP->Reply(-550, "only [l|u][m|r|h] and s[0..6] allowed!");
  }
  dsyslog("%s: messages lock=%s count=%d responses lock=%s count=%d history lock=%s count=%d state=%d", plugin_name, Messages.GetLock() ? "true" : "false", Messages.Count(), Responses.GetLock() ? "true" : "false", Responses.Count(), History.GetLock() ? "true" : "false", History.Count(), controlstate);
  SVDRP->Reply(250, "messages lock=%s count=%d responses lock=%s count=%d history lock=%s count=%d state=%d", Messages.GetLock() ? "true" : "false", Messages.Count(), Responses.GetLock() ? "true" : "false", Responses.Count(), History.GetLock() ? "true" : "false", History.Count(), controlstate);
}
#endif

VDRPLUGINCREATOR(cPluginStatusAndQuestion); // Don't touch this!
