/***********************************************************************
*
*      Projet  : DilibSxml
*      Module  : SxPath
*      Fichier : SxPathTools.c
*      Auteur  : J. Ducloy
*      Origine : J. Ducloy
*
*      Derniere mise a jour Decembre 2000
*      $Id: SxPathTools.c,v 1.6 2003/10/06 14:58:19 parmentf Exp $
*
************************************************************************
* 
*     Copyright (C) 2000 INIST
*
************************************************************************/
/**
   @file

   @brief Internal functions for evaluating SxPath axis

   Mainly descendant:: and child:: axis.

   @todo Create an SxPathIter type? for @ref SxPathIterCreate, @ref
   SxPathIterInit, etc.
   
   @author &copy; 2000 INIST-CNRS
   @author Jacques DUCLOY <DilibMaster@inist.fr>
 */


#include "SxPath.h"
#include "string.h"
#include "SgmlNode.h"
/**
   @see \ref SxPathIterInterTopImpl "Implementation of SxPathIterInterTop"
 */
SxmlNode *SxPathIterInterTop();

/** Trace level of SxPathInter. 0 means no trace. */
int SxPathInterTraceLevel=0;

/**
   Set the trace level for SxPathIter functions.

   @param l trace level
   @note Debug function
 */
void SxPathInterSetTrace(int l)
{SxPathInterTraceLevel=l;}


/**
   Returns the first descendant next in the tree (in the siblings of
   @a n1) which name is @a t1.

   Returns NULL if none is found.

   @param n1
   @param t1 tag name
 */
SxmlNode *SxPathDescendantNameNextInTree(SxmlNode *n1,
				       char    *t1)
{
  SxmlNode *n2;
  SxmlNode *n3;
  n2=n1;
  while((n2=SxmlNextSibling(n2)))
    {
      if ((n3=SxPathDescendantFirstByTagName(n2,t1)))return n3;
    }
  return NULL;
}

/**
   If @a n1 is a tag and has @a t1 as name, returns @a n1.  
   
   Else, if @a n1 has a descendant which name is @a t1, returns this
   descendant.

   Otherwise, returns the first descendant in the remaining of the
   tree (in the siblings of @a n1) wich name is @a t1.

   @param n1 
   @param t1 tag name
   @internal Beware: this calls SxPathDescendantNameNextInTree, which
   calls SxPathDescendantFirstByTagName, which calls this one.
 */
SxmlNode *SxPathDescendantNameFirstInTree(SxmlNode *n1,
					char    *t1)
{ 
  SxmlNode *n2;
  SxmlNode *n3;
  if (!n1)return NULL;
  if (SxmlIsElement(n1))
    {
      if (SxmlNodeHasName(n1,t1))return n1;
      if ((n2=SxmlFirstChild(n1))&&(n3=SxPathDescendantNameFirstInTree(n2,t1)))return n3;
    }
  return SxPathDescendantNameNextInTree(n1,t1);
}

/**
   If @a n1 is a tag and has @a t1 as name, returns @a n1.

   Else, returns the first descendant of @a n1 which name is @a t1.

   If no descendant has this property, returns NULL.

   @param n1
   @param t1 tag name
   @note Returns NULL too if @a n1 is NULL, or if it is not a tag.
 */
SxmlNode *SxPathDescendantFirstByTagName(SxmlNode *n1,
				       char    *t1)
{
  if (!n1)return NULL;
  if (!SxmlIsElement(n1))return NULL;
  if (SxmlNodeHasName(n1,t1))return n1;
  return (SxPathDescendantNameFirstInTree(SxmlFirstChild(n1),t1));
}

/**
   Returns a node in the tree *after* @a n1 (a descendant of @a n1, or
   another node on a next branch of @a n1's tree), which name is @a
   t1.

   If @a n2 equals @a n1, looks only in @a n1's descendants, not in the
   remaining branches.

   @param n1
   @param t1
   @param n2
   @returns
   - NULL if @a n1 is NULL
   - the first descendant in the descendant of the next siblings of @a
     n1 which name is @a t1 if it exists.
   - NULL if @a n1 equals @a n2, and there is no node such as
     described in the previous item.
   - if none of the previous item gives a result, the first node in
     the remaining of @a n1's tree which name is @a t1.
 */
