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

#include <stdio.h>
#include <string>
#include <iostream>
#include <vdr/plugin.h>
#include <vdr/i18n.h>
#include <vdr/font.h>
#include <vdr/osd.h>
#include <vdr/epg.h>
#include <vdr/channels.h>
#include <vdr/sources.h>
#include <curl/curl.h>
#include <zlib.h>
#include <getopt.h>
#include "i18n.h"
#include "xml2cEvent.h"
#include "versioncheck.h"
#include "dbgtools.h"

//using namespace std;

static const char *VERSION        = "0.0.1";
static const char *DESCRIPTION    = "Get EPG-Infos from the web";
static const char *MAINMENUENTRY  = "Download EPG from the web";

const char *ConfDir, *xmldir, *importdir, *webepglog;
int HideMenuEntry;
int Mode, Days;
tm *CurrDate;
char **NeededFiles;
int NumberOfNeededFiles;
tColor clrBack, clrFront, clrShadow, clrError, clrHint, clrGood;

int ml = 300;
char mess[300];

CURL *curl;
CURL *curlp;
FILE *Currfile;

static size_t writefunc(void *buffer, size_t size, size_t nmemb, void *data)
{
  int bytesize = size * nmemb;
  fwrite(buffer, size, nmemb, Currfile);
  return bytesize;
}

const char *GetFileNameFromURL(const char *URL){
  string buffer = URL;
  buffer.erase(0, buffer.rfind("/")+1);
  return buffer.c_str();
}

const char *GetchannameFromURL(const char *URL){
  string buffer = URL;
  buffer.erase(0, buffer.rfind("/")+1);
  buffer.erase(buffer.find(".xml.gz"), buffer.length());
  return buffer.c_str();
}

time_t GetDateFromURL(const char *URL){
  const char *FN = GetFileNameFromURL(URL);
  tm *tms = new tm;
  sscanf(FN, "%4d%2d%2d", &tms->tm_year, &tms->tm_mon, &tms->tm_mday);
  tms->tm_year -= 1900;
  tms->tm_mon -= 1;
  tms->tm_mday += 1;
  tms->tm_hour = 3;
  tms->tm_min = 0;
  tms->tm_sec = 0;
  time_t t = mktime(tms);
  delete tms;
  return t;
}

static int FilterEpgTar(const struct dirent *file){
  if(strstr(file->d_name, ".epg.tar") != NULL)
    return 1;
  else
    return 0;
}

static int CheckIfNeeded(const struct dirent *file){
  int found = 0, j = 0;
  while((found == 0)&&(NumberOfNeededFiles > j))
    if(NeededFiles[j] != NULL)
      if (strcmp(NeededFiles[j], file->d_name) == 0)
        found = 1;
      else
        j++;
  if(found == 1)
    return 0;
  return 1;
}

//cMenuSetupWebepg:------------------------------------------------------

class cMenuSetupWebepg : public cMenuSetupPage{
private:
  int nHideMenuEntry;
  int nMode;
  const char *Modes[2];
  int nDays;
protected:
  virtual void Store(void);
public:
  cMenuSetupWebepg(void);
};

cMenuSetupWebepg::cMenuSetupWebepg(void){
  nHideMenuEntry = HideMenuEntry;
  nMode = Mode;
  nDays = Days;
  Modes[0] = tr("online");
  Modes[1] = tr("offline");
  Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"), &nHideMenuEntry));
  Add(new cMenuEditStraItem(tr("Mode"), &nMode, 2, Modes));
  Add(new cMenuEditIntItem(tr("Max. number of days to load"), &nDays, 0, 50));
}

void cMenuSetupWebepg::Store(void){
  HideMenuEntry = nHideMenuEntry;
  Mode = nMode;
  Days = nDays;
  SetupStore("HideMenuEntry", HideMenuEntry);
  SetupStore("Mode", Mode);
  SetupStore("Days", Days);
}


//cWebepg:---------------------------------------------------------------

class cWebepg : public cThread, public cOsdObject{
private:
  cOsd *osd;
  void osdmess(const char *mess, int row, int col, int offx = 0, int offy = 0, tColor Color = clrFront);
  void DrawOsdList(void);
  int DownloadFile(const char *URL, const char *target, bool Use_POST);
  void LoadChannelsconf(void);
  void Loadepgphp(void);
  void LoadIdxxml(int i);
  void SaveIdxxml(int i);
  void ImportFiles(void);
  void AddCurrIdxToNeededFiles(void);
  void AddCharToNeededFiles(string c);
  void CleanDirectory(void);
  cxmlIdx *ParsexmlIdx(char *file, int d);
  //cEvents *sheds;
  vector<cEvents> sheds;
  cChan *chans;
  int NumberOfsheds, NumberOfErrors, NumberOfNew, NumberOfchans, NumberOfskipped, NumberOfxmls, NumberOfPages;
  cxmlIdx *xmlIdx;
  char texts[30][8][2][100];
  tColor OSDColors[30][8][2];
  int CurrPage;
  int Aborting, Ready;
protected:
  virtual void Action();
public:
  cWebepg(const char *Description = NULL);
  virtual ~cWebepg(void);
  bool Strat(void);
  virtual void Show(void);
  eOSState ProcessKey(eKeys Key);
};

