#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <regex.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libintl.h>
#define _(String) gettext (String)

#include <gnome-xml/parser.h>

#include "tab_categories.h"

/*-------------------------------------------------------------------
  Free all the elements of a tTab structure.
-------------------------------------------------------------------*/
void tab_free (ptrTab tab) {
  int idx;

  if (! tab) return;

  if (tab->title) free (tab->title);

  idx = 0;
  while (tab->patterns[idx])
    free (tab->patterns[idx++]);

  idx = 0;
  while (tab->regexp[idx])
    free (tab->regexp[idx++]);

  idx = 0;
  while (tab->strings[idx])
    free (tab->strings[idx++]);
}
    
/*-------------------------------------------------------------------
  Free a list of tTab structures, then free the list itself.
-------------------------------------------------------------------*/
void tab_free_all (ptrTab * tabs) {
  int idx;

  if (! tabs) return;

  idx = 0;
  while (tabs[idx]) {
    free (tabs[idx++]);
  }
  free (tabs);
}

/*-------------------------------------------------------------------
  Print out data from a tTab struct to stdout.
-------------------------------------------------------------------*/
void tab_print (ptrTab tab) {
  int idx;

  if (! tab) return;

  if (tab->title) printf (_("title = %s\n"), tab->title);

  idx = 0;
  while (tab->patterns[idx])
    printf (_("pattern = %s\n"), tab->patterns[idx++]);

  idx = 0;
  while (tab->regexp[idx]) {
    printf (_("regexp = %d\n"), (tab->regexp[idx])->re_nsub);
    idx++;
  }

  idx = 0;
  while (tab->strings[idx])
    printf (_("string = %s\n"), tab->strings[idx++]);
}
    
/*-------------------------------------------------------------------
  Build a tTab data structure from the parsed xml data.
-------------------------------------------------------------------*/
ptrTab tab_build_struct(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur) {
  ptrTab ret = NULL;
  int pidx = 0;
  int sidx = 0;

  /* allocate the struct */
  ret = (ptrTab) malloc(sizeof(tTab));
  if (ret == NULL) {
    fprintf(stderr,_("out of memory\n"));
    return(NULL);
  }
  memset(ret, 0, sizeof(tTab));

  if ((!strcmp(cur->name, "Tab")) && (cur->ns == ns)) {

    ret->title = xmlGetProp(cur, "title");
    if (ret->title == NULL) {
      tab_free (ret);
      return (NULL);
    }

    /* We don't care what the top level element name is */
    cur = cur->childs;
    while (cur != NULL) {
      if ((!strcmp(cur->name, "PatternMatch")) && (cur->ns == ns)) {
	/* Allocate space for another pattern string */
	ret->patterns = (char **) realloc (ret->patterns, 
					   sizeof (char *) * (pidx + 1));
	if (ret->patterns)
	  ret->patterns[pidx] = xmlGetProp (cur, "text");
	else {
	  tab_free (ret);
	  return NULL;
	}

	/* Allocate space for another compiled regexp */
	ret->regexp = (regex_t **) realloc (ret->regexp,
					    sizeof (regex_t *) * (pidx + 1));
	if (ret->regexp) {
	  ret->regexp[pidx] = (regex_t *) malloc (sizeof (regex_t));
	  regcomp (ret->regexp[pidx], ret->patterns[pidx], REG_NOSUB);
	}

	pidx++;
      }
      if ((!strcmp(cur->name, "StringMatch")) && (cur->ns == ns)) {
	ret->strings = (char **) realloc (ret->strings,
					  sizeof (char *) * (sidx + 1));
	if (ret->strings)
	  ret->strings[sidx++] = xmlGetProp (cur, "text");
	else {
	  tab_free (ret);
	  return NULL;
	}
      }
      cur = cur->next;
    }
    ret->patterns = (char **) realloc (ret->patterns, 
				       sizeof (char *) * (pidx + 1));
    ret->patterns[pidx] = NULL;
      
    ret->regexp = (regex_t **) realloc (ret->regexp, 
					sizeof (regex_t *) * (pidx + 1));
    ret->regexp[pidx] = NULL;
      
    ret->strings = (char **) realloc (ret->strings, 
				      sizeof (char *) * (sidx + 1));
    ret->strings[sidx] = NULL;
      
  }

  return(ret);
}

/*-------------------------------------------------------------------
  Return 1 if file exists (as regular file), or 0 otherwise.
-------------------------------------------------------------------*/
static int file_exists (char *filename) {
  struct stat s;

  if (! filename) return 0;

  if (stat(filename, &s) != 0)
    return 0;
  else {
    if (S_ISREG (s.st_mode))
      return 1;
    else
      return 0;
  }
}