SxmlNode *SxPathDescendantNameNext(SxmlNode *n1,
				 char    *t1,
				 SxmlNode *n2)
{
  SxmlNode *n3;
  if (!n1)return NULL;
  if ((n3=SxPathDescendantNameNextInTree(n1,t1)))return n3;
  if (n1==n2)return NULL;
  n3=n1;
  while ((n3=SxmlParent(n3)))
    {
      SxmlNode *n4;
      if (n3==n2)return NULL;
      if ((n4=SxPathDescendantNameNextInTree(n3,t1)))return n4;
    }
  return NULL; /* WARNING: added by someone else than the author */
}


/**
   Sets the NextNode property of @a n1 to the first descendant of @a
   n2 which name is @a t (if possible). Returns it. Set the
   ExpressionNode of @a n1 to @a n2. Set @a n1's name to @a t.

   If any of the parameters is NULL, returns NULL.

   @param n1 SxPathResultStep to update
   @param n2 SxmlNode to treat
   @param t  tag name looked for
   @returns the first descendant of @a n2 which name is @a t.
 */
SxmlNode *SxPathDescendantNameIterInit(SxPathResultStep *n1,
				     SxmlNode         *n2,
				     char            *t)
{
  if(n1&&n2&&t)
    {
      SxmlSetNextNodeProperty(n1, SxPathDescendantFirstByTagName(n2, t));
      SxmlSetNodeValue(n1, ((char *)n2));
      SxmlSetNodeName(n1,t);
    }
  else return NULL;
  return SxmlNextNodeProperty(n1);
 
}


/**
   Sets @a n1's NextNode property to the first node in the tree after
   the old @a n1's NextNode, which name is @a n1's.

   @param n1
   @return the old NextNode property of @a n1
 */
SxmlNode *SxPathDescendantNameIterNext(SxPathResultStep *n1)
{
    SxmlNode *n2;
    n2=SxmlNextNodeProperty(n1);
    if (n2)
      {
	SxmlSetNextNodeProperty
	  (n1,
	   SxPathDescendantNameNext(n2,
				   SxmlNodeName(n1),
				   SxmlNodePointerValue(n1)));
      }
    return n2;
}

/**
   Sets the name of @a n1 to @a t1.
   Sets its NextNode to the first child of @a n2 which name is @a t1.

   @param n1 (could be a SxPathResultStep ?)
   @param n2 
   @param t1 tag name

   @returns NULL if @a n1 or @a n2 or @a n3 is NULL, else the new
   NextNode of @a n1
 */
SxmlNode *SxPathChildNameIterInit(SxmlNode *n1,
				SxmlNode *n2,
				char    *t1)
{
  if(n1&&n2&&t1)
    {
      SxmlSetNextNodeProperty(n1, SxmlGetFirstChildByTagName(n2, t1));
      SxmlSetNodeName(n1,t1);
    }
  else return NULL;
  return SxmlNextNodeProperty(n1);
}

/**
   Sets NextNode property of @a n1 to the first next sibling of its
   NextNode which names matches the one of @a n1.

   @return the NextNode of @a n1 
   @param n1 (could be a SxPathResultStep)
 */
SxmlNode *SxPathChildNameIterNext(SxmlNode *n1)
{
  SxmlNode *n2;

  n2=SxmlNextNodeProperty(n1);
  SxmlSetNextNodeProperty(n1, SxmlGetNextSiblingByTagName(n2, SxmlNodeName(n1)));
  return n2;
}

/**
   Creates an SxmlNode which name is "stack".
   @returns the created SxmlNode (a stack)
 */
SxmlNode *SxPathIterCreate()
{
  SxmlNode *iter;
  iter=SxmlElementCreate("stack");
  return iter;
}

/**
   Does nothing but returning NULL!

   @param p1
   @param n1
   @returns NULL
   @internal Was it buggy? Is it useless now?

   @todo Write or delete this function (and, if delete, all the
   calling functions).
 */