cWebepg::cWebepg(const char *Description){
  osd = NULL;
  SetDescription(Description);
  //sheds = (cEvents *)calloc(1, sizeof(cEvents));
  chans = (cChan *)calloc(1, sizeof(cChan));
  NumberOfsheds = 0;
  NumberOfErrors = 0;
  NumberOfNew = 0;
  NumberOfchans = 0;
  NumberOfskipped = 0;
  NumberOfxmls = 0;
  NumberOfPages = 0;
  NumberOfNeededFiles = 6;
  if(NeededFiles == NULL)
    NeededFiles = (char **)malloc(6*sizeof(char*));
  else
    NeededFiles = (char **)realloc(NeededFiles, 6*sizeof(char*));
  NeededFiles[0] = ".";
  NeededFiles[1] = "..";
  NeededFiles[2] = "channels.conf";
  NeededFiles[3] = "epg.php";
  NeededFiles[4] = "version.php";
  NeededFiles[5] = "webepg.log";
  Aborting = 0;
  Ready = 0;
  CurrPage = 0;
  int i, j, k;
  for(i = 0; i < 30; i++)
    for(j = 0; j < 9; j++)
      for(k = 0; k < 2; k++)
        snprintf(texts[i][j][k], 100, "%s", "");
}

cWebepg::~cWebepg(){
  //cout << "Killing cWebepg...";
  if(osd != NULL)
    delete osd;
  //cout << "osd killed...";
  //cout << NumberOfsheds;
  //if(sheds != NULL)
    //delete sheds;
  //cout << "sheds killed...";
  if(chans != NULL)
    delete chans;
  //cout << "chans killed...";
  //cout << "done\n";
}

void cWebepg::Show(void){
  dbgmess(4, "Show started");
  osd = cOsdProvider::NewOsd(50,50);
  if (osd){
    dbgmess(2, "Trying nice osd-mode...");
    tArea Areas[] = {{0, 0, 599, 449, 4}};
    if(osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk){
      dbgmess(2, "Using nice osd-mode...");
      clrBack = clrGray50;
      clrFront = clrWhite;
      clrShadow = clrBlack;
      clrError = clrRed;
      clrHint = clrYellow;
      clrGood = clrGreen;
      osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
    }
    else{
      dbgmess(2, "Trying bad osd-mode...");
      tArea Areas[] = {{0, 0, 599, 449, 2}};
      if(osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk){
        dbgmess(2, "Using bad osd-mode...");
        clrBack = clrTransparent;
        clrFront = clrWhite;
        clrShadow = clrTransparent;
        clrError = clrRed;
        clrHint = clrWhite;
        clrGood = clrGreen;
        osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
      }
      else{
        NumberOfErrors++;
        dbgmess(1, "Error: OSD can't handle needed Area! Aborting");
        delete osd;
        osd = NULL;
        return;
      }
    }
    osd->DrawRectangle(0, 0, 599, 449, clrBack);
    osd->DrawRectangle(0, 0, 599, 0, clrFront);
    osd->DrawRectangle(0, 0 ,0, 448, clrFront);
    osd->DrawRectangle(0, 449, 599, 449, clrFront);
    osd->DrawRectangle(599, 0, 599, 449, clrFront);
    osd->DrawRectangle(0, 50, 599, 50, clrFront);
    char title[20];
    snprintf(title, 20, "WebEPG %s", VERSION);
    osd->DrawText(8, 8, title, clrShadow, clrTransparent, cFont::GetFont(fontOsd), 400, 50, taDefault);
    osd->DrawText(6, 6, title, clrFront, clrTransparent, cFont::GetFont(fontOsd), 400, 50, taDefault);
    snprintf(title, 20, "%i.%i.%i", CurrDate->tm_mday, CurrDate->tm_mon + 1, CurrDate->tm_year + 1900);
    osd->DrawText(405, 8, title, clrShadow, clrTransparent, cFont::GetFont(fontOsd), 400, 50, taDefault);
    osd->DrawText(403, 6, title, clrFront, clrTransparent, cFont::GetFont(fontOsd), 400, 50, taDefault);
    osd->Flush();
    Start();
  }
  dbgmess(4, "Show stopped");
  //Start();
}

void cWebepg::ImportFiles(void){
  struct dirent **dirlist, **list;
  int i;
  int NumberOfTars = scandir(importdir, &dirlist, FilterEpgTar, alphasort);
  snprintf(mess, ml, "Found %i files to import", NumberOfTars);
  dbgmess(2, mess);
  for(i = 0, list = dirlist; i < NumberOfTars; i++, *list++){
    snprintf(mess, ml, "Importing %s...", (*list)->d_name);
    dbgmess(2, mess);
    char *tarverbose = "v";
    if(DebugLevel < 3)
      tarverbose = "";
    snprintf(mess, ml, "cd %s; tar -x%sf %s/%s", xmldir, tarverbose, importdir, (*list)->d_name);
    system(mess);
  }
  free(dirlist);
}

void cWebepg::AddCurrIdxToNeededFiles(void){
  for(int i = 0; i < NumberOfxmls; i++){
    const char *File = GetFileNameFromURL(xmlIdx[i].URL);
    snprintf(mess, ml, "File: %s", File);
    dbgmess(4, mess);
    AddCharToNeededFiles(File);
  }
}

