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

/****************************************************************************
*
*      Projet  : DilibPro
*      Module  : Sxml
*      Fichier : SxmlTree.c
*      Auteur  : J. Ducloy
*
*-----------------------------------------------------------------------
*
*       Set of functions for browsing and updating xml trees
*
*-----------------------------------------------------------------------
*
*      Derniere mise a jour Octobre 2003
*
************************************************************************
* 
*     Copyrigth (C) 2003 CNRS INIST
*
****************************************************************************/


#include "SxmlNode.h"

SxmlNode *SxmlNextNode(SxmlNode *n1)
{
  SxmlNode *n2;
  n2=n1->nextNode;
  if(n2)n1->nextNode=n2->nextSibling;
  return n2;
}

SxmlNode *SxmlIteratorHasNextNode(SxmlNode *n1)
{
  SxmlNode *n2;
  n2=n1->nextNode;
  return n2;
}

SxmlNode	*SxmlAppendChild(SxmlNode *n1 , SxmlNode *n2)   /* -> fils */
{
  SxmlNode	*nextSib;
  if (n1)
    {
      if (n1->subType=='E')
	{
	  n1->subType='S';
	}
      if (n2)
	{
	  SxmlSetParent(n2,n1);
	  if ( SxmlFirstChild(n1) == NULL )
	    {
	      SxmlSetFirstChild(n1,n2);
	      SxmlSetLastChild(n1,n2);
	      if (SxmlIsElement(n1))SxmlElementResetEmpty(n1);
	      SxmlReset(n1);
	    }
	  else 
	    {
	      nextSib = SxmlLastChild(n1);
	      SxmlSetLastChild(n1,n2);
	      SxmlSetNextSibling(nextSib,n2);
	      SxmlSetPreviousSibling(n2,nextSib);
	    };
	}
    }
  else return(NULL);

  return (n2);
}

SxmlNode *SxmlGetChildByPosition(SxmlNode *n1, int pos)
{
  SxmlNode *c1;
  int currentPos;
  if (!n1)return NULL;
  c1=SxmlFirstChild(n1);
  currentPos=1;
  while (c1)
    {
      if (currentPos==pos) return c1;
      currentPos++;
      c1=SxmlNextSibling(c1);
    }
  return NULL;
}

SxmlNode *SxmlGetFirstChildByTagName(SxmlNode *n1, char *t1)
{
  SxmlNode *ch1;
  if((ch1 = SxmlFirstChild(n1))) {
    do {
      if(strcmp(t1, SxmlNodeName(ch1)) == 0) {
	return ch1;
      }
    } while ((ch1=SxmlNextSibling(ch1)));
  }
  return NULL;
}

int SxmlRankWithTagName(SxmlNode *x1)
{
  char *tag;
  SxmlNode *p1;
  int r;
  tag=SxmlNodeName(x1);
  p1=SxmlPreviousSibling(x1);
  if (!p1)return 1;
  r=1;
  while(p1)
    {
      if (SxmlNodeHasName(p1, tag)) r++;
      p1=SxmlPreviousSibling(p1);
    }
  return r;
}

SxmlNode *SxmlGetNextSiblingByTagName(n1,t1)
     SxmlNode *n1;
     char *t1;
{
  SxmlNode *ch1;
  ch1=n1;
  while((ch1=SxmlNextSibling(ch1)))
    {
      if (strcmp(t1, SxmlNodeName(ch1))==0)
	{
	  return ch1;
	}
    }
  return NULL;
}

