/*
 * PremiereEpg plugin to VDR (C++)
 *
 * (C) 2005 Stefan Huelswitt <s.huelswitt@gmx.de>
 *
 * This code is base on the commandline tool premiereepg2vdr
 * (C) 2004-2005 by Axel Katzur software@katzur.de
 * but has been rewritten from scratch
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

#include <vdr/plugin.h>
#include <vdr/filter.h>
#include <vdr/epg.h>
#include <vdr/channels.h>
#include <vdr/dvbdevice.h>
#include <vdr/i18n.h>
#include <libsi/section.h>
#include <libsi/descriptor.h>

//#define DEBUG
//#define DEBUG2

#ifdef DEBUG
#define d(x) { (x); }
#else
#define d(x) ; 
#endif
#ifdef DEBUG2
#define d2(x) { (x); }
#else
#define d2(x) ; 
#endif

#define PMT_SCAN_TIMEOUT  10  // seconds
#define PMT_SCAN_IDLE     300 // seconds

static const char *VERSION        = "0.0.5";
static const char *DESCRIPTION    = "Parses extended Premiere EPG data";

// --- cSetupPremiereEpg -------------------------------------------------------

const char *optPats[] = {
  "%s",
  "%s (Option %d)",
  "%s (O%d)",
  "#%2$d %1$s",
  "[%2$d] %1$s"
  };
#define NUM_PATS (sizeof(optPats)/sizeof(char *))

class cSetupPremiereEpg {
public:
  int OptPat;
  int OrderInfo;
  int RatingInfo;
public:
  cSetupPremiereEpg(void);
  };

cSetupPremiereEpg SetupPE;

cSetupPremiereEpg::cSetupPremiereEpg(void)
{
  OptPat=1;
  OrderInfo=1;
  RatingInfo=1;
}

// --- i18n --------------------------------------------------------------------

const tI18nPhrase Phrases[] = {
/*
*/
  { "PremiereEPG",
    "PremiereEPG",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "Parses extended Premiere EPG data",
    "Liest erweiterte Premiere EPG Daten ein",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "Tag option events",
    "Options Events markieren",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "Show order information",
    "Bestellhinweise anzeigen",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "Show rating information",
    "Altersfreigaben anzeigen",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },

  { "Ordernumber",
    "Bestellnummer",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "Price",
    "Preis",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "Ordering",
    "Bestellen",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "SMS",
    "SMS",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "WWW",
    "WWW",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "Rating",
    "Altersfreigabe",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },
  { "years",
    "Jahre",
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
    "", // TODO
  },

  { NULL }
  };

// --- cMenuSetupPremiereEpg ------------------------------------------------------------

class cMenuSetupPremiereEpg : public cMenuSetupPage {
private:
  cSetupPremiereEpg data;
  const char *optDisp[NUM_PATS];
  char buff[NUM_PATS][32];
protected:
  virtual void Store(void);
public:
  cMenuSetupPremiereEpg(void);
  };

cMenuSetupPremiereEpg::cMenuSetupPremiereEpg(void)
{
  data=SetupPE;
  SetSection(tr("PremiereEPG"));
  optDisp[0]=tr("off");
  for(unsigned int i=1; i<NUM_PATS; i++) {
    snprintf(buff[i],sizeof(buff[i]),optPats[i],"Event",1);
    optDisp[i]=buff[i];
    }
  Add(new cMenuEditStraItem(tr("Tag option events"),&data.OptPat,NUM_PATS,optDisp));
  Add(new cMenuEditBoolItem(tr("Show order information"),&data.OrderInfo));
  Add(new cMenuEditBoolItem(tr("Show rating information"),&data.RatingInfo));
}

void cMenuSetupPremiereEpg::Store(void)
{
  SetupPE=data;
  SetupStore("OptionPattern",SetupPE.OptPat);
  SetupStore("OrderInfo",SetupPE.OrderInfo);
  SetupStore("RatingInfo",SetupPE.RatingInfo);
}