void cWebepg::AddCharToNeededFiles(string c){
  if(c != ""){
    snprintf(mess, ml, "Added %s to needed files", c.c_str());
    dbgmess(4, mess);
    NumberOfNeededFiles++;
    snprintf(mess, ml, "Added %s to needed files", c.c_str());
    dbgmess(4, mess);
    NeededFiles = (char **)realloc(NeededFiles, NumberOfNeededFiles*sizeof(char*));
    snprintf(mess, ml, "Added %s to needed files", c.c_str());
    dbgmess(4, mess);
    NeededFiles[NumberOfNeededFiles-1] = (char *)malloc(sizeof(char)*(c.length()+1));
    snprintf(mess, ml, "Added %s to needed files", c.c_str());
    dbgmess(4, mess);
    strncpy(NeededFiles[NumberOfNeededFiles-1], c.c_str(), c.length()+1);
    snprintf(mess, ml, "Added %s to needed files", c.c_str());
    dbgmess(4, mess);
  }
}

void cWebepg::CleanDirectory(void){
  dbgmess(4, "CleanDirectory started");
  struct dirent **dirlist, **list;
  int i;
  int NumberOfFiles = scandir(xmldir, &dirlist, CheckIfNeeded, alphasort);
  dbgmess(4, "NeededFiles:");
  for(i = 0; i < NumberOfNeededFiles; i++){
    snprintf(mess, ml, "#%i: %s", i, NeededFiles[i]);
    dbgmess(4, mess);
  }
  snprintf(mess, ml, "Found %i files to delete", NumberOfFiles);
  dbgmess(2, mess);
  for(i = 0, list = dirlist; i < NumberOfFiles; i++, *list++){
    snprintf(mess, ml, "Deleting %s...", (*list)->d_name);
    dbgmess(3, mess);
    snprintf(mess, ml, "%s/%s", xmldir, (*list)->d_name);
    remove(mess);
  }
  free(dirlist);
  dbgmess(4, "CleanDirectory stopped");
}

void cWebepg::osdmess(const char *mess, int row, int col, int offx, int offy, tColor Color){
  dbgmess(4, "osdmess started");
  int PageToUse = 0, r = row;
  while(r >= 8){
    PageToUse++;
    r -= 8;
  }
  strncpy(texts[PageToUse][r][col], mess, 100);
  OSDColors[PageToUse][r][col] = Color;
  CurrPage = PageToUse;
  if(PageToUse >= NumberOfPages)
    NumberOfPages++;
  if(Aborting == 0)
    DrawOsdList();
  dbgmess(4, "osdmess stopped");
}

void cWebepg::DrawOsdList(void){
  dbgmess(4, "DrawOsdList started");
  if(osd){
    int row, col;
    osd->DrawRectangle(1, 51, 598, 448, clrTransparent);
    osd->DrawRectangle(1, 51, 598, 448, clrBack);
    snprintf(mess, ml, tr("Page %i/%i"), CurrPage + 1, NumberOfPages);
    dbgmess(4, mess);
    osd->DrawText(489, 428, mess, clrShadow, clrTransparent, cFont::GetFont(fontSml), 100, 30, taDefault);
    osd->DrawText(487, 426, mess, clrFront, clrTransparent, cFont::GetFont(fontSml), 100, 30, taDefault);
    if(CurrPage < 29)
      for(row = 0; row < 8; row++)
        for(col = 0; col < 2; col++){
          snprintf(mess, ml, "Row: %i; Col: %i", row, col);
          dbgmess(4, mess);
          if(texts[CurrPage][row][col] != NULL){
            osd->DrawText(col*400 + 5, (row+1)*50 + 4, texts[CurrPage][row][col], clrShadow, clrTransparent, cFont::GetFont(fontOsd), 400, 50, taDefault);
            osd->DrawText(col*400 + 3, (row+1)*50 + 2, texts[CurrPage][row][col], OSDColors[CurrPage][row][col], clrTransparent, cFont::GetFont(fontOsd), 400, 50, taDefault);
          }
        }
    dbgmess(4, "For() ready!");
    osd->Flush();
  }
  dbgmess(4, "DrawOsdList stopped");
}

int cWebepg::DownloadFile(const char *URL, const char *target, bool Use_POST){
  Currfile = fopen(target,"w+");
  if (Currfile != NULL){
    int success;
    struct curl_httppost *post = NULL;
    char useragent[20];
    snprintf(useragent, 20, "WebEPG-%s", VERSION);
    if (Use_POST==true){
      struct curl_httppost *last = NULL;
      curl_formadd(&post, &last, CURLFORM_COPYNAME, "user", CURLFORM_COPYCONTENTS, "webepg", CURLFORM_END);
      curl_formadd(&post, &last, CURLFORM_COPYNAME, "pass", CURLFORM_COPYCONTENTS, "download", CURLFORM_END);
      curl_easy_setopt(curlp, CURLOPT_HTTPPOST, post);
      curl_easy_setopt(curlp, CURLOPT_USERAGENT, useragent);
      curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, writefunc);
      curl_easy_setopt(curlp, CURLOPT_URL, URL);
      success = curl_easy_perform(curlp);
    }
    else{
      curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent);
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
      curl_easy_setopt(curl, CURLOPT_URL, URL);
      success = curl_easy_perform(curl);
    }
    curl_formfree(post);
    fclose(Currfile);
    return success;
  }
  else
    return CURLE_WRITE_ERROR;
}

