/*
 * exec: A plugin for the Video Disk Recorder
 * Copyright (C) 2007  Winfried Koehler
 * Licence GPL
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include <vdr/plugin.h>
#include <vdr/tools.h>
#include "exec.h"
#include "menu.h"
#include "exectimer.h"
#if VDRVERSNUM < 10507
#include "i18n.h"
#include "compat.h"
#endif //VDRVERSNUM < 10507


static const char *VERSION        = "0.0.2";
static const char *DESCRIPTION    = "execute shell commands";
static const char *MAINMENUENTRY  = "Exec Timers";

cExecSetup ExecSetup;
int myState, logState;

const char * ExecVersion(void) { return VERSION; }

class cPluginExec : public cPlugin {
private:
  // Add any member variables or functions you may need here.
  char * confdir;
  cTaskThread * TaskThread;

public:
  cPluginExec(void);
  virtual ~cPluginExec();
          char       *ConfDir(void) const { return confdir; }
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return tr(DESCRIPTION); }
  virtual const char *CommandLineHelp(void);
  virtual bool ProcessArgs(int argc, char *argv[]);
  virtual bool Initialize(void);
  virtual bool Start(void);
  virtual void Stop(void);
  virtual void Housekeeping(void);
  virtual void MainThreadHook(void);
  virtual cString Active(void);
  virtual time_t WakeupTime(void);
  virtual const char *MainMenuEntry(void) { return ExecSetup.showMainMenu? tr(MAINMENUENTRY) : NULL; }
  virtual cOsdObject *MainMenuAction(void);
  virtual cMenuSetupPage *SetupMenu(void);
  virtual bool SetupParse(const char *Name, const char *Value);
  virtual bool Service(const char *Id, void *Data = NULL);
  virtual const char **SVDRPHelpPages(void);
  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
  };

cPluginExec::cPluginExec(void)
{
  // 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!
}

cPluginExec::~cPluginExec()
{
  // Clean up after yourself!
}

const char *cPluginExec::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.
  return NULL;
}

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

bool cPluginExec::Initialize(void)
{
  // Initialize any background activities the plugin shall perform.

#if VDRVERSNUM < 10507
  /*
   * oldstyle internationalization;
   * officially unsupported, but implemented anyhow...
   */
  RegisterI18n(ExecPhrases); 
#endif //VDRVERSNUM < 10507

  return true;
}

bool cPluginExec::Start(void)
{
  // Start any background activities the plugin shall perform.
  asprintf(&confdir, ConfigDirectory()); 
  ExecTimers.Load(AddDirectory(confdir, "exectimers.conf"));
  ExecTimers.Modified(myState); /* sync myState to ExecTimers.state */
  ExecLog.Load("/var/log/exec.log");
  ExecLog.Modified(logState);
  TaskThread = new cTaskThread(&myState,&logState);
  return true;
}

void cPluginExec::Stop(void)
{// Stop any background activities the plugin is performing.
  ExecTimers.Save();
  ExecLog.Save();
  if (TaskThread) TaskThread->Stop();

#if VDRVERSNUM < 10501
#ifndef PATCH_SHUTDOWN_REWRITE
  /* no possibility to wakeup with old vdr versions;
   * thus print some warning message
   */
  WarnNoWakeup();
#endif
#endif
}

void cPluginExec::Housekeeping(void)
{ // Perform any cleanup or other regular tasks. 
}

void cPluginExec::MainThreadHook(void)
{
  // Perform actions in the context of the main program thread.
  // WARNING: Use with great care - see PLUGINS.html!
}

cString cPluginExec::Active(void)
{
  // Return a message string if shutdown should be postponed
  return NULL;
}

time_t cPluginExec::WakeupTime(void)
{ // Return custom wakeup time for shutdown script
  cExecTimer *et = ExecTimers.GetNextWakeupTimer();

  if (et) {
    /* a wakeup exectimer will wake up the
     * computer "Recordings->Margin at start"
     * minutes before timer event
     */
    log(4,"next wakeup=%d, now=%d",et->StartTime() - Setup.MarginStart * 60,Now());
    return et->StartTime() - Setup.MarginStart * 60;
    }
  else {
    log(4, "no wakeup timers.");
    return 0;
    }
}

cOsdObject *cPluginExec::MainMenuAction(void)
{
  return new cMenuExecTimers;
}

cMenuSetupPage *cPluginExec::SetupMenu(void)
{
  // Return a setup menu in case the plugin supports one.
  return new cMenuExec;
}

bool cPluginExec::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  if      (!strcasecmp(Name, "logLevel"))        ExecSetup.logLevel     =atoi(Value);
  else if (!strcasecmp(Name, "logFile"))         ExecSetup.logFile      =atoi(Value);
  else if (!strcasecmp(Name, "logLength"))       ExecSetup.logLength    =atoi(Value);
  else if (!strcasecmp(Name, "showMainMenu"))    ExecSetup.showMainMenu =atoi(Value);
  else if (!strcasecmp(Name, "enableSVDRP"))     ExecSetup.enableSVDRP  =atoi(Value);
  else return false;
  return true;
}

bool cPluginExec::Service(const char *Id, void *Data)
{
  // Handle custom service requests from other plugins
  return false;
}

