/*   -*- coding: utf-8 -*-  */

/****************************************************************************
*
*      Projet  : DilibPro
*      Module  : Sxml
*      Fichier : SxmlText.c
*      Auteur  : J. Ducloy
*
************************************************************************
* 
*     CopyLeft
*
****************************************************************************/
/**
   @file

   @brief Functions for SxmlText and SxmlLeaf handling.

   @author &copy; 2001 INIST-CNRS
   @author Jacques DUCLOY <DilibMaster@inist.fr>
 */

#include "Buffer.h"
#include "SxmlNode.h"
#include "Except.h"

/**
   Creates an SxmlText, and put @a str in it.

   @remark The created SxmlText have to be freed (using SxmlFree).

   @param str content of the new SxmlText
   @returns the created SxmlText
 */
SxmlText *SxmlTextCreate(char *str)
{
  SxmlNode *t1;

  t1=SxmlNodeCreate(XML_NODE_TEXT);
  SxmlSetNodeName(t1,"#text");
  if (str)
    {
        char *strD;
	SxmlSetNodeValueToFree(t1);
	if(!(strD=strdup(str)))
	  {
	    ExceptSetError("SxmlNode","MA", "memory allocation failed","","",2);
	  }
	SxmlSetNodeValue(t1,strD);
    }
  else SxmlSetNodeValue(t1,"");
  return t1;
}

SxmlText *SxmlCdataCreate(char *str)
{
  SxmlNode *t1;

  t1=SxmlNodeCreate(XML_NODE_CDATA_SECTION);
  SxmlSetNodeName(t1,"#cdata");
  if (str)
    {
        char *strD;
	SxmlSetNodeValueToFree(t1);
	if(!(strD=strdup(str)))
	  {
	    ExceptSetError("SxmlNode","MA", "memory allocation failed","","",2);
	  }
	SxmlSetNodeValue(t1,strD);
    }
  else SxmlSetNodeValue(t1,"");
  return t1;
}

SxmlText *SxmlTextCreateFromSubstr(char *str, int n)
{
  SxmlNode *t1;

  t1=SxmlNodeCreate(XML_NODE_TEXT);
  SxmlSetNodeName(t1,"#text");
  if (str)
    {
        char *str0;
	SxmlSetNodeValueToFree(t1);
	if(!(str0=malloc(n+1)))
	  {
	    ExceptSetError("SxmlNode","MA", "memory allocation failed","","",2);
	  }
	strncpy(str0, str, n);
	str0[n]='\0';
	SxmlSetNodeValue(t1,str0);
    }
  else SxmlSetNodeValue(t1,"");
  return t1;
}

/**
   Gets the first text inside @a x1 (in fact, the content of the first
   SxmlText in the tree).
   
   @param x1 the tree to search
   @returns the content of the first SxmlText found.
 */
char *SxmlGetFirstText(SxmlNode *x1)
{
  SxmlNode *x2;
  char *x3;
  if (!x1) return (NULL);
  if (SxmlIsText(x1))return (SxmlNodeValue(x1));
  if ( (x2=SxmlFirstChild(x1))
       &&(x3=SxmlGetFirstText(x2))) return (x3);
  if (( x2=SxmlNextSibling(x1)) ) return (SxmlGetFirstText(x2));
  return NULL;
}

/**
   Creates an SxmlLeaf with @a t1 tag, containing @a s1 text.

   @param t1 tag of the SxmlLeaf to create.
   @param s1 string to put in the SxmlLeaf.
   @returns
   - if t1 is not null, the SxmlLeaf
   - if t1 is null, return an SxmlText!
 */
SxmlLeaf *SxmlLeafCreate(char *t1, char *s1)
{
  SxmlNode *el1;

  if (t1)
    {
      el1=SxmlElementCreate(t1);
      if (s1)SxmlAppendChild(el1,SxmlTextCreate(s1));
      return el1;
    }
  if (s1) return SxmlTextCreate(s1);
  return NULL;
}

SxmlLeaf *SxmlLeafCreateFromSubstr(char *t1, char *s1, int n)
{
  SxmlNode *el1;

  if (t1)
    {
      el1=SxmlElementCreate(t1);
      if (s1)SxmlAppendChild(el1,SxmlTextCreateFromSubstr(s1, n));
      return el1;
    }
  if (s1) return SxmlTextCreateFromSubstr(s1, n);
  return NULL;
}

/**
   Determines whether @a x1 is an SxmlLeaf or not.

   @a x1 is an SxmlLeaf is it is an SxmlElement containing an SxmlText.

   @param x1 tree to check
   @returns 
	@a x1 if it is an SxmlLeaf,
	NULL otherwise.
 */
