/*
 * vod.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.h>
#include <ctype.h>
#include <stack> 
#include <expat.h>
#include "parser.h"

#define BUFFSIZE 8192

extern cItems Items;
cItem   *item;
int     depth;
char    data_string[MAXTEXTLENLONG];
char    current_node[MAXTEXTLEN];
char    attribute_1[MAXTEXTLEN];
char    buff[BUFFSIZE];

struct XmlNode {
      char  nodename[MAXTEXTLEN];
      int   depth;
};

std::stack<struct XmlNode> nodestack;

// --- cItem ------------------------------------------------------------
cItem::cItem(const char *Title, const char *Desc, const char *Date, const char *Target) {
  strncpy(title, Title, MAXTEXTLEN);
  strncpy(desc, Desc, MAXTEXTLENLONG);
  strncpy(date, Date, MAXTEXTLEN);
  strncpy(target, Target, MAXTEXTLENLONG);
}

cItem::cItem(void) {
  strcpy(title, "");
  strcpy(desc, "");
  strcpy(date, "");
  strcpy(target, "");
}

void cItem::Clear(void) {
  strcpy(title, "");
  strcpy(desc, "");
  strcpy(date, "");
  strcpy(target, "");
}

// --- Other functions --------------------------------------------------
char *striphtml (char *s) {
  char *c,t=0,*r;
  c=s;
  r=s;
  while (*s!='\0') {
    if (*s=='<') {
      t++;
    } else if (*s=='>') {
      t--;
    } else if (t<1) {
      *(c++)=*s;
    }
    s++;
  }
  *c='\0';
  return r;
}

char *stripspaces (char *str) {
  char tmp[MAXTEXTLENLONG];
  char *ptr;
  
  if (str == NULL)
    return str;
  
  strncpy( tmp, str, MAXTEXTLENLONG );
  strcpy( str, "");
  ptr = strtok( tmp, " \n\t\r\x3F" );
  while(ptr) {
    strcat( str, ptr);
    strcat( str, " ");
    ptr = strtok( NULL, " \n\t\r\x3F" );
  }
  return str;
}

int dl_page(char *str) {
  char *cmd;
  int  len;
  
  cmd = strdup("");
  len = strlen(WGET) + strlen(WG_OPTS) + strlen(str) + 3;
  cmd = (char *)realloc(cmd, len);
  sprintf(cmd, "%s %s %s", WGET, WG_OPTS, str);
  dsyslog("vod/dl_page: running '%s'", cmd);
  //Skins.Message(mtInfo, "Downloading...");
  int r=system(cmd);
  if(r!=0) {
    esyslog("vod/dl_page: page download (via '%s') failed", cmd);
    free(cmd);
    return 0;
  }
  free(cmd);
  dsyslog("vod/dl_page: Done (return code: %d)", r);
  //Skins.Message(mtInfo, "Downloading: Done.");
  return -1;
}

// --- Expat callbacks --------------------------------------------------
static void XMLCALL
start(void *data, const char *el, const char **attr)
{
  XmlNode node;

  strncpy(node.nodename, el, MAXTEXTLEN);
  node.depth = depth;
  nodestack.push(node);

  //dsyslog("CURRENT_NODE: '%s', depth %d", el, depth);
  if (!strncmp(el, "item", 4)) {
    if (depth == 2) {
      cItem *tmpitem = new cItem;
      item = tmpitem;
      //dsyslog("NEW ITEM-----");
      item->Clear();
    }
  } else if (!strncmp(el, "enclosure", 9)) {
    //dsyslog("enclosure, depth %d", depth);
    for (int i = 0; attr[i]; i += 2) {
      if (!strncmp(attr[i], "url", 3)) {
        dsyslog("  url='%s'", attr[i + 1]);
        item->SetTarget(attr[i + 1]);
      }
    }
  }
  depth++;
}

static void XMLCALL
end(void *data, const char *el)
{
  char txt[MAXTEXTLENLONG];
  char parent[MAXTEXTLEN];
  
  //dsyslog("End of element '%s', stacksize=%d", el, nodestack.size());
  if (nodestack.size()>1) {
    nodestack.pop();
  } else {
    nodestack.pop();
    return;
  }
  strncpy(parent, (nodestack.top()).nodename, MAXTEXTLEN);
  // No need to free the node
  
  depth--;
  //dsyslog("         depth %d, parent '%s'", depth, parent);
  if (!strncmp(el, "item", 4)) {
    if (!strncmp(parent, "channel", 7)) {
      // End of the current item
      Items.Add(item);
    }
  } else if (!strncmp(el, "title", 5)) {
    stripspaces(data_string);
    if (!strncmp(parent, "item", 4)) {
      item->SetTitle(data_string);
    } else if (!strncmp(parent, "channel", 7)) {
      dsyslog("vod/rss_parser: RSS title '%s'", data_string);
    }
  } else if (!strncmp(el, "pubDate", 7)) {
    stripspaces(data_string);
    if (!strncmp(parent, "item", 4)) {
      item->SetDate(data_string);
    } else if (!strncmp(parent, "channel", 7)) {
      dsyslog("vod/rss_parser: RSS date '%s'", data_string);
    }
  } else if (!strncmp(el, "description", 11)) {
    if (!strncmp(parent, "item", 4)) {
      strncpy(txt, data_string, MAXTEXTLENLONG);
      striphtml(txt);
      stripspaces(txt);
      item->SetDesc(txt);
    } else if (!strncmp(parent, "channel", 7)) {
      dsyslog("vod/rss_parser: RSS description '%s'", data_string);
    }
  }
  //printf("(%s)\n", striphtml(stripspaces(data_string)));
  strcpy(data_string, "");
}

static void
data (void *user_data, const XML_Char * s, int len)
{
  /* Only until the maximum size of the buffer */
  if (strlen(data_string) + len <= MAXTEXTLENLONG)
    strncat(data_string, s, len);
}

// --- Parse an RSS  ------------------------------------------------------
int rss_parser(char * filename) {  
  FILE *fp;

  depth = 0;
  // Setup expat
  XML_Parser p = XML_ParserCreate(NULL);
  if (! p) {
    esyslog("vod/rss_parser: Couldn't allocate memory for parser");
    return 0;
  }

  XML_SetElementHandler(p, start, end);
  XML_SetCharacterDataHandler(p, data);

  // Clear Items list to play
  Items.Clear();
  // Load the RSS
  if ((fp = fopen(filename, "r")) == NULL) {
    esyslog("vod/rss_parser: file does not exist");
    return 0;
  }

  // Go through all items
  for(;;) {
    int done;
    int len;

    len = fread(buff, 1, BUFFSIZE, fp);
    if (ferror(fp)) {
      esyslog("vod/rss_parser: Read error");
      return 0;
    }
    done = feof(fp);
    if (XML_Parse(p, buff, len, done) == XML_STATUS_ERROR) {
      esyslog("vod/rss_parser: Parse error at line %d:\n%s\n",
              XML_GetCurrentLineNumber(p),
              XML_ErrorString(XML_GetErrorCode(p)));
      return 0;
    }

    if (done)
      break;
  }
  return -1;
}