SxmlNode *SxPathCreateItem(SxmlNode *p1, SxmlNode *n1)
{
  /*
  SxmlNode *item;
  if(!n1)return NULL;
  if(!p1)return NULL;
  if (SxmlNodeHasName(p1, "descendantName")
      ||SxmlNodeHasName(p1, "childName"))
    {
      SxmlNode *pN;
      SxmlNode *pP;
      char *att;
      item=SxmlElementCreate("item");
      if ( SxPathInterTraceLevel > 0)SxmlSetAttribute(item,"proc",SxmlNodeName(p1));
      SxmlAppendChild (item, pP=SgmlIterCreate());
      SgmlIterSetNode(pP,p1);
      if (!(att=SxmlGetAttribute(p1,"name")))
	ExceptSetError("SxPath", "co", "attribute name is mandatory in descendantName","","",2);
      SgmlIterSetStr(pP,SxmlNodeName(p1));
      SxmlAppendChild (item, pN=SgmlIterCreate());
      if (SxmlNodeHasName(p1, "descendantName"))SxPathDescendantNameIterInit(pN,n1,att);
      else SxPathChildNameIterInit(pN,n1,att);
      return item;
    }
  
  ExceptSetError("SxPath", "co", "not yet implemented","","",2);
  */
  return NULL;
}

/**
   Frees all @a i1's children, without unlinking them.

   @param i1 iterator (SxPathIter ?)
   @param p1 path (SxPathExpression ?)
   @param n1 node
   @return NULL

   @warning Do not use this twice! Memory fault danger!

   @internal As SxPathCreateItem does nothing than returning NULL, this
   always return NULL!
 */
SxmlNode *SxPathIterInit(SxmlNode *i1,	/* SxPathIter */
		       SxmlNode *p1,
		       SxmlNode *n1)
{
  SxmlNode *bottom;
  SxmlFreeChildren(i1);
  if ((bottom=SxPathCreateItem(p1,n1)))
    {
      SxmlAppendChild (i1, bottom);
      return i1;
    }
  return NULL;
}

/**
   Frees all children of @a stack, except the first one.

   @param stack
   @returns NULL pointer

   @note Don't call this function twice on the same @a stack.
   @warning Empties the stack (ie, frees all @a stack's children,
   except the first one, but does not change the links with the old
   children, the lastChild pointer is still pointing on a freed part
   of memory). This can lead to Segmentation faults, if one calls this
   function more than once on @a stack!
 */
SxmlNode *SxPathIterStackDown(SxmlNode *stack)
{
  SxmlNode *top;			/* SxPathIter */
  SxmlNode *beforeTop;
  top=SxmlLastChild(stack);
  beforeTop=SxmlPreviousSibling(top);
  while (beforeTop)
    {
      SxmlNode *ret3;
      SxmlFree(top);
      top=beforeTop;
      if ((ret3=SxPathIterInterTop(top)))return ret3;
      beforeTop=(SxmlPreviousSibling(top));
    }
  return NULL;
}

/**
   \anchor SxPathIterInterTopImpl
   Does nothing.

   @returns NULL
   @param top
   @internal Was it buggy? Is it useless now?
 */
SxmlNode *SxPathIterInterTop(SxmlNode *top)
{
  /*
  SxmlNode *retVal;
  if (!top)return NULL;
  retVal=NULL;
  if (SxmlNodeHasName(SxmlFirstChild(top), "descendantName"))
    {
      retVal=SxPathDescendantNameIterNext(SxmlLastChild(top));
    }
  else if (SxmlNodeHasName(SxmlFirstChild(top), "childName"))
    {
      retVal=SxPathChildNameIterNext(SxmlLastChild(top));
    }
  if (retVal)
    {
      SxmlNode *progChild;
      if(progChild=SxmlFirstChild(SgmlIterGetNode(SxmlFirstChild(top))))
	{
	  SxmlNode *item;
	  SxmlNode *ret2;
	  SxmlNode *beforeTop;
	  item=SxPathCreateItem(progChild,retVal);
	  SxmlAppendChild (SxmlParent(top), item);
	  ret2=SxPathIterInterTop(item);
	  if(ret2)return (ret2);
	  return SxPathIterStackDown(SxmlParent(top));
	}
      return retVal;
    }
  return SxPathIterStackDown(SxmlParent(top));
  */
  return NULL; /* Avoid WARNING */
}

/**
   As SxPathIterInterTop returns a NULL pointer, this function does the
   same.
   @param i1 iterator
   @returns NULL
   @internal Shall we keep this function here?
 */
SxmlNode *SxPathIterNext(SxmlNode *i1)
{
  if ( SxPathInterTraceLevel > 0)
    {
      printf("--- SxPathIterNext\n"); 
      if ( SxPathInterTraceLevel > 1){SxmlPrint(i1);putchar('\n');}
    }
  return SxPathIterInterTop(SxmlLastChild(i1));
}