SxmlLeaf *SxmlIsLeaf(SxmlNode *x1)
{
  SxmlNode *x2;
  if ((x1)
      &&(SxmlIsElement(x1))
      &&((x2=SxmlFirstChild(x1)))
      &&(SxmlIsText(x2))
      &&(!(SxmlNextSibling(x2))))
    return x1;
  else return NULL;
}

void SxmlTextIndent(SxmlNode *n1, Buffer *bufInd, int inc)
{
  printf("%s%s\n", BufferString(bufInd), SxmlNodeValue(n1));
}

void SxmlCdataIndent(SxmlNode *n1, Buffer *bufInd, int inc)
{
  printf("%s<![CDATA[%s]]>\n", BufferString(bufInd), SxmlNodeValue(n1));
}

/**
   Sets the text of @a xl1 to @a s1.

   @param xl1 the SxmlLeaf for which to set the text
   @param s1  the text to set
   @return the modified @a xl1
 */
SxmlLeaf *SxmlLeafSetText(SxmlLeaf *xl1, char *s1)
{
  SxmlFree(SxmlFirstChild(xl1));
  SxmlAppendChild(xl1,SxmlTextCreate(s1));
  return xl1;
}

/**
   Gets the text of @a xl1

   @param xl1
   @returns the text of @a xl1, or NULL, when there is none.
 */
char *SxmlLeafText(SxmlLeaf *xl1)
{
  SxmlNode *c1;
  if((c1=SxmlFirstChild(xl1)))return SxmlNodeValue(c1);
  else return NULL;
}

/**
   Prints the content of @a t1 into @a f1.

   @a f1 must have been opened (with fopen) and has to be closed.

   @param t1 the SxmlText to print in @a f1
   @param f1 the file in which to print @a t1
 */
void SxmlTextFilePrint(SxmlText *t1, FILE *f1)
{
  fprintf(f1,"%s", SxmlNodeValue(t1));
}

void SxmlCdataFilePrint(SxmlText *t1, FILE *f1)
{
  fprintf(f1,"<![CDATA[%s]]>", SxmlNodeValue(t1));
}

SxmlLeaf *SxmlFirstChildLeafText(SxmlElement *parent, char *tag, char *text)
{
  SxmlNode *child;
  SxmlReset(parent);
  while ((child=SxmlNextNode(parent)))
    {
      if (!(SxmlIsLeaf(child)))continue;
      if (!(SxmlNodeHasName(child,tag)))continue;
      if (strcmp(SxmlLeafText(child),text) !=0) continue;
      return child;
    }
  return NULL;
}

/**
   Gets the textContent of @a xn.

   Returns the text content of @a xn and its descendants. No
   serialization is performed, the returned string does not contain
   any markup.

   DOM Level 3: attribute textContent.

   @param xn an SxmlNode

   @return the textcontent of @a xn, and its descendants, without any
   markup. Can be NULL. This textContent must be freed!

   @see http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Node3-textContent
   @see SxmlToString(3)

 */
char *SxmlGetTextContent(SxmlNode *xn) 
{

  if(!xn) return NULL;

  switch(SxmlNodeType(xn)) {
  case XML_NODE_ELEMENT:
  case XML_NODE_ATTRIBUTE:
  case XML_NODE_DOCUMENT_FRAGMENT:
    /* Recursive call on all children */
    {
      Buffer  *bufTextContent = BufferCreate(50,100);
      SxmlNode *xmlChild        = SxmlFirstChild(xn);
      char    *strTextContent = NULL;

      while(xmlChild) {
	char *strChildTextContent = SxmlGetTextContent(xmlChild);
	
	if(strChildTextContent)
	  BufferStrcat(bufTextContent,strChildTextContent);
	xmlChild = SxmlNextSibling(xmlChild);
      }
      strTextContent = BufferSave(bufTextContent);
      BufferFree(bufTextContent);
      return strTextContent;
    }
    break;

  case XML_NODE_TEXT:
/*case XML_NODE_CDATA_SECTION:*/
  case XML_NODE_COMMENT:
  case XML_NODE_PROCESSING_INSTRUCTION:
    if(SxmlNodeValue(xn)) {
      return strdup(SxmlNodeValue(xn));
    }
    break;

  case XML_NODE_DOCUMENT:
  case XML_NODE_DOCUMENT_TYPE:
/* case XML_NODE_NOTATION:*/
  default:
    break;
  }
  return NULL;
}