void cWebepg::LoadChannelsconf(void){
  char chanconf[strlen(ConfDir)+15];
  snprintf(chanconf, sizeof(chanconf), "%s/%s", ConfDir, "channels.conf");
  snprintf(mess, ml, "Reading %s", chanconf);
  dbgmess(2, mess);
  FILE *chanfile = fopen(chanconf, "r");
  if(chanfile == NULL){
    NumberOfErrors++;
    snprintf(mess, ml, "Error: %s not found", chanconf);
    dbgmess(1, mess);
    osdmess(tr("channels.conf not found"), 1, 0, 0, 0, clrError);
  }
  else{
    int bl = 200;
    char buffer[bl];
    int line = 0;
    while(fgets(buffer, 200, chanfile)){
      line++;
      const char *VDRname = NULL;
      int Freq = 0;
      char *Pol = NULL;
      char *Src = NULL;
      int sid = 0;
      char *Name = NULL;
      int CDays = 0;
      int noti = 0;
      char *notc = NULL;
      int F = sscanf(buffer, "%a[^:]:%d:%a[^:]:%a[^:]:%d:%a[^:]:%a[^:]:%d:%a[^:]:%d:%d:%d:%d:%a[^:]:%d\n", &VDRname, &Freq, &Pol, &Src, &noti, &notc, &notc, &noti, &notc, &sid, &noti, &noti, &noti, &Name, &CDays);
      if(F == 14){
        sscanf(Name, "%s\n", Name);
        CDays = Days;
        F = 15;
      }
      if(F != 15){
        NumberOfErrors++;
        snprintf(mess, ml, "Error: Line %i of channels.conf is corrupted", line);
        dbgmess(1, mess);
      }
      else{
        NumberOfchans++;
        chans = (cChan *)realloc(chans, NumberOfchans*sizeof(cChan));
        //skipped:
        if(VDRname[0] == '#'){
          chans[NumberOfchans-1].skipped = 1;
          NumberOfskipped++;
          string buffer = VDRname;
          buffer.erase(0, 1);
          VDRname = buffer.c_str();
        }
        else
          chans[NumberOfchans-1].skipped = 0;
        //VDRname:
        chans[NumberOfchans-1].VDRname = (char *)malloc(sizeof(char)*(strlen(VDRname)+1));
        strncpy(chans[NumberOfchans-1].VDRname, VDRname, strlen(VDRname)+1);
        //Transponder:
        chans[NumberOfchans-1].Transponder = cChannel::Transponder(Freq, Pol[0]);
        //Source:
        chans[NumberOfchans-1].Source = cSource::FromString(Src);
        //SID:
        chans[NumberOfchans-1].ServiceID = sid;
        //Name:
        chans[NumberOfchans-1].name = Name;
        //Days:
        chans[NumberOfchans-1].Days = CDays;
        //Output:
        snprintf(mess, ml, "Found %s at %i, Transponder %i ,SID %i for %s", chans[NumberOfchans-1].VDRname, chans[NumberOfchans-1].Source, chans[NumberOfchans-1].Transponder, chans[NumberOfchans-1].ServiceID, chans[NumberOfchans-1].name);
        dbgmess(3, mess);
      }
    }
    fclose(chanfile);
    if(NumberOfchans <= 0){
      NumberOfErrors++;
      dbgmess(1, "Error: No channels found in channels.conf");
      osdmess(tr("No channels found"), 1, 0, 0, 0, clrError);
    }
    else if(NumberOfskipped == NumberOfchans){
      NumberOfErrors++;
      dbgmess(1, "Error: All channels are skipped");
      osdmess(tr("All channels are skipped"), 1, 0, 0, 0, clrError);
    }
    else{
      snprintf(mess, ml, "Found %i channels (%i skipped)", NumberOfchans, NumberOfskipped);
      dbgmess(2, mess);
    }
  }
}

