/*
 * rotor.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include <linux/dvb/frontend.h>
#include <sys/ioctl.h>
#include <vdr/diseqc.h>
#include <vdr/plugin.h>
#include <vdr/dvbdevice.h>
#include "menu.h"
#include "status.h" 

static const char *VERSION        = "0.1.4";
static const char *DESCRIPTION    = tr("Controlling a Rotor");
static const char *MAINMENUENTRY  = "Rotor";

// --- cPluginRotor ---------------------------------------------------------

class cPluginRotor : public cPlugin {
private:
  // Add any member variables or functions you may need here.
  cStatusMonitor *statusMonitor;
public:
  cPluginRotor(void);
  virtual ~cPluginRotor();
  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 Housekeeping(void);
  virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); }
  virtual cOsdObject *MainMenuAction(void);
  virtual cMenuSetupPage *SetupMenu(void);
  virtual bool SetupParse(const char *Name, const char *Value);
#if VDRVERSNUM >= 10331
  virtual const char **SVDRPHelpPages(void);
  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
#endif
};

cPluginRotor::cPluginRotor(void)
{
  statusMonitor = NULL;
  // 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!
}

cPluginRotor::~cPluginRotor()
{
  // Clean up after yourself!
  delete statusMonitor;
}

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

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

bool cPluginRotor::Initialize(void)
{
  // Initialize any background activities the plugin shall perform.
  return true;
}

bool cPluginRotor::Start(void)
{
  // Start any background activities the plugin shall perform.
  statusMonitor = new cStatusMonitor;
//  RegisterI18n(Phrases);
  RotorPositions.Load(AddDirectory(ConfigDirectory(), "rotor.conf"), true);
  cRotorPos *t = new cRotorPos;
  t->AddDescription(tr("Not in DISEQC.CONF"));
  RotorPositions.Ins(t);

//List.Add(tr("Not in DISEQC.CONF"),"",0,0);
  cDiseqc *diseqc;
  uchar codes[4];
  for(const cSource *source=Sources.First();source;source=(cSource *)source->Next())
  {
    if ((source->Code() & 0xC000) != 0x8000)
      continue;
    if ((diseqc=Diseqcs.Get(source->Code(),12000,'h')) || (diseqc=Diseqcs.Get(source->Code(),12000,'v')) || (diseqc=Diseqcs.Get(source->Code(),12000,'l')) || (diseqc=Diseqcs.Get(source->Code(),12000,'r'))) 
    {
      char *c=strdup(diseqc->Commands());
      while (c = strchr(c, '['))
      {
        char *e = strchr(++c, ']');
        int n = 0;
        if (e)
        {
          char *t = c;
          char *p = c;
          c=e; 
          while (t < e)
          { 
            if (n > 4)
              break;
            errno = 0;
            int m = strtol(t, &p, 16);
            if (!errno && p != t && 0 <= m && m <= 255)
            {
              codes[n++] = m;
              t = skipspace(p);
            }  
            else
              break;
          }
        }
        if (n==4 && (codes[0]==0xe0 || codes[0]==0xe1) && (codes[1]==0x30 || codes[1]==0x31) && codes[2]==0x6b)
        {
          cRotorPos *p = RotorPositions.GetfromSource(source->Code());
          if (p==RotorPositions.First())
            RotorPositions.Add(new cRotorPos(source->Code(),codes[3])); 
          else
            p->SetPos(codes[3]);
          break;
        }
      }
      free(c);
    }
    cRotorPos *p = RotorPositions.GetfromSource(source->Code());
    if (p==RotorPositions.First())
      RotorPositions.Add(p = new cRotorPos(source->Code(),0));
    char buf[200];
    sprintf(buf,"%s - %s",*cSource::ToString(source->Code()),source->Description());
    if (p)
      p->AddDescription(buf);
  }
  RotorPositions.Save();
  char buffer[PATH_MAX];
  snprintf(buffer, sizeof(buffer), "/dev/dvb/adapter%d/frontend0", data.DvbCard);
  data.fd_frontend = open(buffer,0);
  return true;
}

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

cOsdObject *cPluginRotor::MainMenuAction(void)
{
  // Perform the action when selected from the main VDR menu.
  return new cMainMenuRotor;
}

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

bool cPluginRotor::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  if      (!strcasecmp(Name, "DVB-Karte"))      data.DvbCard = atoi(Value);
  else if (!strcasecmp(Name, "Wiederholen"))    data.repeat = atoi(Value);
  else if (!strcasecmp(Name, "Latitude"))       data.Lat = atoi(Value);
  else if (!strcasecmp(Name, "SouthNorth"))     data.SN = atoi(Value);
  else if (!strcasecmp(Name, "Longitude"))      data.Long = atoi(Value);
  else if (!strcasecmp(Name, "EastWest"))       data.EW = atoi(Value);
  else if (!strcasecmp(Name, "UseGotoX"))       data.UseGotoX = atoi(Value);
  else if (!strcasecmp(Name, "UseGotoPos"))     data.UseGotoPos = atoi(Value);
  else if (!strcasecmp(Name, "Card1"))          data.card[0] = atoi(Value);
  else if (!strcasecmp(Name, "Card2"))          data.card[1] = atoi(Value);
  else if (!strcasecmp(Name, "Card3"))          data.card[2] = atoi(Value);
  else if (!strcasecmp(Name, "Card4"))          data.card[3] = atoi(Value);
  else if (!strcasecmp(Name, "ShowInfo"))       data.ShowInfo = atoi(Value);
  else
    return false;
  return true;
}

#if VDRVERSNUM >= 10331

const char **cPluginRotor::SVDRPHelpPages(void)
{
  static const char *HelpPages[] = {
    "DRIVE east | west\n"
    "    Drives the rotor east or west.",
    "HALT\n"
    "    Halts the rotor.",
    "STEPS east | west [ <steps> ]"
    "    Drives the rotor one (or the given) steps east or west.",
    "GOTO <position>"
    "    Lets the motor drive to the position.",
    "STORE <position>"
    "    Stores the current position.",
    "RECALC [ <satellite position> ]\n"
    "    Recalculates the satellite positions.",
    "LIMITS on | off\n"
    "    Enable / Disable Softlimits."
    "SETL east | west\n"
    "    Sets east / west softlimit.",
    "GOTOX <source>\n"
    "    Drives the rotor to the given source (like S13E)",
    NULL
    };
  return HelpPages;
}

cString cPluginRotor::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
  if (strcasecmp(Command, "DRIVE") == 0) {
    if (*Option) {
      if (strcasecmp(Option, "east") == 0) {
         DiseqcCommand(DriveEast);
         return cString::sprintf("Started to drive the rotor eastward");
         }
      else if (strcasecmp(Option, "west") == 0) {
         DiseqcCommand(DriveWest);
         return cString::sprintf("Started to drive the rotor westward");
         }
      else {
         return cString::sprintf("Unknown option: \"%s\"", Option);
         }
      }
    else {
      return cString::sprintf("No argument given for command DRIVE");
      }
    }  
  else if (strcasecmp(Command, "HALT") == 0) {
    DiseqcCommand(Halt);
    return cString::sprintf("Rotor was halted");
    }
  else if (strcasecmp(Command, "STEPS") == 0) {
    if (*Option) {
      char buf[strlen(Option) + 1];
      strcpy(buf, Option);
      const char *delim = " \t";
      char *strtok_next;
      char *p = strtok_r(buf, delim, &strtok_next);
      if (strcasecmp(p, "east") == 0 || strcasecmp(p, "west") == 0) {
          char *b = strtok_r(NULL, delim, &strtok_next);
          int steps=1;
          if (b)
            steps=atoi(b);
          if (strcasecmp(p, "east") == 0) {
             DiseqcCommand(DriveStepsEast,steps);
             return cString::sprintf("Drove %d steps eastward.",steps);
             }
          else {
             DiseqcCommand(DriveStepsWest,steps);
             return cString::sprintf("Drove %d steps westward.",steps);
             }
         }
      else {
         return cString::sprintf("Unknown option: \"%s\"", Option);
         }
      }
    else {
      return cString::sprintf("No argument given for command STEPS");
      }
    }
  else if (strcasecmp(Command, "GOTO") == 0) {
    if (*Option) {
      DiseqcCommand(Goto,atoi(Option));
      return cString::sprintf("Goto position %d",atoi(Option));
      }
    else
      return cString::sprintf("No argument given for command GOTO");
    }
  else if (strcasecmp(Command, "STORE") == 0) {
    if (*Option) {
      DiseqcCommand(Store,atoi(Option));
      return cString::sprintf("Stored position %d",atoi(Option));
      }
    else
      return cString::sprintf("No argument given for command STORE");
    }
  else if (strcasecmp(Command, "RECALC") == 0) {
    int number = 0;
    if (*Option) 
      number = atoi(Option);
    DiseqcCommand(Recalc,number);
    return cString::sprintf("Recalculated positions");
    }
  else if (strcasecmp(Command, "LIMITS") == 0) {
    if (*Option) {
      if (strcasecmp(Option, "on") == 0) {
         DiseqcCommand(LimitsOn);
         return cString::sprintf("Turned limits on");
         }
      else if (strcasecmp(Option, "off") == 0) {
         DiseqcCommand(LimitsOff);
         return cString::sprintf("Turned limits off");
         }
      else {
         return cString::sprintf("Unknown option: \"%s\"", Option);
         }
      }
    else {
      return cString::sprintf("No argument given for command LIMITS");
      }
    }
  else if (strcasecmp(Command, "SETL") == 0) {
    if (*Option) {
      if (strcasecmp(Option, "east") == 0) {
         DiseqcCommand(SetEastLimit);
         return cString::sprintf("Set east limit");
         }
      else if (strcasecmp(Option, "west") == 0) {
         DiseqcCommand(SetWestLimit);
         return cString::sprintf("Set west limit");
         }
      else {
         return cString::sprintf("Unknown option: \"%s\"", Option);
         }
      }
    else {
      return cString::sprintf("No argument given for command SETL");
      }
    }
  else if (strcasecmp(Command, "GOTOX") == 0) {
    if (*Option) {
      int i=cSource::FromString(Option);
      if (!i)
        return cString::sprintf("Unknown source");
      GotoX(i);
      return cString::sprintf("Goto angular position %s",Option);
      }
    else
      return cString::sprintf("No argument given for command GOTOX");
    }
  return NULL;  
}

#endif

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