/******************************************************************************
                       SxmlDescendant
***********************************************************************/
SxmlNode *SxmlGetFirstDescendantByTagNameWithSibling(SxmlNode *n1, char *t1)
{
   SxmlNode *ch1;
   SxmlNode *sib1;
   SxmlNode *ret1;
   if((ch1 = SxmlFirstChild(n1)))
     {
       if(strcmp(t1, SxmlNodeName(ch1)) == 0) return ch1;
       ret1=SxmlGetFirstDescendantByTagNameWithSibling(ch1, t1);
       if (ret1) return ret1;
     }
   sib1=SxmlNextSibling(n1);
   while (sib1)
     {
       if(strcmp(t1, SxmlNodeName(sib1)) == 0) return sib1;
       ret1=SxmlGetFirstDescendantByTagNameWithSibling(sib1, t1);
       if (ret1) return ret1;
       sib1=SxmlNextSibling(sib1);
     }
   return NULL;
}

SxmlNode *SxmlGetFirstDescendantByTagName(SxmlNode *n1, char *t1)
{
  SxmlNode *ch1;
  SxmlNode *sib1;
  SxmlNode *ret1;

  if((ch1 = SxmlFirstChild(n1)))
    {
      if(strcmp(t1, SxmlNodeName(ch1)) == 0) return ch1;
      return SxmlGetFirstDescendantByTagNameWithSibling(ch1, t1);
    }
  else return NULL;
}

SxmlNode *SxmlGetNextDescendantByTagName(SxmlNode *n1, SxmlNode *from, char *t1)
{
    SxmlNode *sib1;
    SxmlNode *ret1;
    SxmlNode *par1;
    sib1=SxmlNextSibling(from);
    while (sib1)               
      {
	if(strcmp(t1, SxmlNodeName(sib1)) == 0) return sib1;
	ret1=SxmlGetFirstDescendantByTagNameWithSibling(sib1, t1);
       if (ret1) return ret1;
	sib1=SxmlNextSibling(sib1);
      }
    par1=SxmlParent(from);
    while (par1)
      {
	SxmlNode *sib2;
	if (par1==n1)return NULL;
	if ((sib2=SxmlNextSibling(par1)))
	  {
	    if(strcmp(t1, SxmlNodeName(sib2)) == 0) return sib2;
	    return SxmlGetNextDescendantByTagName(n1, sib2, t1);
	  }
	par1=SxmlParent(par1);
      }
    return NULL;
}

SxmlNodeList *SxmlListDescendant(SxmlNode *root, char *tag)
{
  SxmlNodeList *list;
  SxmlNode     *elem;
  if ((elem=SxmlGetFirstDescendantByTagName(root, tag)))
    {
      list=SxmlNodeListCreate();
      while (elem)
	{
	  SxmlNodeListAppend (list, elem);
	  elem=SxmlGetNextDescendantByTagName(root, elem, tag);
	}
      return list;
    }
  else return NULL;
}

SxmlNode *SxmlGetFirstDescendantTagAtt(SxmlNode *root, char *tag, char *att, char *val)
{
  SxmlNode *elem;
  elem=SxmlGetFirstDescendantByTagName(root, tag);
  while (elem)
    {
      if (SxmlHasAttribute(elem, att, val)) return elem;
      elem=SxmlGetNextDescendantByTagName(root, elem, tag);
    }
  return NULL;
}
/****************************************************************************** 
 *                     SxmlInsertAfter

 ******************************************************************************/
SxmlNode  *SxmlInsertAfter(n1, n2)
          SxmlNode *n1;   /* point d'insertion */
          SxmlNode *n2;   /* noeud a inserer   */
{
      SxmlNode *pere;
      SxmlNode *FrereSuivant;

      if(!n1)return (NULL);
      if(!n2)return (NULL);

      pere = SxmlParent(n1);
      FrereSuivant = SxmlNextSibling(n1);
     
      SxmlSetParent(n2,pere);
      if(!FrereSuivant)
	{
	  if (pere) SxmlAppendChild(pere,n2);
	  else 
	    {
	      SxmlSetPreviousSibling(n2,n1);
	      SxmlSetNextSibling(n1,n2);
	      SxmlSetNextSibling(n2,NULL);
	    }
	}
      else {          
              SxmlSetNextSibling(n2,FrereSuivant);
	      SxmlSetPreviousSibling(FrereSuivant,n2);
              SxmlSetPreviousSibling(n2,n1);
	      SxmlSetNextSibling(n1,n2);
           }

      return(n2);
}

