/*
 * undelete: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: undelete.c 0.2 2004/10/10 12:23:20 hflor Exp $
 */

#include "undelete.h"
#include "menuundelete.h"
#include "menusetup.h"
#include "i18n.h"
#include "vdrtools.h"
#include <getopt.h>
#include <ctype.h>
#include <vdr/recording.h>

static const char *VERSION           = "0.0.2";
static const char *DESCRIPTION       = "undelete for recordings";

// Global variables that control the overall behaviour:

// var pos2
tParamFile  undeleteconfigfile     = { false, false, false, "undelete.conf", "undelete.conf" };
tParamInt   commandline_preference = { false, false, false, true };
tParamInt   confirmdel             = { false, false, false, true };
tParamInt   confirmdeldir          = { false, false, false, true };
tParamInt   confirmundel           = { false, false, false, true };
tParamInt   confirmundeldir        = { false, false, false, true };
tParamFile  mainmenu_name          = { false, false, false, "Undelete recordings" };
tParamInt   mainmenu_visible       = { false, false, false, false };
tParamInt   verbose                = { false, false, false, false, true };

char        plugin_name[MaxFileName]  = "Undelete";
bool        PurgeRecording = false;
bool        SalvageRecording = false;
#if VDRVERSNUM >= 10311
cRecordings DeletedRecordings(true);
#else
cRecordings DeletedRecordings;
#endif
char        *WorkFilename = NULL;

void DisplaySetings(void)
{
  #define WriteSource(T)   plugin_name, T.r ? 'r' : ' ', T.c ? 'c' : ' '
  #define IsDefault(T)     WriteSource(T), T.u == T.d ? '*' : ' '
  #define IsDefaultFile(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", IsDefaultFile(mainmenu_name), mainmenu_name.u);
    isyslog("%s: confirm undelete           = [ %c%c%c ] %s", BoolValue(confirmundel));
    isyslog("%s: confirm undelete directory = [ %c%c%c ] %s", BoolValue(confirmundeldir));
    isyslog("%s: confirm delete             = [ %c%c%c ] %s", BoolValue(confirmdel));
    isyslog("%s: confirm delete directory   = [ %c%c%c ] %s", BoolValue(confirmdeldir));
    isyslog("%s: verbose                    = [ %c%c%c ] %s", BoolValue(verbose));
  }

#ifdef UND_Debug1
  if (!verbose.u)
  {
    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", IsDefaultFile(mainmenu_name), mainmenu_name.u);
    dsyslog("%s: confirm undelete           = [ %c%c%c ] %s", BoolValue(confirmundel));
    dsyslog("%s: confirm undelete directory = [ %c%c%c ] %s", BoolValue(confirmundeldir));
    dsyslog("%s: confirm delete             = [ %c%c%c ] %s", BoolValue(confirmdel));
    dsyslog("%s: confirm delete directory   = [ %c%c%c ] %s", BoolValue(confirmdeldir));
    dsyslog("%s: verbose                    = [ %c%c%c ] %s", BoolValue(verbose));
  }
#endif

  #undef WriteSource
  #undef IsDefault
  #undef IsDefaultFile
  #undef BoolValue
}

void ExpandEnvironment(tParamFile *FileStruc)
{
#ifdef UND_Debug2
  dsyslog("%s: ExpandEnvironment text=%s", plugin_name, FileStruc->u);
#endif
  char *s;
  char *p;
  strn0cpy(FileStruc->e, FileStruc->u, sizeof(FileStruc->e));
  while ((s = strstr(FileStruc->e, "$(")) || (s = strstr(FileStruc->e, "${")))
  {
    char c = *(s + 1) == '(' ? ')' : '}';
    p = strchr(s, c); // search closing ) or }
    if (p)
    {
      *p++ = 0;
      *s = 0;
      s += 2;
      char *e = getenv(s);
      if (e)
      {
        char *buffer = NULL;
        asprintf(&buffer, "%s%s%s", FileStruc->e, e, p);
        strn0cpy(FileStruc->e, buffer, sizeof(FileStruc->e));
        FREENULL(buffer);
      } else
      {
        esyslog("%s: environmentvariable '%s' not found path=%s", plugin_name, s, FileStruc->u);
        FileStruc->e[0] = 0;
      }
    } else
    {
      esyslog("%s: missing ')' after '$(' path=%s", plugin_name, FileStruc->u);
      FileStruc->e[0] = 0;
    }
  }
  while ((p = strstr(FileStruc->e, "//")))
    strcpy(p, p + 1);
#ifdef UND_Debug2
  dsyslog("%s: ExpandEnvironment return=%s", plugin_name, FileStruc->e);
#endif
}

// --- cPluginUndelete ----------------------------------------------------------