void cWebepg::Loadepgphp(void){
  osdmess(tr("Index"), 1, 0);
  char epgphp[strlen(xmldir)+9];
  snprintf(epgphp, sizeof(epgphp), "%s/%s", xmldir, "epg.php");
  int DLResult = CURLE_OK;
  if(Mode == 0){
    dbgmess(2, "Downloading Index...");
    osdmess(tr("Downloading..."), 1, 1, 0, 0, clrGood);
    DLResult = DownloadFile("http://www.wontorra.net/webepg/epg.php", epgphp, true);
    if(DLResult != CURLE_OK){
      NumberOfErrors++;
      snprintf(mess, ml, "Error: Downloading Index failed. CURL-Errorcode: %i", DLResult);
      dbgmess(1, mess);
      osdmess(tr("Error"), 1, 1, 0, 0, clrError);
    }
    else
      dbgmess(2, "Download of Indexfile finished");
  }
  FILE *Indexfile = fopen(epgphp, "r");
  if((Indexfile == NULL)||(DLResult != CURLE_OK)){
    NumberOfErrors++;
    snprintf(mess, ml, "Error: Can't open %s", epgphp);
    dbgmess(1, mess);
    osdmess(tr("Error"), 1, 1, 0, 0, clrError);
  }
  else{
    dbgmess(2, "Reading epg.php...");
    osdmess(tr("Processing..."), 1, 1, 0, 0, clrGood);
    char buffer[10000] = "";
    fgets(buffer, 10000, Indexfile);
    string bufstr = buffer;
    while(bufstr != ""){
      string tmp = bufstr;
      if((tmp.find("<br>") > 0)&&(tmp.length() > 1)){
        NumberOfsheds++;
        sheds.push_back(cEvents());
        sheds[NumberOfsheds-1].SetDebugLevel(DebugLevel);
        tmp.erase(tmp.find("<br>"), tmp.find("\n"));
        char CurrURL[tmp.length()];
        strncpy(CurrURL, tmp.c_str(), tmp.length()+1);
        snprintf(mess, ml, "Found URL: %s", CurrURL);
        dbgmess(3, mess);
        char CurrName[tmp.length()];
        strncpy(CurrName, GetchannameFromURL(tmp.c_str()), strlen(GetchannameFromURL(tmp.c_str()))+1);
        int j = 0;
        int found = 0;
        while((found == 0)&&(NumberOfchans > j))
          if (strcmp(CurrName, chans[j].name) == 0)
            found = 1;
          else
            j++;
        if(j < NumberOfchans){
          chans[j].URL = (char *)malloc(sizeof(char)*(strlen(CurrURL)+1));
          strncpy(chans[j].URL, CurrURL, strlen(CurrURL)+1);
          sheds[NumberOfsheds-1].SetChannel(chans[j]);
          snprintf(mess, ml, "for %s", sheds[NumberOfsheds-1].GetChannel().VDRname);
          dbgmess(3, mess);
          osdmess(sheds[NumberOfsheds-1].GetChannel().VDRname, NumberOfsheds+1, 0);
          if(chans[j].skipped == 1)
            sheds[NumberOfsheds-1].shedState = ssSkipped;
        }
        else{
          sheds[NumberOfsheds-1].shedState = ssNew;
          cChan *NewChan = new cChan;
          NewChan->name = (char *)malloc(sizeof(char)*(strlen(CurrName)+1));
          strncpy(NewChan->name, CurrName, strlen(CurrName)+1);
          NewChan->VDRname = "";
          NewChan->URL = "";
          sheds[NumberOfsheds-1].SetChannel(NewChan[0]);
          snprintf(mess, ml, "Hint: %s not found in channels.conf. It's possible that it is a new channel.", sheds[NumberOfsheds-1].GetChannel().name);
          dbgmess(1, mess);
          osdmess(sheds[NumberOfsheds-1].GetChannel().name, NumberOfsheds+1, 0);
        }
        cout << "After if(j...\n";
        bufstr.erase(0, bufstr.find("<br>") + 4);
      }
      else
        bufstr = "";
    }
  }
  fclose(Indexfile);
  if(NumberOfsheds == 0){
    NumberOfErrors++;
    dbgmess(1, "Error: No URLs found in Index");
    osdmess(tr("No URLs found in Index!"), 2, 0, 0, 0, clrError);
  }
  else{
    snprintf(mess, ml, "Found %i Urls", NumberOfsheds);
    dbgmess(2, mess);
    osdmess(tr("Ready"), 1, 1, 0, 0, clrGood);
  }
}

void cWebepg::LoadIdxxml(int i){
  snprintf(mess, ml, "%i: shedState: %i", i, sheds[i].shedState);
  dbgmess(4, mess);
  if(sheds[i].shedState == 2)
    osdmess(tr("skipped"), i+2, 1, 0, 0, clrHint);
  else if(sheds[i].shedState == 1){
    osdmess(tr("new"), i+2, 1, 0, 0, clrHint);
    NumberOfNew++;
  }
  else{
    osdmess(tr("Processing..."), i+2, 1, 0, 0, clrGood);
    char Idxfile[200] = "";
    snprintf(Idxfile, 200, "%s/%s.xml.gz", xmldir, sheds[i].GetChannel().name);
    snprintf(mess, ml, "Reading %s...", Idxfile);
    dbgmess(3, mess);
    char *fn = (char *)malloc(sizeof(char)*(strlen(sheds[i].GetChannel().name)+8));
    snprintf(fn, strlen(sheds[i].GetChannel().name)+8, "%s.xml.gz", sheds[i].GetChannel().name);
    AddCharToNeededFiles(fn);
    cxmlIdx *oldxmlIdx = ParsexmlIdx(Idxfile, sheds[i].GetChannel().Days);
    xmlIdx = oldxmlIdx;
    if(Mode == 0){
      int NumberOfOldxmls = NumberOfxmls;
      snprintf(mess, ml, "Downloading %s...", sheds[i].GetChannel().URL);
      dbgmess(3, mess);
      int DLResult = DownloadFile(sheds[i].GetChannel().URL, Idxfile, false);
      if(DLResult != CURLE_OK){
        NumberOfErrors++;
        snprintf(mess, ml, "Error: Download of %s failed", sheds[i].GetChannel().URL);
        dbgmess(1, mess);
        osdmess(tr("Error"), i+2, 1, 0, 0, clrError);
      }
      else{
        osdmess(tr("Downloading..."), i+2, 1, 0, 0, clrGood);
        cxmlIdx *newxmlIdx = ParsexmlIdx(Idxfile, sheds[i].GetChannel().Days);
        xmlIdx = newxmlIdx;
        int j;
        for(j = 0; j < NumberOfxmls; j++){
          int Update = 0;
          int URLfound = 0;
          int k = 0;
          while((Update == 0)&&(URLfound == 0)&&(k < NumberOfOldxmls)){
            if(strcmp(newxmlIdx[j].URL, oldxmlIdx[k].URL) == 0){
              URLfound = 1;
              if(strcmp(newxmlIdx[j].md5, oldxmlIdx[k].md5) != 0)
                Update = 1;
              }
            else
              k++;
          }
          if(k >= NumberOfOldxmls)
            Update = 1;
          if(Update == 1){
            snprintf(mess, ml, "New xml-File found at %s, downloading and updating...", newxmlIdx[j].URL);
            dbgmess(3, mess);
            string tmpstr = newxmlIdx[j].URL;
            tmpstr.erase(0, tmpstr.rfind("/"));
            tmpstr = xmldir + tmpstr;
            DLResult = DownloadFile(newxmlIdx[j].URL, tmpstr.c_str(), false);
            if(DLResult != CURLE_OK){
              NumberOfErrors++;
              snprintf(mess, ml, "Error: Download of %s failed", newxmlIdx[k].URL);
              dbgmess(1, mess);
              osdmess(tr("Error"), i+2, 1, 0, 0, clrError);
            }
          }
        }
        snprintf(mess, ml, "All files loaded for %s", sheds[i].GetChannel().VDRname);
        dbgmess(3, mess);
      }
    }
  }
}