const char **cPluginExec::SVDRPHelpPages(void)
{
  static const char * HelpPages[] = {
    "NEWT <DAY(YYYY-MM-DD/-1/MTWTFSS)>:<TIME(hhmm/-1)>:0:<ACTIVE(0/1):<ACTION(1..4)>:<WAKEUP(0/1):<DETACH(0/1):<COMMANDSTR(char[1..255])>",
    "LSTT [<INDEX>] lists all/specified timer(s)",
    "DELT <INDEX> delete specified timer",
    "RUNT <INDEX> run specified timer now",
    "CLRT clear timer list",
    "NACT next active timer",
    "NWUT next wakeup timer",
    NULL
    };
  return HelpPages;
}

cString cPluginExec::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
  if (! ExecSetup.enableSVDRP) {
    ReplyCode=550; // not executed.
    return "exec: SVDRP support disabled by setup.";
    }
  if (strcasecmp(Command, "NEWT") == 0) {
    ExecTimers.IncBeingEdited();
    cExecTimer *et=new cExecTimer();
    if (! et->Parse(Option)) {
      free(et);
      ExecTimers.DecBeingEdited();
      ReplyCode=501; /* syntax err */
      return "NEWT: error reading arguments.";
      }
    log(EXEC_LOG + 3,"new: %s",*et->ToSVDRDescr());
    ExecTimers.Add(et);
    ExecTimers.DecBeingEdited();
    ExecTimers.SetModified();
    ReplyCode=250; /* success */
    return "new timer accepted.";
    }

  if (strcasecmp(Command, "LSTT") == 0) {
    int index=-1;
    char * buf;
    ReplyCode=250;
    if (1 == sscanf(Option, "%d",&index)) { /* timer index was specified */
      cExecTimer *et = ExecTimers.Get(index);
      if (! et) {
        ReplyCode=501; /* syntax err */
        return "invalid timer";
        }      
      asprintf(&buf, "%s\n",*et->ToSVDRDescr());
      ReplyCode=250; /* success */
      return cString(buf, true);
      }
    buf=NULL;
    if (! ExecTimers.Count())
      return "no timers";
    for (index = 0; index < ExecTimers.Count(); index++) {
      ReplyCode=250; /* success */
      cExecTimer *et = ExecTimers.Get(index);
      if (et) {
        if (! buf)
          asprintf(&buf,"%s\n", *et->ToSVDRDescr());
        else
          asprintf(&buf,"%s%s\n", buf, *et->ToSVDRDescr());
        }
      else { /* should never happen. */
        log(0, "cPluginExec::SVDRPCommand(%s, %s)", Command, Option);
        ReplyCode=554;
        return "Invalid pointer in plugin exec.\n Please report this error with logfile to plugins author.";
        }
      }
    return cString(buf, true);
    }

  if (strcasecmp(Command, "DELT") == 0) {
    int index=-1;
    if (1 == sscanf(Option, "%d",&index)) {
      cExecTimer *et = ExecTimers.Get(index);
      if (! et) {
        ReplyCode=501; /* syntax err */
        return "invalid timer";
        }
      ExecTimers.IncBeingEdited();
      log(EXEC_LOG + 3,"deleted: %s", *et->ToSVDRDescr());
      ExecTimers.Del(et);
      ExecTimers.DecBeingEdited();
      ExecTimers.SetModified();
      ReplyCode=250;
      return "timer deleted";
      }
    ReplyCode=501; /* syntax err */
    return "DELT: error reading arguments.";    
    }

  if (strcasecmp(Command, "RUNT") == 0) {
    int index=-1;
    if (1 == sscanf(Option, "%d",&index)) {
      cExecTimer *et = ExecTimers.Get(index);
      if (! et) {
        ReplyCode=501; /* syntax err */
        return "invalid timer";
        }
      log(EXEC_LOG + 3,"run: %s", *et->ToSVDRDescr());
      et->Execute();
      ReplyCode=250;
      return "timer command started";
      }
    ReplyCode=501; /* syntax err */
    return "DELT: error reading arguments.";    
    }

  if (strcasecmp(Command, "CLRT") == 0) {
    ExecTimers.IncBeingEdited();
    while (1) {
      cExecTimer *et = ExecTimers.Get(0);
      if (! et) break;
      ExecTimers.Del(et);
      }
    log(EXEC_LOG + 3, "list cleared");   
    ExecTimers.DecBeingEdited();
    ExecTimers.SetModified();
    ReplyCode=250;
    return "list cleared"; 
    }

  if (strcasecmp(Command, "NACT") == 0) {
    char * buf;
    cExecTimer *et = ExecTimers.GetNextActiveTimer();
    ReplyCode=250;
    if (et)
      asprintf(&buf,"next active timer %s", *et->ToSVDRDescr());
    else
      asprintf(&buf,"no active timers.");
    return buf;
    }

  if (strcasecmp(Command, "NWUT") == 0) {
    char * buf;
    cExecTimer *et = ExecTimers.GetNextWakeupTimer();
    ReplyCode=250;
    if (et)
      asprintf(&buf,"next wakeup timer %s", *et->ToSVDRDescr());
    else
      asprintf(&buf,"no wakeup timers.");
    return buf;
    }

  return NULL; /* unknown command */
}

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