/**
 * \file XMLTag.cpp
 * Classe de description d'une structure XML.
 * 
 * @author Nicolas Janey
 * @author nicolas.janey@univ-fcomte.fr
 * @version 1.0, 01/01/10
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "XMLTag.h"
#include "XMLProperty.h"

#include "Vector.h"
#include "FileUtils.h"

static size_t trouveTagOuvrant(char *tag,char *t,size_t size,size_t pi,size_t *taille);
static size_t trouveTagOuvrantFermant(char *tag,char *t,size_t size,size_t pi,size_t *taille);
static XMLProperty **getProprietes(char *contenu,short *nb);
static char *extractionContenu(char *t,char *s,size_t pi,size_t pf);

static const size_t MOINS1 =(size_t) -1;

/**
 * Constructeur pour un nouvel objet XMLTag.
 *
 * @author Nicolas Janey
 * @author nicolas.janey@univ-fcomte.fr
 * @version 1.0, 01/01/10
 */
XMLTag::XMLTag(void) {
  name = NULL;
//  chaine = NULL;
  nbProprietes = 0;
  proprietes = NULL;
  nbTags = 0;
  tagList = NULL;
  strList = NULL;
  niveau = 0;
  num = 0;
  dynamique = 0;
}

/**
  * Constructeur pour un nouvel objet XMLTag.
  *
  * @param filename Le nom du fichier de chargement.
  * @param ts Le tableau des chaines de caracteres de recherche.
  * @param ns Le nombre de  chaines de caracteres de recherche.
  * @author Nicolas Janey
  * @author nicolas.janey@univ-fcomte.fr
  * @version 1.0, 01/01/10
  */
XMLTag::XMLTag(char *filename,char **ts,int ns) {
  dynamique = 1;
  nbTags = 0;
//  chaine = NULL;
  tagList = NULL;
  strList = NULL;
  niveau = 0;
  num = 0;
  size_t size =(size_t) fileSize(filename);
  char *t = loadFile(filename);
  size_t p = 0;
  size_t taille = size;
  for ( int i = 0 ; i < ns ; i++ ) {
    size_t aux = taille;
    p = trouveTagOuvrant(ts[i],t,size,p,&taille);
    if ( p == MOINS1 ) {
      taille = aux;
      p = trouveTagOuvrantFermant(ts[i],t,size,p,&taille);
      if ( p == MOINS1 ) {
        name =(char *) calloc(1,1);
        nbProprietes = 0;
        proprietes = NULL;
        free(t);
        return; } } }
  size_t pf = p;
  while ( t[pf] != '>' )
    pf++;
  char *contenu = extractionContenu(t,ts[ns-1],p,pf);
  proprietes = getProprietes(contenu,&nbProprietes);
  name =(char *) calloc(strlen(ts[ns-1])+1,1);
  strcpy(name,ts[ns-1]);
  free(contenu);
  free(t);
}

/**
 * Destructeur pour un objet XMLTag.
 *
 * @author Nicolas Janey
 * @author nicolas.janey@univ-fcomte.fr
 * @version 1.0, 01/01/10
 */
XMLTag::~XMLTag() {
  if ( name && dynamique )
    free(name);
//  if ( chaine )
//    free(chaine);
  if ( tagList ) {
    if ( dynamique ) {
      for ( int i = 0 ; i < nbTags ; i++ )
        delete(tagList[i]);
      free(tagList); } }
  if ( strList ) {
    //for ( int i = 0 ; i < nbTags+1 ; i++ )
    //  if ( strList[i] ) {
    //    free(strList[i]); }
    free(strList); }
  if ( proprietes ) {
    if ( dynamique ) {
      for ( int i = 0 ; i < nbProprietes ; i++ )
        delete(proprietes[i]);
      free(proprietes); } }
}

/**
 * Retour du premier tag inclu portant un nom.
 *
 * @param name Le nom du tag inclu recherche.
 * @return Le tag inclu (NULL si non trouve).
 * @author Nicolas Janey
 * @author nicolas.janey@univ-fcomte.fr
 * @version 1.0, 01/01/10
 */
XMLTag *XMLTag::getFirstTag(char *sname) {
  if ( !nbTags )
    return(NULL);
  int i = 0;
  while ( ( i < nbTags ) && ( strcmp(tagList[i]->name,sname) ) ) {
    i++; }
  return(( i == nbTags ) ? NULL : tagList[i]);
}

