/*
 * Analogue TV Player plugin for VDR
 *
 * Copyright (C) 2003, 2005 Andreas Kool <akool@gmx.de>
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 *
 * Version: $Id: epg.c,v 1.19 2005/01/09 18:16:08 akool Exp $
 * Release: $Name:  $
 *
 */

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <cstdlib>
#include <cstring>

#include <vdr/player.h>

#include "epg.h"
#include "player-analogtv.h"

// #define DEBUG
#define MAXSTATIONS 128
#define MAXTHEMES   256

typedef struct {
  char *Name;
  char *ID;
  int   events;
  int   known;
} EPGCHANNEL;

static char *Stations[MAXSTATIONS];
static int   Mode[MAXSTATIONS];
static char *Themes[MAXTHEMES];

static const char *EPGOUT         = "/tmp/epg.out";
static const char *EPGERR         = "/tmp/epg.err";

static EPGCHANNEL **EPGchannel    = (EPGCHANNEL **)NULL;
static int          EPGchannels   = 0;

cAnalogtvEPGhandler::cAnalogtvEPGhandler(void)
{
}

cAnalogtvEPGhandler::~cAnalogtvEPGhandler(void)
{
}

static void addChannel(const char *Name, const char *ID, int known)
{
  EPGchannel = (EPGCHANNEL **)realloc((void *)EPGchannel, (EPGchannels + 1) * sizeof(EPGCHANNEL *));
  EPGchannel[EPGchannels] = (EPGCHANNEL *)malloc(sizeof(EPGCHANNEL));
  EPGchannel[EPGchannels]->Name = strdup(Name);
  EPGchannel[EPGchannels]->ID = strdup(ID);
  EPGchannel[EPGchannels]->events = 0;
  EPGchannel[EPGchannels]->known = known;
  EPGchannels++;
}

static int external()
{
  register int  cc;
  auto     char cmd[128];

  sprintf(cmd, s.epg_external, s.epg_provider);

  d(1, "Starting external programm \"%s\"", cmd);

  if ((cc = SystemExec(cmd)) < 0) {

    s.epg_autoupdate = 0; // disabled

    d(1, "External programm \"%s\" failed with cc=%d, %d:%s",
      cmd, cc, errno, strerror(errno));

    return 1;
  }

  d(1, "Successfully done.");

  return 0;
}