void cWebepg::SaveIdxxml(int i){
  if(NumberOfxmls > 0){
    char buffer[200] = "";
    snprintf(buffer, 200, "%s/%s.xml.gz", xmldir, sheds[i].GetChannel().name);
    gzFile xmlFile = gzopen(buffer, "w+");
    int j;
    if(xmlFile != NULL)
      for(j = 0; j < NumberOfxmls; j++){
        snprintf(buffer, 200, "%s:%s\n", xmlIdx[j].URL, xmlIdx[j].md5);
        gzputs(xmlFile, buffer);
      }
    gzclose(xmlFile);
  }
}

cxmlIdx *cWebepg::ParsexmlIdx(char *file, int d){
  dbgmess(4, "ParsexmlIdx started.");
  NumberOfxmls = 0;
  gzFile xmlFile = gzopen(file, "r");
  int bl = 200;
  char buffer[bl];
  cxmlIdx *Idx = NULL;
  if(xmlFile == NULL){
    snprintf(mess, ml, "Can't read %s, it seems to be new...", file);
    dbgmess(3, mess);
    if(Mode == 1)
      NumberOfErrors++;
  }
  else{
    Idx = (cxmlIdx *)malloc(sizeof(cxmlIdx));
    int line = 0;
    while(gzgets(xmlFile, buffer, bl) != NULL){
      line++;
      char *URLprefix = NULL;
      char *URL = NULL;
      char *md5 = NULL;
      int F = sscanf(buffer, "%a[^:]:%a[^:]:%a[^\n]\n", &URLprefix, &URL, &md5);
      if(F != 3){
        NumberOfErrors++;
        snprintf(mess, ml, "Error: Line %i of %s is corrupted", line, file);
        dbgmess(1, mess);
      }
      else{
        time_t xmlDate = GetDateFromURL(URL);
        time_t ct = mktime(CurrDate);
        if(xmlDate < ct){
          snprintf(mess, ml, "Ignoring %s because it's too old", GetFileNameFromURL(URL));
          dbgmess(3, mess);
        }
        else if((xmlDate > (ct + (86400*d)))&&(d != 0)){
          snprintf(mess, ml, "Ignoring %s because it's out of the Max Time %i", GetFileNameFromURL(URL), d);
          dbgmess(3, mess);
        }
        else{
          NumberOfxmls++;
          char *HoleURL = (char *)malloc(sizeof(char)*(strlen(URLprefix)+strlen(URL)+2));
          snprintf(HoleURL, sizeof(char)*(strlen(URLprefix)+strlen(URL)+2), "%s:%s", URLprefix, URL);
          Idx = (cxmlIdx *)realloc(Idx, sizeof(cxmlIdx)*NumberOfxmls);
          Idx[NumberOfxmls-1].URL = (char *)malloc(sizeof(char)*strlen(HoleURL)+1);
          strncpy(Idx[NumberOfxmls-1].URL, HoleURL, strlen(HoleURL)+1);
          Idx[NumberOfxmls-1].md5 = (char *)malloc(sizeof(char)*strlen(md5)+1);
          strncpy(Idx[NumberOfxmls-1].md5, md5, strlen(md5)+1);
        }
      }
    }
    gzclose(xmlFile);
  }
  return Idx;
  dbgmess(4, "ParsexmlIdx stopped.");
}