/**
 * Retour du prochain tag inclu portant le meme nom qu'un tag.
 *
 * @param tag Le tag de debut de recherche.
 * @return Le tag inclu (NULL si non trouve).
 * @author Nicolas Janey
 * @author nicolas.janey@univ-fcomte.fr
 * @version 1.0, 01/01/10
 */
XMLTag *XMLTag::getNextTag(XMLTag *tag) {
  if ( !nbTags )
    return(NULL);
  int i = tag->num+1;
  while ( ( i < nbTags ) && ( strcmp(tagList[i]->name,tag->name) ) ) {
    i++; }
  return(( i == nbTags ) ? NULL : tagList[i]);
}

/**
 * Retour de la valeur une propriete.
 *
 * @param name Le nom de la propriete recherchee.
 * @return La chaine valeur (NULL si non trouve).
 * @author Nicolas Janey
 * @author nicolas.janey@univ-fcomte.fr
 * @version 1.0, 01/01/10
 */
char *XMLTag::getProperty(char *sname) {
  if ( !nbProprietes )
    return(NULL);
  int i = 0;
  while ( ( i < nbProprietes ) && ( strcmp(proprietes[i]->name,sname) ) ) {
    i++; }
  return(( i == nbProprietes ) ? NULL : proprietes[i]->value);
}

static void tagOuvrant(char *st1,char *st2,char *tag) {
  st1[0] = '<';
  strcpy(&st1[1],tag);
  int l =(int) strlen(st1);
  st1[l] = ' ';
  st1[l+1] = 0x00;
  st2[0] = '<';
  strcpy(&st2[1],tag);
  st2[l] = '>';
  st2[l+1] = 0x00;
}

static void tagFermant(char *st1,char *st2,char *tag) {
  st1[0] = '<';
  st1[1] = '/';
  strcpy(&st1[2],tag);
  int l =(int) strlen(st1);
  st1[l] = '>';
  st1[l+1] = 0x00;
  st2[0] = '<';
  st2[1] = '/';
  strcpy(&st2[2],tag);
  st2[l] = ' ';
  st2[l+1] = 0x00;
}

static void tagFermant(char *st) {
  st[0] = '/';
  st[1] = '>';
  st[2] = 0x00;
}

static int occurence(char *s,char *t) {
  int l =(int) strlen(s);
  for ( int i = 0 ; i < l ; i++ )
    if ( s[i] != t[i] )
      return(0);
  return(1);
}

static size_t nextPos(char *s,char *t,size_t size,size_t pos) {
  size_t p = pos;
  int l =(int) strlen(s);
  while ( ( p+l < size ) && !occurence(s,&t[p]) )
    p++;
  if ( p+l >= size )
    return(MOINS1);
  return(p);
}

static size_t finTagFermant(char *t,size_t size,size_t pos) {
  while ( ( pos < size ) && ( t[pos] != '>' ) )
    pos++;
  if ( pos >= size )
    return(MOINS1);
  return(pos);
}

static size_t trouveTagOuvrant(char *tag,char *t,size_t size,size_t pi,size_t *taille) {
  size_t pos = pi;
  char st1[2048];
  char st2[2048];
  tagOuvrant(st1,st2,tag);
  pos = nextPos(st1,t,size,pos);
  if ( pos == MOINS1 )
    pos = nextPos(st2,t,size,pos);
  if ( pos == MOINS1 )
    return(MOINS1);
  tagFermant(st1,st2,tag);
  size_t end = nextPos(st1,t,size,pos);
  if ( end == MOINS1 )
    end = nextPos(st2,t,size,pos);
  if ( end == MOINS1 )
    return(MOINS1);
  end = finTagFermant(t,size,end);
  if ( end == MOINS1 )
    return(MOINS1);
  *taille = end-pos;
  return(pos);
}

static size_t trouveTagOuvrantFermant(char *tag,char *t,size_t size,size_t pi,size_t *taille) {
  size_t pos = pi;
  char st1[2048];
  char st2[2048];
  tagOuvrant(st1,st2,tag);
  pos = nextPos(st1,t,size,pos);
  if ( pos == MOINS1 )
    pos = nextPos(st2,t,size,pos);
  if ( pos == MOINS1 )
    return(MOINS1);
  tagFermant(st1);
  size_t end = nextPos(st1,t,size,pos);
  if ( end == MOINS1 )
    return(MOINS1);
  end = finTagFermant(t,size,end);
  if ( end == MOINS1 )
    return(MOINS1);
  *taille = end-pos;
  return(pos);
}