// --- CIT ---------------------------------------------------------------------

namespace SI {

#define CIT_LEN 17

struct cit {
   u_char table_id                               :8;
#if BYTE_ORDER == BIG_ENDIAN
   u_char section_syntax_indicator               :1;
   u_char                                        :3;
   u_char section_length_hi                      :4;
#else
   u_char section_length_hi                      :4;
   u_char                                        :3;
   u_char section_syntax_indicator               :1;
#endif
   u_char section_length_lo                      :8;
   u_char service_id_hi                          :8;
   u_char service_id_lo                          :8;
#if BYTE_ORDER == BIG_ENDIAN
   u_char                                        :2;
   u_char version_number                         :5;
   u_char current_next_indicator                 :1;
#else
   u_char current_next_indicator                 :1;
   u_char version_number                         :5;
   u_char                                        :2;
#endif
   u_char section_number                         :8;
   u_char last_section_number                    :8;
   u_char content_id_hi_hi                       :8;
   u_char content_id_hi_lo                       :8;
   u_char content_id_lo_hi                       :8;
   u_char content_id_lo_lo                       :8;
   u_char duration_h                             :8;
   u_char duration_m                             :8;
   u_char duration_s                             :8;
#if BYTE_ORDER == BIG_ENDIAN
   u_char                                        :4;
   u_char descriptors_loop_length_hi             :4;
#else
   u_char descriptors_loop_length_hi             :4;
   u_char                                        :4;
#endif
   u_char descriptors_loop_length_lo             :8;
};

class CIT : public NumberedSection {
public:
   CIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
   CIT() {}
   int getContentId(void) const;
   time_t getDuration(void) const;
   DescriptorLoop eventDescriptors;
protected:
   virtual void Parse(void);
private:
   const cit *s;
};

int CIT::getContentId(void) const {
   return (HILO(s->content_id_hi)<<16) | (HILO(s->content_id_lo));
}

time_t CIT::getDuration(void) const {
   return DVBTime::getDuration(s->duration_h,s->duration_m,s->duration_s);
}

void CIT::Parse(void) {
#if VDRVERSNUM >= 10343
   int offset=0;
#else
   unsigned int offset=0;
#endif
   data.setPointerAndOffset<const cit>(s, offset);
   eventDescriptors.setData(data+offset,HILO(s->descriptors_loop_length));
}

} // end of namespace

// --- cDescrF2 ----------------------------------------------------------------

class cDescrF2 {
private:
  SI::Descriptor *d;
  SI::CharArray data;
  int idx, loop, nloop, index;
public:
  cDescrF2(SI::Descriptor *D);
  int TransportStreamId(void) { return data.TwoBytes(2); }
  int OrgNetworkId(void)      { return data.TwoBytes(4); }
  int ServiceId(void)         { return data.TwoBytes(6); }
  void Start(void);
  bool Next(void);
  time_t StartTime(void);
  int Index(void) { return index; }
  };

cDescrF2::cDescrF2(SI::Descriptor *D)
{
  d=D;
  data=d->getData();
  Start();
}

void cDescrF2::Start(void)
{
  idx=8; loop=0; nloop=-3; index=-1;
}

bool cDescrF2::Next(void)
{
  loop+=3;
  if(loop>=nloop) {
    idx+=nloop+3;
    if(idx>=d->getLength()) return false;
    loop=0; nloop=data[idx+2];
    }
 index++;
 return true;
}

time_t cDescrF2::StartTime(void)
{
  int off=idx+3+loop;
  return SI::DVBTime::getTime(data[idx+0],data[idx+1],data[off+0],data[off+1],data[off+2]);
}

// --- cFilterPremiereEpg ------------------------------------------------------

class cFilterPremiereEpg : public cFilter {
private:
  int pmtpid, pmtidx, pmtnext;
  //
  void NextPmt(void);
protected:
  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
public:
  cFilterPremiereEpg(void);
  virtual void SetStatus(bool On);
  void Trigger(void);
  };