void cWebepg::Action(){
  dbgmess(4, "Action started");
  int i = 0;
  dbgmess(1, "Starting WebEPG...");
  if(ConfDir == NULL)
    ConfDir = cPlugin::ConfigDirectory("webepg");
  if(xmldir == NULL)
    xmldir = cPlugin::ConfigDirectory("webepg");
  if(importdir == NULL)
    importdir = cPlugin::ConfigDirectory("webepg");
  snprintf(mess, ml, "Running Version: %s", VERSION);
  dbgmess(2, mess);
  snprintf(mess, ml, "Config-Directory: %s", ConfDir);
  dbgmess(2, mess);
  snprintf(mess, ml, "Data-Directory: %s", xmldir);
  dbgmess(2, mess);
  snprintf(mess, ml, "Import-Directory: %s", importdir);
  dbgmess(2, mess);
  snprintf(mess, ml, "Logfile: %s", webepglog);
  dbgmess(2, mess);
  if((Mode == 0)&&((curl == NULL)||(curlp == NULL))){
    NumberOfErrors++;
    dbgmess(1, "Initialising libcurl failed");
    osdmess(tr("Initialising libcurl failed!"), 0, 0, 0, 0, clrError);
  }
  else{
    if(Mode == 1)
      ImportFiles();
    if(Aborting == 1){Ready = 1; Aborting = 0; osdmess(tr("Aborted"), 0, 0, 0, 0, clrError); return;}
    char ver[100] = "";
    snprintf(ver, 100, "%s/version.php", xmldir);
    if(Mode == 0)
      DownloadFile("http://www.wontorra.net/webepg/version.php", ver, true);
    if(Aborting == 1){Ready = 1; Aborting = 0; osdmess(tr("Aborted"), 0, 0, 0, 0, clrError); return;}
    FILE *verfile = fopen(ver, "r");
    char buffer[50] = "";
    fgets(buffer, 50, verfile);
    if((verfile == NULL)||(strlen(buffer) == 0)){
      NumberOfErrors++;
      snprintf(mess, ml, "Error: Can't read %s", ver);
      dbgmess(1, mess);
    }
    else{
      fclose(verfile);
      char *cCurrVer = NULL, *cMinVer = NULL;
      sscanf(buffer, "%a[^;];%a[^;];", &cCurrVer, &cMinVer);
      snprintf(mess, ml, "Current version: %s", cCurrVer);
      dbgmess(2, mess);
      osdmess(tr("Current Version"), 0, 0);
      cVersion ThisVer = Verfromchar(VERSION);
      cVersion CurrVer = Verfromchar(cCurrVer);
      cVersion MinVer = Verfromchar(cMinVer);
      if(ThisVer < MinVer){
        osdmess(cCurrVer, 0, 1, 0, 0, clrError);
        osdmess(tr("You have to update WebEPG"), 1, 0, 0, 0, clrError);
        dbgmess(1, "Error: Because of important changes you have to update WebEPG before using it.");
      }
      else{
        if(ThisVer < CurrVer){
          osdmess(cCurrVer, 0, 1, 0, 0, clrHint);
          dbgmess(1, "There's a new version of WebEPG available. It's commendable to update.");
        }
        else
          osdmess(cCurrVer, 0, 1, 0, 0, clrGood);
        //Reading Configuation...
        if(Mode == 0)
          dbgmess(2, "Set online-mode");
        else
          dbgmess(2, "Set offline-mode");
        if(Aborting == 1){Ready = 1; Aborting = 0; osdmess(tr("Aborted"), 0, 0, 0, 0, clrError); return;}
        LoadChannelsconf();
        if(Aborting == 1){Ready = 1; Aborting = 0; osdmess(tr("Aborted"), 0, 0, 0, 0, clrError); return;}
        if(NumberOfchans > NumberOfskipped)
          Loadepgphp();
        if(Aborting == 1){Ready = 1; Aborting = 0; osdmess(tr("Aborted"), 0, 0, 0, 0, clrError); return;}
        int j;
        for(i = 0; i < NumberOfsheds; i++){
          snprintf(mess, ml, "In for(NumberOfsheds); i = %i; IdxURL: %s; Channame: %s; IdxName: %s", i, sheds[i].GetChannel().URL, sheds[i].GetChannel().VDRname, sheds[i].GetChannel().name);
          dbgmess(4, mess);
          NumberOfxmls = 0;
          LoadIdxxml(i);
          SaveIdxxml(i);
          AddCurrIdxToNeededFiles();
          if(Aborting == 1){Ready = 1; Aborting = 0; osdmess(tr("Aborted"), 0, 0, 0, 0, clrError); return;}
          if((NumberOfxmls <= 0)&&(sheds[i].shedState == ssOk)){
            NumberOfErrors++;
            osdmess(tr("Error"), i+2, 1, 0, 0, clrError);
          }
          else{
            if(sheds[i].shedState == ssOk)
              osdmess(tr("Processing..."), i+2, 1, 0, 0, clrGood);
            snprintf(mess, ml, "Processing EPG for %s...", sheds[i].GetChannel().VDRname);
            dbgmess(2, mess);
            for(j = 0; j < NumberOfxmls; j++){
              string DayxmlFile = xmldir;
              int Date = 0;
              sscanf(GetFileNameFromURL(xmlIdx[j].URL), "%i", &Date);
              DayxmlFile += "/";
              string GF = GetFileNameFromURL(xmlIdx[j].URL);
              DayxmlFile += GF;
              snprintf(mess, ml, "Processing %s...", DayxmlFile.c_str());
              dbgmess(3, mess);
              sheds[i].FromXMLFile(DayxmlFile.c_str());
              dbgmess(4, "FromXMLFile() ready");
            }
            if(sheds[i].GetNumberOfEvents() > 0){
              snprintf(mess, ml, "%i events found", sheds[i].GetNumberOfEvents());
              dbgmess(2, mess);
              sheds[i].clearEpg();
              sheds[i].feedEpg();
            }
            else
              dbgmess(2, "No Events found to feed!");
            snprintf(mess, ml, "%s finished", sheds[i].GetChannel().VDRname);
            dbgmess(2, mess);
            if(sheds[i].shedState == ssOk)
              osdmess(tr("Ready"), i+2, 1, 0, 0, clrGood);
            if(Aborting == 1){Ready = 1; Aborting = 0; osdmess(tr("Aborted"), 0, 0, 0, 0, clrError); return;}
          }
        }
        CleanDirectory();
      }
    }
  }
  if(NumberOfNew > 0){
    snprintf(mess, ml, tr("Found %i new channels"), NumberOfNew);
    dbgmess(2, mess);
    osdmess(mess, i+3, 0, 0, 0, clrHint);
    i++;
  }
  snprintf(mess, ml, "Ready! %i errors occured!", NumberOfErrors);
  dbgmess(1, mess);
  if(NumberOfErrors > 0){
    snprintf(mess, ml, tr("%i Errors occured,"), NumberOfErrors);
    osdmess(mess, i+3, 0, 0, 0, clrError);
    osdmess(tr("see log for more information"), i+4, 0, 0, 0, clrError);
  }
  Ready = 1;
  dbgmess(4, "Action stopped");
}