/****************************************************************************** 
 *                     SxmlInsertBefore
 *
 *
 ******************************************************************************/
SxmlNode  *SxmlInsertBefore(SxmlNode *n1, SxmlNode *n2)
                           /* n1 point d'insertion */
                           /* n2 noeud a inserer   */
{
      SxmlNode *pere;
      SxmlNode *FrerePrecedant;

      if(!n1)return (NULL);
      if(!n2)return (NULL);

      pere = SxmlParent(n1);
      FrerePrecedant = SxmlPreviousSibling(n1);

      SxmlParent(n2)=pere;
      if(!FrerePrecedant)
	{
	  if (pere)SxmlSetFirstChild(pere,n2);
	  SxmlSetPreviousSibling(n1,n2);
	  SxmlSetNextSibling(n2,n1);
	  SxmlSetPreviousSibling(n2,NULL);
	}
      else 
	{          
	  SxmlSetPreviousSibling(n2,FrerePrecedant); 
	  SxmlSetNextSibling(FrerePrecedant,n2);
	  SxmlSetNextSibling(n2,n1);
	  SxmlSetPreviousSibling(n1,n2);
	}

      return(n2);
}
/***************************************************************************

     SxmlAddFirst

****************************************************************************/
SxmlNode *SxmlAddFirstChild(np, ns)
     SxmlNode *np;
     SxmlNode *ns;
{
  SxmlNode *fils;

  if ((fils=SxmlFirstChild(np))) SxmlInsertBefore(fils, ns);
  else SxmlAppendChild(np, ns);
  return (ns);
}

SxmlNode *SxmlFreeNodeInTree(x1)
     SxmlNode *x1;
{
  SxmlNode *temp;
  SxmlNode *prev;
  SxmlNode *parent;
  SxmlNode *item;
  SxmlNode *retNode;

  if(!x1)return NULL;
  retNode=NULL;
  if(!(SxmlFirstChild(x1)))
    {
      SxmlFree(x1);
      return NULL;
    }
  prev=SxmlPreviousSibling(x1);
  parent=SxmlParent(x1);
  temp=SxmlElementCreate("#store");
  SxmlReset(x1);
  while((item=SxmlNextNode(x1)))SxmlAppendChild(temp,SxmlRemoveChild(item));
  SxmlFree(x1);
  SxmlReset(temp);
  while((item=SxmlNextNode(temp)))
    {
      if (!prev)SxmlAddFirstChild(parent, SxmlRemoveChild(item));
      else SxmlInsertAfter(prev,SxmlRemoveChild(item));
      if(!retNode)retNode=item;
      prev=item;
    }
  return retNode;
}

/********************************************************

    Combining tags and attributes
 
*********************************************************/

SxmlNode *SxmlGetNextSiblingTagAtt(x1,t1,a1,v1)
     SxmlNode *x1;
     char *t1;
     char *a1;
     char *v1;
{
  SxmlNode *retNode;
  if(!x1)return NULL;
  if (!(retNode=SxmlGetNextSiblingByTagName(x1,t1)))return NULL;
  if (SxmlHasAttribute(retNode,a1,v1))return retNode;
  return SxmlGetNextSiblingTagAtt(retNode,t1,a1,v1);
}

SxmlNode *SxmlGetFirstChildTagAtt(x1,t1,a1,v1)
     SxmlNode *x1;
     char *t1;
     char *a1;
     char *v1;
{
  SxmlNode *retNode;

  if(!x1)return NULL;
  if (!(retNode=SxmlGetFirstChildByTagName(x1,t1)))return NULL;
  if (SxmlHasAttribute(retNode,a1,v1))return retNode;
  else return SxmlGetNextSiblingTagAtt(retNode,t1,a1,v1);
}