cFilterPremiereEpg::cFilterPremiereEpg(void)
{
  Trigger();
  Set(0x00,0x00);
}

void cFilterPremiereEpg::Trigger(void)
{
  d(printf("trigger\n"))
  pmtpid=0; pmtidx=0; pmtnext=0;
}

void cFilterPremiereEpg::SetStatus(bool On)
{
  d(printf("setstatus %d\n",On))
  cFilter::SetStatus(On);
  Trigger();
}

void cFilterPremiereEpg::NextPmt(void)
{
  Del(pmtpid,0x02);
  pmtpid=0;
  pmtidx++;
  d(printf("PMT next\n"))
}

void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
  if(Pid==0 && Tid==SI::TableIdPAT) {
    int now=time(0);
    if(!pmtnext || now>pmtnext) {
      if(pmtpid) NextPmt();
      if(!pmtpid) {
        SI::PAT pat(Data,false);
        if(pat.CheckCRCAndParse()) {
          SI::PAT::Association assoc;
          int idx=0;
          for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
            if(!assoc.isNITPid()) {
              if(idx++==pmtidx) {
                pmtpid=assoc.getPid();
                Add(pmtpid,0x02);
                pmtnext=now+PMT_SCAN_TIMEOUT;
                d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
                break;
                }
              }
            }
          if(!pmtpid) {
            pmtidx=0;
            pmtnext=now+PMT_SCAN_IDLE;
            d(printf("PMT scan idle\n"))
            }
          }
        }
      }
    }
  else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
    SI::PMT pmt(Data,false);
    if(pmt.CheckCRCAndParse()) {
      SI::PMT::Stream stream;
      for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
        if(stream.getStreamType()==0x05) {
          SI::CharArray data=stream.getData();
          if((data[1]&0xE0)==0xE0 && (data[3]&0xF0)==0xF0) {
            bool prvData=false, usrData=false;
            SI::Descriptor *d;
            for(SI::Loop::Iterator it; (d=stream.streamDescriptors.getNext(it)); ) {
              switch(d->getDescriptorTag()) {
                case SI::PrivateDataSpecifierDescriptorTag:
                  d(printf("prv: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
                  if(d->getLength()==6 && d->getData().FourBytes(2)==0x000000be)
                    prvData=true;
                  break;
                case 0x90:
                  d(printf("usr: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
                  if(d->getLength()==6 && d->getData().FourBytes(2)==0x0000ffff)
                    usrData=true;
                  break;
                default:
                  break;
                }
              delete d;
              }
            if(prvData && usrData) {
              int pid=stream.getPid();
              d(printf("found citpid 0x%04x",pid))
              if(!Matches(pid,0xA0)) {
                Add(pid,0xA0);
                d(printf(" (added)"))
                }
              d(printf("\n"))
              }
            }
          }
        }
      NextPmt(); pmtnext=0;
      }
    }
  else if(Tid==0xA0 && Source()) {
    SI::CIT cit(Data,false);
    if(cit.CheckCRCAndParse()) {
      cSchedulesLock SchedulesLock(true,10);
      cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
      if(Schedules) {
        int nCount=0;
        time_t firstTime=0;
        SI::Descriptor *d;
        int LanguagePreferenceShort=-1;
        int LanguagePreferenceExt=-1;
        bool UseExtendedEventDescriptor=false;
        SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
        SI::ShortEventDescriptor *ShortEventDescriptor=0;
        char *order=0, *rating=0;
        for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
          switch(d->getDescriptorTag()) {
            case 0xF0: // order information
              if(SetupPE.OrderInfo) {
                static const char *text[] = {
                  "Ordernumber",
                  "Price",
                  "Ordering",
                  "SMS",
                  "WWW"
                  };
                char buff[512];
                int p=0;
                const unsigned char *data=d->getData().getData()+2;
                for(int i=0; i<5; i++) {
                  int l=data[0]; 
                  if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %.*s",tr(text[i]),l,&data[1]);
                  data+=l+1;
                  }
                if(p>0) order=strdup(buff);
                }
              break;
            case 0xF1: // parental rating
              if(SetupPE.RatingInfo) {
                char buff[512];
                int p=0;
                const unsigned char *data=d->getData().getData()+2;
                p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %d %s",tr("Rating"),data[0]+3,tr("years"));
                data+=7;
                int l=data[0]; 
                if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p," (%.*s)",l,&data[1]);
                if(p>0) rating=strdup(buff);
                }
              break;
            case 0xF2: // transmisions
              if(nCount>=0) {
                nCount++;
                cDescrF2 f2(d);
                if(f2.Next()) {
                  if(nCount==1) firstTime=f2.StartTime();
                  else {
                    time_t time=f2.StartTime();
                    if(firstTime<time-5*50 || firstTime>time+5*60)
                      nCount=-1;
                    }
                  }
                }
              break;
            case SI::ExtendedEventDescriptorTag:
              {
              SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
#if VDRVERSNUM < 10332
              if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(eed->languageCode), LanguagePreferenceExt) || !ExtendedEventDescriptors) {
#else
              if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
#endif
                 delete ExtendedEventDescriptors;
                 ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
                 UseExtendedEventDescriptor=true;
                 }
              if(UseExtendedEventDescriptor) {
                 ExtendedEventDescriptors->Add(eed);
                 d=NULL; // so that it is not deleted
                 }
              if(eed->getDescriptorNumber()==eed->getLastDescriptorNumber())
                 UseExtendedEventDescriptor=false;
              }
              break;
            case SI::ShortEventDescriptorTag:
              {
              SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
#if VDRVERSNUM < 10332
              if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(sed->languageCode), LanguagePreferenceShort) || !ShortEventDescriptor) {
#else
              if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
#endif
                 delete ShortEventDescriptor;
                 ShortEventDescriptor=sed;
                 d=NULL; // so that it is not deleted
                 }
              }
              break;
            default:
              break;
            }
          delete d;
          }

        bool Modified=false;
        int optCount=0;
        for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
          if(d->getDescriptorTag()==0xF2) {
            optCount++;

            cDescrF2 f2(d);
            tChannelID channelID(Source(),f2.OrgNetworkId(),f2.TransportStreamId(),f2.ServiceId());
            cChannel *channel=Channels.GetByChannelID(channelID,true);
            if(!channel) continue;

            cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
            if(!pSchedule) {
               pSchedule=new cSchedule(channelID);
               Schedules->Add(pSchedule);
               }

            for(f2.Start(); f2.Next();) {
              u_int16_t EventId=(cit.getContentId()<<4) | f2.Index();
              time_t StartTime=f2.StartTime();

              bool isOpt=false;
              if(f2.Index()==0 && nCount>1) isOpt=true;

              d2(printf("ch=%s id=%04x/%08x %d opt=%d/%d st=%s ",*channelID.ToString(),EventId,cit.getContentId(),f2.Index(),isOpt,optCount,stripspace(ctime(&StartTime))))
              if(StartTime+cit.getDuration()+Setup.EPGLinger*60<time(0)) {
                d2(printf("(old)\n"))
                continue;
                }

              bool newEvent=false;
              cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,StartTime);
              if(!pEvent) {
                 d2(printf("(new)\n"))
#if VDRVERSNUM >= 10325
                 pEvent=new cEvent(EventId);
#else
                 pEvent=new cEvent(channelID,EventId);
#endif
                 if(!pEvent) continue;
                 newEvent=true;
                 }
              else {
                 d2(printf("(upd)\n"))
                 pEvent->SetSeen();
                 if(pEvent->TableID()==0x00) continue;
                 if(Tid==pEvent->TableID() && pEvent->Version()==cit.getVersionNumber()) continue;
                 }
              pEvent->SetEventID(EventId);
              pEvent->SetTableID(Tid);
              pEvent->SetVersion(cit.getVersionNumber());
              pEvent->SetStartTime(StartTime);
              pEvent->SetDuration(cit.getDuration());

              if(ShortEventDescriptor) {
                char buffer[256];
                ShortEventDescriptor->name.getText(buffer,sizeof(buffer));
                if(isOpt) {
                  char buffer2[sizeof(buffer)+32];
                  snprintf(buffer2,sizeof(buffer2),optPats[SetupPE.OptPat],buffer,optCount);
                  pEvent->SetTitle(buffer2);
                  }
                else
                  pEvent->SetTitle(buffer);
                pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
                }
              if(ExtendedEventDescriptors) {
                char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ")+1];
                pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer,sizeof(buffer),": "));
                }
              if(order || rating) {
                int len=(pEvent->Description() ? strlen(pEvent->Description()) : 0) +
                        (order                 ? strlen(order) : 0) +
                        (rating                ? strlen(rating) : 0);
                char buffer[len+32];
                buffer[0]=0;
                if(pEvent->Description()) strcat(buffer,pEvent->Description());
                if(rating)                strcat(buffer,rating);
                if(order)                 strcat(buffer,order);
                pEvent->SetDescription(buffer);
                }

              if(newEvent) pSchedule->AddEvent(pEvent);