eOSState cWebepg::ProcessKey(eKeys Key)
{
  dbgmess(4, "ProcessKey started");
  switch(Key){
    case kLeft:
      if(CurrPage > 0){
        dbgmess(4, "Left");
        CurrPage--;
        DrawOsdList();
      }
      break;
    case kRight:
      if(CurrPage < NumberOfPages-1){
        dbgmess(4, "Right");
        CurrPage++;
        DrawOsdList();
      }
      break;
    case kBack:
      if(Ready == 0){
        dbgmess(1, "Aborting...");
        osdmess(tr("Aborting..."), 0, 0, 0, 0, clrError);
        Aborting = 1;
      }
      else{
        return osEnd;
      }
    default:
      snprintf(mess, ml, "Key: %i", Key);
      dbgmess(4, mess);
      dbgmess(4, "ProcessKey stopped at default");
      return osContinue;
  }
  return osContinue;
}


//cPlugin:---------------------------------------------------------------

class cPluginWebepg : public cPlugin {
private:
  // Add any member variables or functions you may need here.
public:
  cPluginWebepg(void);
  virtual ~cPluginWebepg();
  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 const char *MainMenuEntry(void) { return HideMenuEntry ? NULL : tr(MAINMENUENTRY); }
  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);
  };

cPluginWebepg::cPluginWebepg(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!
}

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

const char *cPluginWebepg::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.
  return "  -d X,     --debuglevel X Set DebugLevel X for webepg. [default: 0]\n"\
         "                             0: No Debug-Messages\n"\
         "                             1: Just Errors will be shown\n"\
         "                             2: Standard-Messages\n"\
         "                             3: Details about every channel will be shown\n"\
         "                             4: Lots of debugmessages for development will be shown\n"\
         "  -c DIR,   --confdir DIR  Use DIR as configuration-directory with the channels.conf (for webepg)\n"\
         "                           [default: vdr-conf-dir/plugins/webepg]\n"\
         "  -x DIR,   --xmldir DIR   Download/Import all xml-files to DIR\n"\
         "                           [default: vdr-conf-dir/plugins/webepg]\n"\
         "  -i DIR,   --import DIR   Import .epg.tar-files from this directory in offline-mode\n"\
         "                           [default: vdr-conf-dir/plugins/webepg]\n"\
         "  -l FILE,  --log FILE     Write a log in FILE\n"\
         "                           [default: no log]\n";
  return NULL;
}

bool cPluginWebepg::ProcessArgs(int argc, char *argv[])
{
  // Implement command line argument processing here if applicable.
  static struct option long_options[] = {
    { "debuglevel", required_argument, NULL, 'd'},
    { "confdir",    required_argument, NULL, 'c'},
    { "xmldir",     required_argument, NULL, 'x'},
    { "import",     required_argument, NULL, 'i'},
    { "log",        required_argument, NULL, 'l'},
    {NULL}
  };
  int c;
  while ((c = getopt_long(argc, argv, "d:c:x:i:l:", long_options, NULL)) != -1) {
    switch (c) {
      case 'd':
        DebugLevel = atoi(optarg);
        cout << "webepg: Debuglevel " << DebugLevel << " set\n";
        break;
      case 'c':
        ConfDir = optarg;
        cout << "ConfDir set\n";
        break;
      case 'x':
        xmldir = optarg;
        cout << "xmldir set\n";
        break;
      case 'i':
        importdir = optarg;
        cout << "importdir set\n";
        break;
      case 'l':
        webepglog = optarg;
        cout << "Logfile set\n";
        break;
    }
  }
  return true;
}

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

bool cPluginWebepg::Start(void)
{
  // Start any background activities the plugin shall perform.
  RegisterI18n(Phrases);
  dbgmess(1, "webepg: Initialising libcurl...");
  curl_global_init(CURL_GLOBAL_ALL);
  curl = curl_easy_init();
  curlp = curl_easy_init();
  if (DebugLevel >= 1)
    if ((curl == NULL)||(curlp == NULL))
      dbgmess(1, "failed");
    else 
      dbgmess(1, "done");
  logfile = fopen(webepglog, "w+");
  return true;
}

void cPluginWebepg::Stop(void)
{
  // Stop any background activities the plugin shall perform.
  curl_global_cleanup(); 
  if(logfile != NULL)
    fclose(logfile);
  logfile = NULL;
}

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

cOsdObject *cPluginWebepg::MainMenuAction(void)
{
  // Perform the action when selected from the main VDR menu.
  dbgmess(4, "MainMenuAction started");
  time_t CurrTime;
  time(&CurrTime);
  CurrDate = localtime(&CurrTime);
  return new cWebepg("webepg-Download");
  dbgmess(4, "MainMenuAction stopped");
}

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

bool cPluginWebepg::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  if(!strcasecmp(Name, "HideMenuEntry")) HideMenuEntry = atoi(Value);
  else if(!strcasecmp(Name, "Mode")) Mode = atoi(Value);
  else if(!strcasecmp(Name, "Days")) Days = atoi(Value);
  else return false;
  return true;
}

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

const char **cPluginWebepg::SVDRPHelpPages(void)
{
  // Return help text for SVDRP commands this plugin implements
  return NULL;
}

cString cPluginWebepg::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
  // Process SVDRP commands this plugin implements
  return NULL;
}


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