bool cAnalogtvEPGhandler::Process(void)
{
  register int    i, channelknown = 0, havedata = 0, ln = 0;
  register int    totalevents = 0, takenevents = 0, errors = 0, found = 0;
  register char  *p;
  auto     bool   cc = false;
  auto     char   t[KILOBYTE(10)];
  auto     UI     uEventID;
  auto     long   lDuration;
  auto     int    n, c;
  auto     char   s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ];
  auto     char   s5[BUFSIZ], s6[BUFSIZ], s7[BUFSIZ], s8[BUFSIZ];
  auto     char   cmd[128], fn[256];
  auto     struct tm tm;
  auto     time_t t1, t2, now;
  auto     int    prat, erat, is_wide, is_palplus, is_digital, is_encrypted;
  auto     int    is_live, is_repeat, is_subtitled, theme[7], is_news;


  if (s.epg_source == 2) { // hoerzu2vdr
    if (*s.epg_external)
      if (external())
        return false;
    return true;
  }

  if ((ferr = fopen(EPGERR, "w")) != (FILE *)NULL) {

    // STEP#1: Get Channel-Name -> Channel-ID mapping from VDR

    EPGchannel = (EPGCHANNEL **)NULL;
    EPGchannels = 0;

    if (Channels.MaxNumber() >= 1) {
      int i = 1;

      while (i <= Channels.MaxNumber()) {
        cChannel *channel = Channels.GetByNumber(i, 1);

        if (channel && channel->Frequency()) { // dont insert entries like "webcam" into the epg
          addChannel(channel->Name(), channel->GetChannelID().ToString(), 1);
#if VDRVERSNUM >= 10318
          fprintf(ferr, "channels.conf: %s=%s\n", *channel->GetChannelID().ToString(), channel->Name());
#else          
          fprintf(ferr, "channels.conf: %s=%s\n", channel->GetChannelID().ToString(), channel->Name());
#endif
        }

        i = channel->Number() + 1;
      }
    }


    if (EPGchannels) {
      memset(Stations, 0, sizeof Stations);
      memset(Mode, 0, sizeof Mode);
      memset(Themes, 0, sizeof Themes);

      // STEP#2: Run the external programm

      if (*s.epg_external)
        if (external()) {
          fclose(ferr);
          ferr = (FILE *)NULL;

          return false;
        }

      // STEP#3: Fetch the "Providers network table"

      if (s.epg_source) {
        sprintf(cmd, "nxtvepg -dump ai -prov %s", s.epg_provider);
        fin = popen(cmd, "r");
      }
      else
        fin = fopen("/tmp/ai.txt", "r");

      if (fin != (FILE *)NULL) {
        while (fscanf(fin, "%d %*d %*d %*d %*d %*d %[^\n]", &n, t) == 2)
          if (n < MAXSTATIONS) {
            Stations[n] = strdup(t);
            fprintf(ferr, "ai: %3d: \"%s\"\n", n, Stations[n]);
          }

        if (s.epg_source)
          pclose(fin);
        else
          fclose(fin);
      }

      // STEP#4: Fetch "epg.conf" and overwrite nxtvepg-channel names

      sprintf(fn, "%s/epg.conf", confdir);

      if ((fin = fopen(fn, "r")) != (FILE *)NULL) {
        while (fgets(t, sizeof t, fin)) {
          if (*t != '#') {
            if (sscanf(t, "%[^:]:%[^:]:%d", s1, s2, &c) == 3) {

              fprintf(ferr, "epg.conf: VDR-Name=\"%s\", nxtvepg-Name=\"%s\", Modus=%d\n", s1, s2, c);

              for (n = 0; n < MAXSTATIONS; n++)
                if (Stations[n] && !strcasecmp(s2, Stations[n])) {
                  fprintf(ferr, "epg.conf: Overwrite \"%s\"", Stations[n]);
                  free(Stations[n]);
                  Stations[n] = strdup(s1);
                  Mode[n] = c;
                  fprintf(ferr, " with \"%s\", Modus=%d\n", Stations[n], Mode[n]);
                  break;
                }
            }
          }
        }

        fclose(fin);
      }
      else
        fprintf(ferr, "Cannot open %s\n", fn);

      for (n = 0; n < MAXSTATIONS; n++)
        if (Stations[n])
          fprintf(ferr, "ai->vdr: %3d: \"%s\" Modus=%d\n", n, Stations[n], Mode[n]);

      // STEP#5: Fetch the "PDC themes table"

      if (s.epg_source) {
        sprintf(cmd, "nxtvepg -dump pdc -prov %s", s.epg_provider);
        fin = popen(cmd, "r");
      }
      else
        fin = fopen("/tmp/pdc.txt", "r");

      if (fin != (FILE *)NULL) {
        while (fscanf(fin, "%d %*d %[^\n\t] %[^\n\t] %[^\n]", &n, s1, s2, s3) == 4) {

          fprintf(ferr, "pdc: %3d: s1=\"%s\", s2=\"%s\", s3=\"%s\"\n", n, s1, s2, s3);

          if (n < MAXTHEMES)
            switch (s.epg_language) {
              case 1 : Themes[n] = strdup(s1);  // English
                       break;
              case 2 : Themes[n] = strdup(s2);  // Deutsch
                       break;
              case 3 : Themes[n] = strdup(s3);  // Französisch
                       break;
            }
        }

        if (s.epg_source)
          pclose(fin);
        else
          fclose(fin);
      }


      if ((fout = fopen(EPGOUT, "w")) != (FILE *)NULL) {

        // STEP#6: Fetch the "Programme information table"

        if (s.epg_source) {
          sprintf(cmd, "nxtvepg -dump pi -prov %s", s.epg_provider);
          fin = popen(cmd, "r");
        }
        else
          fin = fopen("/tmp/pi.txt", "r");

        if (fin != (FILE *)NULL) {
          while (fgets(t, sizeof t, fin)) {

            totalevents++;
            ln++;

            if ((p = strstr(t, "\\N"))) {
              memmove(p + 2, p + 1, strlen(p));
              *(p + 1) = ' ';
            }

            *s7 = *s8 = 0;

            n = sscanf(t, "%d %s %s %s %s %s %d %d %s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %[^\n\t] %[^\n]",
                  &c, s1, s2, s3, s4, s5, &prat, &erat, s6,
                  &is_wide, &is_palplus, &is_digital, &is_encrypted, &is_live, &is_repeat, &is_subtitled,
                  &theme[0], &theme[1], &theme[2], &theme[3], &theme[4], &theme[5], &theme[6], s7, s8);

            if (n > 23) {

              fprintf(ferr, "pi: n=%d, c=%d, s1=\"%s\", s2=\"%s\", s3=\"%s\", s4=\"%s\", s5=\"%s\", s6=\"%s\", s7=\"%s\", s8=\"%s\", prat=%d, erat=%d, is_wide=%d, is_palplus=%d, is_digital=%d, is_encrypted=%d, is_live=%d, is_repeat=%d is_subtitled=%d, themes=",
                n, c, s1, s2, s3, s4, s5, s6, s7, s8,
                prat, erat, is_wide, is_palplus, is_digital, is_encrypted, is_live, is_repeat, is_subtitled);

              for (i = 0; i < 7; i++)
                if (theme[i])
                  fprintf(ferr, "%d ", theme[i]);
              fprintf(ferr, "\n");

              s1[4] = s1[7] = 0;
              tm.tm_year = atoi(s1) - 1900;
              tm.tm_mon = atoi(s1 + 5) - 1;
              tm.tm_mday = atoi(s1 + 8);

              s2[2] = s2[5] = 0;
              tm.tm_hour = atoi(s2);
              tm.tm_min = atoi(s2 + 3);
              tm.tm_sec = atoi(s2 + 6);
              tm.tm_isdst = -1;

              t1 = mktime(&tm);

              now = time((time_t)NULL) + (Mode[c] * 60 * 60 * 12);

              if (t1 >= now) {
                channelknown = found = 0;

                for (i = 0; i < EPGchannels; i++) {
                  if (!strcasecmp(EPGchannel[i]->Name, Stations[c])) {
                    found++;
                    EPGchannel[i]->events++;

                    if (EPGchannel[i]->known) {
                      fprintf(fout, "C %s %s\n", EPGchannel[i]->ID, Stations[c]);
                      channelknown = havedata = 1;
                      takenevents++;
                    }
                    break;
                  }
                }

                if (!channelknown)
                  errors++;

                if (!found)
                  addChannel(Stations[c], "", 0);

                if (channelknown) {
                  s3[2] = s3[5] = 0;
                  tm.tm_hour = atoi(s3);
                  tm.tm_min = atoi(s3 + 3);
                  tm.tm_sec = atoi(s3 + 6);
                  tm.tm_isdst = -1;

                  t2 = mktime(&tm);

                  lDuration = t2 - t1;

                  if (lDuration < 0) // good morning, next day ;-)
                    lDuration += 60 * 60 * 24;

                  uEventID = t1 / 60 % 0xffff; // this gives us unique ids for every minute of over 6 weeks

                  fprintf(fout, "E %u %ld %ld 0\n", uEventID, t1, lDuration);
                  fprintf(fout, "T %s\n", s7);

                  if (s.epg_language) {
                    fprintf(fout, "S ");

                    is_news = 0;

                    for (i = 0; i < 7; i++)
                      if (theme[i] && (theme[i] < MAXTHEMES) && Themes[theme[i]]) {
                        if ((theme[i] == 32) || (theme[i] == 33)) {
                          is_news++;

                          if (is_news < 2)
                            fprintf(fout, "%s ", Themes[theme[i]]);
                        }
                        else
                          fprintf(fout, "%s ", Themes[theme[i]]);
                      }

                    fprintf(fout, "[%c%s", toupper(*s6), s6 + 1); // mono, 2-chan, stereo, surround
                    if (is_wide)      fprintf(fout, ", Wide");
                    if (is_palplus)   fprintf(fout, ", PAL+");
                    if (is_digital)   fprintf(fout, ", Digital");
                    if (is_encrypted) fprintf(fout, ", Encrypted");
                    if (is_live)      fprintf(fout, ", Live");
                    if (is_repeat)    fprintf(fout, ", Repeat");
                    if (is_subtitled) fprintf(fout, ", Subtitles");
                    if (erat > 0)     fprintf(fout, ", Rating %d (of 1..7)", erat);
                    if (prat > 2)     fprintf(fout, ", age: %d and up", prat);
                    fprintf(fout, "]\n");
                  }

                  if (*s8) {
                    while ((p = strstr(s8, " // "))) {
                      memmove(p + 1, p + 4, strlen(p));
                      *p = '|';
                    }

                    fprintf(fout, "D %s\n", s8);
                  }

                  fprintf(fout, "c\n");
                }
              }
            }
          }

          if (s.epg_source)
            pclose(fin);
          else
            fclose(fin);
        }

        fclose(fout);
      }

      fprintf(ferr, "\n%d error(s) detected.\n", errors);
      fprintf(ferr, "%d event(s) found, %d event(s) taken.\n", totalevents, takenevents);
      fprintf(ferr, "\nKnown channels are:\n");

      for (i = 0; i < EPGchannels; i++)
        if (EPGchannel[i]->known)
          fprintf(ferr, "ID=%s\tName=%s - %d event(s)\n", EPGchannel[i]->ID, EPGchannel[i]->Name, EPGchannel[i]->events);

      fprintf(ferr, "\nUnknown channels are:\n");

      for (i = 0; i < EPGchannels; i++)
        if (!EPGchannel[i]->known)
          fprintf(ferr, "Name=%s - %d event(s)\n", EPGchannel[i]->Name, EPGchannel[i]->events);

      for (i = 0; i < MAXSTATIONS; i++)
        if (Stations[i])
          free(Stations[i]);

      for (i = 0; i < MAXTHEMES; i++)
        if (Themes[i])
          free(Themes[i]);

      // STEP#7: Hand it over to VDR

      if (havedata) {
        if ((fin = fopen(EPGOUT, "r")) != (FILE *)NULL) {
          if (cSchedules::Read(fin)) {
#if VDRVERSNUM < 10300
            cSIProcessor::TriggerDump();
#else
            cSchedules::Cleanup(true);
#endif
            cc = true;
          }
          else
            fprintf(ferr, "cSchedules::Read() failed, please check /var/log/messages for more details\n");

          fclose(fin);
          fin = (FILE *)NULL;
        }
      }
      else {
        fprintf(ferr, "No useable data found.\n");
        s.epg_autoupdate = 0; // disabled
      }

      for (i = 0; i < EPGchannels; i++) {
        free(EPGchannel[i]->Name);
        free(EPGchannel[i]->ID);
        free(EPGchannel[i]);
      } /* for */

      free(EPGchannel);
    }

    fclose(ferr);
    ferr = (FILE *)NULL;

  }

  epg_update = time((time_t)NULL);

  d(1, "EPG loaded, %d event(s) added/updated", takenevents);

  return cc;
}