/**
   Returns the string representing the children of @a node.

   Ex:
   Let node be the SxmlNode for:
   @code
   <s1><sup>195</sup>Pt- und <sup>19</sup>F-NMR-Spektren der Fluoro-Chloro-Platinate(IV), &lsqb;PtF<sub>n</sub>Cl<sub>6-n</sub>&rsqb;<sup>2-</sup>, n = 0-6</s1>
   @endcode
   
   @code
   SxmlGetTaggedTextContent(node);
   @endcode
   will return:
   @code
   <sup>195</sup>Pt- und <sup>19</sup>F-NMR-Spektren der Fluoro-Chloro-Platinate(IV), &lsqb;PtF<sub>n</sub>Cl<sub>6-n</sub>&rsqb;<sup>2-</sup>, n = 0-6
   @endcode

   @param node
   @return the tagged textContent of @a node
   @warning returned string must be freed

   @see SxmlGetTextContent(3)

 */
char *SxmlGetTaggedTextContent(SxmlNode *node)
{
  SxmlNode *son=NULL;
  Buffer  *text;
  char    *textContent;

  text = BufferCreate(10,10);

  SxmlReset(node);

  while ((son=SxmlNextNode(node)))
    {
      BufferStrcat(text,SxmlToString(son));
    }

  textContent = BufferSave(text);

  if(text) {
    BufferFree(text);
    text = NULL;
  }

  return textContent;
}

/**
   Gets the textContent of @a xn, spacing each piece using @a r.

   Returns the text content of @a xn and its descendants. No
   serialization is performed, the returned string does not contain
   any markup (which are replace by the @a r character).

   @param xn an SxmlNode
   @param r  a character to replace the markups with

   @return the textcontent of @a xn, and its descendants, without any
   markup. Can be NULL. This textContent must be freed!

   @see SxmlToString(3)
   @see SxmlGetTextContent(3)

 */
char *SxmlGetSpacedTextContent(SxmlNode *xn, char r)
{

  if(!xn) return NULL;

  switch(SxmlNodeType(xn)) {
  case XML_NODE_ELEMENT:
  case XML_NODE_ATTRIBUTE:
  case XML_NODE_DOCUMENT_FRAGMENT:
    /* Recursive call on all children */
    {
      Buffer  *bufTextContent = BufferCreate(50,100);
      SxmlNode *xmlChild        = SxmlFirstChild(xn);
      char    *strTextContent = NULL;
      int      i              = 0;

      while(xmlChild) {
	char *strChildTextContent = SxmlGetSpacedTextContent(xmlChild,r);
	
	if(strChildTextContent) {
	  if(i) BufferCharCat(bufTextContent,r);
	  BufferStrcat(bufTextContent,strChildTextContent);
	  i++;
	}
	xmlChild = SxmlNextSibling(xmlChild);
      }
      strTextContent = BufferSave(bufTextContent);
      BufferFree(bufTextContent);
      return strTextContent;
    }
    break;

  case XML_NODE_TEXT:
/*case XML_NODE_CDATA_SECTION:*/
  case XML_NODE_COMMENT:
  case XML_NODE_PROCESSING_INSTRUCTION:
    if(SxmlNodeValue(xn)) {
      return strdup(SxmlNodeValue(xn));
    }
    break;

  case XML_NODE_DOCUMENT:
  case XML_NODE_DOCUMENT_TYPE:
/* case XML_NODE_NOTATION: */
  default:
    break;
  }
  return NULL;
}

char *SxmlTextBufferAppend(Buffer *b1, SxmlNode *n1)
{
  SxmlNode *c1;
  if(!n1)return NULL;
  switch(SxmlNodeType(n1))
    {
    case SXML_NODE_TEXT:
      BufferStrcat(b1, SxmlNodeValue(n1));
      break;
    case SXML_NODE_ELEMENT:
      c1=SxmlFirstChild(n1);
      while(c1)
	{
	  SxmlTextBufferAppend(b1, c1);
	  c1=SxmlNextSibling(c1);
	}
      break;
    }
  return BufferString(b1);
}

int SxmlTextCmp(SxmlNode *n1, SxmlNode *n2)
{
  static Buffer *b1=NULL;
  static Buffer *b2=NULL;
  if (!n1)
    {
      if(!n2) return 0;
      return 1;
    }
  if(!n2) return -1;
  if (SxmlIsText(n1)&&SxmlIsText(n2)) return (strcmp(SxmlNodeValue(n1), SxmlNodeValue(n2)));
  if (!b1) b1=NewBuffer();
  if (!b2) b1=NewBuffer();
  BufferReset(b1);
  BufferReset(b2);
  return (strcmp(SxmlTextBufferAppend(b1, n1), SxmlTextBufferAppend(b2, n2)));
}