cPluginUndelete::cPluginUndelete(void)
{
#ifdef UND_Debug1
  dsyslog("%s: cPluginUndelete::cPluginUndelete", 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!
}

cPluginUndelete::~cPluginUndelete()
{
#ifdef UND_Debug1
  dsyslog("%s: cPluginUndelete::~cPluginUndelete", plugin_name);
#endif
  // Clean up after yourself!
}

bool cPluginUndelete::ProcessArg(int argc, char *argv[])
{
  int c;
  static struct option long_options[] = {
    { "confirm_del_rec",           no_argument,       NULL, 'c' },
    { "not_confirm_del_rec",       no_argument,       NULL, 'C' },
    { "confirm_del_dir",           no_argument,       NULL, 'd' },
    { "not_confirm_del_dir",       no_argument,       NULL, 'D' },
    { "visible_in_mainmenu",       no_argument,       NULL, 'm' },
    { "hide_in_mainmenu",          no_argument,       NULL, 'M' },
    { "mainmenu_name",             required_argument, NULL, 'n' },
    { "confirm_undel_rec",         no_argument,       NULL, 's' },
    { "not_confirm_undel_rec",     no_argument,       NULL, 'S' },
    { "confirm_undel_dir",         no_argument,       NULL, 't' },
    { "not_confirm_undel_dir",     no_argument,       NULL, 'T' },
    { "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 SetvalueFile(T) T.c = true; strn0cpy(T.u, optarg, sizeof(T.u))

  while ((c = getopt_long(argc, argv, "cCdDmMn:sStTvV", long_options, NULL)) != -1)
  {
    // var pos4
    switch (c)
    {
      case 1:   commandline_preference.h = true;
                break;
      case 'c': Setvalue(confirmdel) = true;
                break;
      case 'C': Setvalue(confirmdel) = false;
                break;
      case 'd': Setvalue(confirmdeldir) = true;
                break;
      case 'D': Setvalue(confirmdeldir) = false;
                break;
      case 'm': Setvalue(mainmenu_visible) = true;
                break;
      case 'M': Setvalue(mainmenu_visible) = false;
                break;
      case 'n': SetvalueFile(mainmenu_name);
                Setvalue(mainmenu_visible) = true;
                break;
      case 's': Setvalue(confirmundel) = true;
                break;
      case 'S': Setvalue(confirmundel) = false;
                break;
      case 't': Setvalue(confirmundeldir) = true;
                break;
      case 'T': Setvalue(confirmundeldir) = false;
                break;
      case 'v': Setvalue(verbose) = true;
                break;
      case 'V': Setvalue(verbose) = false;
                break;
      default:  return false;
    }
  }

  #undef Setvalue
  #undef SetvalueFile

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

const char *cPluginUndelete::Version(void)
{
  return VERSION;
}

const char *cPluginUndelete::Description(void)
{
  return tr(DESCRIPTION);
}

const char *cPluginUndelete::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.            column 80 --> |
  return tr("  -m        --visible_in_mainmenu    Show the plugin in the mainmenu\n"
            "  -M        --hide_in_mainmenu       Hide the plugin in the mainmenu\n"
            "                                     You can start the Plugin in the setupmenu\n"
            "  -n Name   --mainmenu_name=Name     Select Name for entry in the mainmenu\n"
            "                                     (set also -m)\n"
            "  -c        --confirm_del_rec        show confirmation for delete recording\n"
            "  -C        --not_confirm_del_rec    delete recording without confirmation\n"
            "  -d        --confirm_del_dir        show confirmation for delete directory\n"
            "  -D        --not_confirm_del_dir    delete directory without confirmation\n"
            "  -s        --confirm_undel_rec      show confirmation for undelete recording\n"
            "  -S        --not_confirm_undel_rec  undelete recording without confirmation\n"
            "  -t        --confirm_undel_dir      show confirmation for undelete directory\n"
            "  -T        --not_confirm_undel_dir  undelete directory without confirmation\n"
            "  -v        --verbose                Enable more logging\n"
            "  -V        --noverbose              Disable more loggig\n"
            "            --nosetup_commandline    Hide the 'Preferr Command Line Parameter'\n"
            "                                     form setup-menu\n"
            "  @config-file                       Read also parameters from this file"
            "\n"
            "  note: recording means deleted recordings (there not show in VDR)");

/* only for translation:
         "  -m        --visible_in_mainmenu    Plugin wird im Hautmenü angezeigt\n"
         "  -M        --hide_in_mainmenu       kein Eintrag im Hauptmenü für das Plugin\n"
         "                                     es kann über das Setup-Menü gestartet werden\n"
         "  -n Name   --mainmenu_name=Name     Name für den Eintrag im Hauptmenü\n"
         "                                     (diese Option setzt auch -m)\n"
         "  -c        --confirm_del_rec        Bestätigung für das Löschen von Aufnahmen\n"
         "  -C        --not_confirm_del_rec    keine Bestätigung (löschen Aufnahmen)\n"
         "  -d        --confirm_del_dir        Bestätigung beim Löschen von Verzeichnissen\n"
         "  -D        --not_confirm_del_dir    keine Bestätigung (löschen Verzeichnissen)\n"
         "  -s        --confirm_undel_rec      Bestätigung Wiederherstellung von Aufnahmen\n"
         "  -S        --not_confirm_undel_rec  keine Best. Wiederherstellung von Aufnahmen\n"
         "  -t        --confirm_undel_dir      Bestätigung Wiederherstellung von Verzei.\n"
         "  -T        --not_confirm_undel_dir  keine Best. Wiederherstellung von Verzei.\n"
         "  -v        --verbose                erweiterte Protokollierung aktivieren\n"
         "  -V        --noverbose              keine erweiterte Protokollierung\n"
         "            --nosetup_commandline    Option 'Kommandozeile hat Vorrang'\n"
         "                                     im Setup-Menü verbergen\n"
         "  @config-file                       Komandozeilenoptionen auch aus der angegebenen\n"
         "                                     Datei lesen\n"
         "\n"
         "  Hinweis: Mit Aufnahmen sind hier die gelöschten Aufnahmen gemeint"
*/
         // free: a b e f g h i j k l o p q r u w x y z
}

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

bool cPluginUndelete::Start(void)
{
  // Start any background activities the plugin shall perform.
  RegisterI18n(Phrases);

#ifdef UND_Debug1
  dsyslog("%s: cPluginUndelete::Start undeleteconfigfile=%s", plugin_name, undeleteconfigfile.u);
#endif

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

    if (!access(undeleteconfigfile.u, F_OK))
    {
      f = fopen(undeleteconfigfile.u, "r");
#ifdef UND_Debug1
      dsyslog("%s: cPluginUndelete::Start open undeleteconfigfile=%s", plugin_name, undeleteconfigfile.u);
#endif
    } else
    {
      f = fopen(p, "r");
#ifdef UND_Debug1
      dsyslog("%s: cPluginUndelete::Start open undeleteconfigfile=%s", plugin_name, p);
#endif
    }
    free(p);
    free(q);
    if (!f)
    {
      esyslog("%s: ERROR: cannot open config file: [%s]%s", plugin_name, ConfigDirectory(""), undeleteconfigfile.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(""), undeleteconfigfile.u);
        return false;
      }
      while(fargc) free(fargv[--fargc]);
  } else
  {
    free(p);
    free(q);
    if (strcmp(undeleteconfigfile.u, undeleteconfigfile.d))
    {
      esyslog("%s: ERROR: config file not found: [%s]%s", plugin_name, ConfigDirectory(""), undeleteconfigfile.u);
      return false;
    } else if (verbose.u)
      isyslog("%s: INFO: config file not found: [%s]%s", plugin_name, ConfigDirectory(""), undeleteconfigfile.u);
  }

  strn0cpy(mainmenu_name.d, tr("Menu$Undelete recordings"), sizeof(mainmenu_name.d));
  
  // 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 SetParamFile(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
  SetParamFile (undeleteconfigfile);
  SetParam     (confirmdel);
  SetParam     (confirmdeldir);
  SetParam     (confirmundel);
  SetParam     (confirmundeldir);
  SetParamFile (mainmenu_name);
  SetParam     (mainmenu_visible);
  SetParam     (verbose);

  #undef SetParam
  #undef SetParamFile

  if (verbose.u)
    isyslog("%s: Start", plugin_name);
  DisplaySetings();
  return true;
}

const char *cPluginUndelete::MainMenuEntry(void)
{
  return mainmenu_visible.u ? mainmenu_name.u : NULL;
}

cOsdObject *cPluginUndelete::MainMenuAction(void)
{
#ifdef UND_Debug1
  dsyslog("%s: cPluginUndelete::MainMenuAction", plugin_name);
#endif
  return new cMenuUndelete;
}

cMenuSetupPage *cPluginUndelete::SetupMenu(void)
{
#ifdef UND_Debug1
  dsyslog("%s: cPluginUndelete::SetupMenu", plugin_name);
#endif
  return new cMenuSetupUndelete;
}


bool cPluginUndelete::SetupParse(const char *Name, const char *Value)
{
  #define SetParam(T) { T.r = true; T.s = strtol(Value, NULL, 0); }
  #define SetParamFile(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, "confdel"))      SetParam     (confirmdel)
  else if (!strcasecmp(Name, "confdeldir"))   SetParam     (confirmdeldir)
  else if (!strcasecmp(Name, "confundel"))    SetParam     (confirmundel)
  else if (!strcasecmp(Name, "confundeldir")) SetParam     (confirmundeldir)
  else if (!strcasecmp(Name, "name"))         SetParamFile (mainmenu_name)
  else if (!strcasecmp(Name, "visible"))      SetParam     (mainmenu_visible)
  else if (!strcasecmp(Name, "verbose"))      SetParam     (verbose)
  else
    return false;
  return true;

  #undef SetParam
  #undef SetParamChar
  #undef SetParamFile
}

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