#if VDRVERSNUM >= 10318
              pEvent->SetComponents(NULL);
#endif
              pEvent->FixEpgBugs();
              Modified=true;
              }
            if(Modified) {
              pSchedule->Sort();
              Schedules->SetModified(pSchedule);
              }
            }
          delete d;
          }
        delete ExtendedEventDescriptors;
        delete ShortEventDescriptor;
        free(order);
        free(rating);
        }
      }
    }
}

// --- cPluginPremiereEpg ------------------------------------------------------

class cPluginPremiereEpg : public cPlugin {
private:
  struct {
    cFilterPremiereEpg *filter;
    cDevice *device;
    } epg[MAXDVBDEVICES];
public:
  cPluginPremiereEpg(void);
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return tr(DESCRIPTION); }
  virtual bool Start(void);
  virtual void Stop(void);
  virtual cMenuSetupPage *SetupMenu(void);
  virtual bool SetupParse(const char *Name, const char *Value);
  };

cPluginPremiereEpg::cPluginPremiereEpg(void)
{
  memset(epg,0,sizeof(epg));
}

bool cPluginPremiereEpg::Start(void)
{
  RegisterI18n(Phrases);
  for(int i=0; i<MAXDVBDEVICES; i++) {
    cDevice *dev=cDevice::GetDevice(i);
    if(dev) {
      epg[i].device=dev;
      dev->AttachFilter(epg[i].filter=new cFilterPremiereEpg);
      isyslog("Attached premiere EPG filter to device %d",i);
      }
    }
  return true;
}

void cPluginPremiereEpg::Stop(void)
{
  for(int i=0; i<MAXDVBDEVICES; i++) {
    cDevice *dev=epg[i].device;
    if(dev) dev->Detach(epg[i].filter);
    delete epg[i].filter;
    epg[i].device=0;
    epg[i].filter=0;
    }
}

cMenuSetupPage *cPluginPremiereEpg::SetupMenu(void)
{
  return new cMenuSetupPremiereEpg;
}

bool cPluginPremiereEpg::SetupParse(const char *Name, const char *Value)
{
  if      (!strcasecmp(Name, "OptionPattern")) SetupPE.OptPat     = atoi(Value);
  else if (!strcasecmp(Name, "OrderInfo"))     SetupPE.OrderInfo  = atoi(Value);
  else if (!strcasecmp(Name, "RatingInfo"))    SetupPE.RatingInfo = atoi(Value);
  else return false;
  return true;
}

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