/*-------------------------------------------------------------------
  Parse the xml file containing tab information.  Use this to build 
  a list of tTab structs that we'll use for matching later on.
-------------------------------------------------------------------*/
ptrTab * tab_parse_xml_file(char *filename) {
    xmlDocPtr doc;
    xmlNsPtr ns;
    xmlNodePtr cur;
    ptrTab tab;
    ptrTab *tabs = NULL;
    int tidx;

    /* Check for existence of file. */
    if (! file_exists (filename)) return NULL;

    /* Build an XML tree from a the file */
    doc = xmlParseFile(filename);
    if (doc == NULL) return(NULL);

    /* Check the document is of the right kind */
    cur = doc->root;
    if (cur == NULL) {
        fprintf(stderr,_("empty document\n"));
	xmlFreeDoc(doc);
	return NULL;
    }

    ns = xmlSearchNsByHref(doc, cur, "http://sourceforge.net/projects/gnulpr");
    if ((ns == NULL) || (strcmp(cur->name, "Helping"))) {
        fprintf(stderr,
	        _("Document of the wrong type\n"));
	xmlFreeDoc (doc);
	return NULL;
    }


    /* Now, walk the tree. */

    /* First level we expect just Tabs */
    cur = cur->childs;
    if ((strcmp(cur->name, "Tabs")) || (cur->ns != ns)) {
        fprintf(stderr,
		_("document of the wrong type, Tabs expected, found (%s)\n"),
		cur->name);
	xmlFreeDoc (doc);
	return NULL;
    }

    /* Second level is a list of Tab */
    cur = cur->childs;
    tidx = 0;
    while (cur != NULL) {
        if ((!strcmp(cur->name, "Tab")) && (cur->ns == ns)) {
	    tab = tab_build_struct(doc, ns, cur);

	    tabs = (ptrTab *) realloc (tabs, sizeof (ptrTab) * (tidx+1));
	    if (tabs) 
	      tabs[tidx++] = tab;
	    else {
	      /* free stuff here... */
	      return NULL;
	    }
	}
	cur = cur->next;
    }
    tabs = (ptrTab *) realloc (tabs, sizeof (ptrTab) * (tidx+1));
    tabs[tidx] = NULL;

    xmlFreeDoc (doc);
    return tabs;
}

/*-------------------------------------------------------------------
  Match the given description string to the list of strings and
  regexp patterns.  If there's a match, return the tab's title
  string.
-------------------------------------------------------------------*/
char * tab_match (char *desc, ptrTab *tablist) {
  int idx;
  int tidx = 0;
  char *retstr = NULL;
  int failed = REG_NOMATCH;
  ptrTab tab = NULL;

  if (! tablist) return NULL;

  tab = tablist[tidx++];
  while (tab) {
    /* First check simple string compares */
    idx = 0;
    while (tab->strings[idx]) {
      if (! strcmp (tab->strings[idx], desc)) {
	retstr = strdup (tab->title);
	return retstr;
      }
      idx++;
    }

    /* Last, check expensive regular expression matches */
    idx = 0;
    while (tab->regexp[idx]) {
      failed = regexec (tab->regexp[idx], desc, 0, NULL, 0) ;
      if (! failed) {
	retstr = strdup (tab->title);
	return retstr;
      }
      idx++;
    }

    tab = tablist[tidx++];
  }
  return NULL;
}

/*-------------------------------------------------------------------
  Used for testing only.
-------------------------------------------------------------------*/
int testmain(int argc, char **argv) {
  int i;
  ptrTab *cur;
  int cidx;
  char teststr[100];

  for (i = 1; i < argc ; i++) {
    cur = tab_parse_xml_file (argv[i]);

    if (cur) {
      printf ("\n\n%02d **************************\n", i);
      cidx = 0;
      while (cur[cidx]) {
	printf ("=============================\n");
	tab_print (cur[cidx++]);
      }
    }
    /* Some test matches */

    strcpy (teststr, "Watermark Stuff");
    printf (_("[%s] matches [%s]\n"), teststr, tab_match (teststr, cur));

    strcpy (teststr, "Foobar");
    printf (_("[%s] matches [%s]\n"), teststr, tab_match (teststr, cur));

    strcpy (teststr, "Water Mark String");
    printf (_("[%s] matches [%s]\n"), teststr, tab_match (teststr, cur));

    strcpy (teststr, "WaterMark");
    printf (_("[%s] matches [%s]\n"), teststr, tab_match (teststr, cur));


    tab_free_all (cur);
  }

    
  return(0);
}