static int nextCararactere(char *contenu,int p,int l) {
  while ( ( p < l ) && ( contenu[p] == ' ' ) )
    p++;
  if ( p == l )
    return(-1);
  return(p);
}

static int nextGuillemet(char *contenu,int p,int l) {
  while ( ( p < l ) && ( contenu[p] != '"' ) )
    p++;
  if ( p == l )
    return(-1);
  return(p);
}

static int nextPostEgal(char *contenu,int p,int l) {
  while ( ( p < l ) && ( contenu[p] != '=' ) )
    p++;
  if ( p == l )
    return(-1);
  p--;
  while ( contenu[p] == ' ' )
    p--;
  return(p);
}

static XMLProperty **getProprietes(char *contenu,short *nb) {
  int l =(int) strlen(contenu);
  int p = 0;
  Vector *v = new Vector();
  while ( ( p < l ) && ( p != -1 ) ) {
    int i1 = p = nextCararactere(contenu,p,l);
    if ( p != -1 ) {
      int i2 = p = nextPostEgal(contenu,p+1,l);
      if ( p != -1 ) {
        p = nextGuillemet(contenu,p+1,l);
        if ( p != -1 ) {
          int i3 = p+1;
          p = nextGuillemet(contenu,p+1,l);
          if ( p != -1 ) {
            int i4 = p-1;
            p++;
            XMLProperty *prpt = new XMLProperty();
            prpt->setName(contenu,i1,i2);
            prpt->setValue(contenu,i3,i4);
            v->add(prpt); } } } } }
  XMLProperty **tv =(XMLProperty **) v->getArray();
  *nb =(short) v->size();
  delete(v);
  return(tv);
}

static char *extractionContenu(char *t,char *s,size_t pi,size_t pf) {
  pi++;
  pi +=(int) strlen(s);
  pi++;
  while ( t[pi] == ' ' )
    pi++;
  pf--;
  while ( t[pf] == ' ' )
    pf--;
  char *cnt =(char *) calloc((int) (pf-pi+2),1);
  for ( size_t i = pi ; i <= pf ; i++ )
    cnt[i-pi] = t[i];
  return(cnt);
}

///
/// Retour de la quantite de memoire utilisee.
///
/// @return la quantite de memoire.
/// @author Nicolas Janey
/// @author nicolas.janey@univ-fcomte.fr
/// @version 1.0, 01/01/10
///
size_t XMLTag::getUsedMemory(void) {
  size_t mem = sizeof(XMLTag);
  if ( name && dynamique )
    mem += strlen(name)+1;
  if ( tagList ) {
    if ( dynamique )
      for ( int i = 0 ; i < nbTags ; i++ )
        mem += tagList[i]->getUsedMemory();
    mem += nbTags*sizeof(XMLTag *); }
  if ( strList ) {
    //for ( int i = 0 ; i < nbTags+1 ; i++ )
    //  if ( strList[i] ) {
    //    free(strList[i]); }
    mem += (nbTags+1)*sizeof(char *); }
  if ( proprietes ) {
    if ( dynamique )
      for ( int i = 0 ; i < nbProprietes ; i++ )
        mem += proprietes[i]->getUsedMemory();
    mem += nbProprietes*sizeof(XMLProperty *); }
  return(mem);
}

///
/// Retour du nombre de handles memoires utilises.
///
/// @return le nombre de handles memoires.
/// @author Nicolas Janey
/// @author nicolas.janey@univ-fcomte.fr
/// @version 1.0, 01/01/10
///
long XMLTag::getUsedHandles(void) {
  long mem = 1;
  if ( name && dynamique )
    mem++;
  if ( tagList ) {
    if ( dynamique ) {
      for ( int i = 0 ; i < nbTags ; i++ )
        mem += tagList[i]->getUsedHandles();
      mem++; } }
  if ( strList ) {
    //for ( int i = 0 ; i < nbTags+1 ; i++ )
    //  if ( strList[i] ) {
    //    free(strList[i]); }
    mem++; }
  if ( proprietes ) {
    if ( dynamique ) {
      for ( int i = 0 ; i < nbProprietes ; i++ )
        mem += proprietes[i]->getUsedHandles();
      mem++; } }
  return(mem);
}
