/*   -*- coding: utf-8 -*-  */
/****************************************************************************
*
*      Module  : SxPath
*      Fichier : SxPathEvalFirst.c
*      Auteur  : J. Ducloy
*
***************************************************************************/

#include <stdio.h>
#include <string.h>
#include "SxPath.h"
#include "Except.h"
#include <stdlib.h>

SxmlNode *SxPathEvalFirstChildPosition(SxPathExpression *xp1, SxmlNode *x1)
{
  int posPath;
  SxmlNode *x2;
  posPath=atoi(SxmlLeafText(xp1));
  x2=SxmlGetChildByPosition(x1, posPath);
  if (x2)
    {
      SxmlNode *xp2;
      if((xp2=SxmlNextSibling(xp1)))
	{
	  SxmlNode *x3;
	  x3=SxPathEvalFirstSelectAxis(xp2,x2);
	  return x3;
	}
      return x2;
    }
  return NULL;
}

/**
   Returns the SxmlNode in @a x1 matching @a xp1, beginning with axis
   @c child::

   @param xr1 the result of the evaluation
   @param xp1 the SxPath to evaluate (child::).
   @param x1  the Sxml tree on which to evaluate @a xp1
   @param pos the position in the @a xp1 (SxmlContainerInteger)
   
 */
SxmlNode *SxPathEvalFirstChild( SxPathExpression *xp1, SxmlNode *x1, SxmlNode *pos) /* translated from SxPathChildFirst */
{
  SxmlNode *x2;
  SxmlNode *x3;
  SxmlNode *testNameNode;
  SxmlNode *testPredicateNode;
  SxmlNode *xp2;

  SxmlReset(x1);
  x3=NULL;
  testNameNode=SxmlFirstChild(xp1);
  testPredicateNode=NULL;
  if(testNameNode)testPredicateNode=SxmlNextSibling(testNameNode);
  xp2=SxmlNextSibling(xp1);
 
  while((x2=SxmlNextNode(x1)))
    {
      SxmlNode *match;
      match =NULL;
      if (testNameNode)
	{
	  match=SxPathEvalTestNameInAxis(testNameNode, x2, pos);
	  if (match && testPredicateNode) match=SxPathEvalListPredicates(testPredicateNode, x2, pos);
	}
      else match=x2;
      if (match)
	{
	  if (xp2)
	    {
	      SxmlNode *x3;
	      x3=SxPathEvalFirstSelectAxis(xp2,x2);
	      if(x3) 
		{
		  return x3;
		}
	      else continue;
	    }
	  return x2;
	}
    }
  return NULL;
}

/**
   Returns the SxmlNode in @a x1 matching @a xp1, beginning with axis
   @c attribute::

   @param xr1 the result of the evaluation
   @param xp1 the SxPath to evaluate (attribute::).
   @param x1  the Sxml tree on which to evaluate @a xp1
   @param pos the position in the @a xp1 (SxmlContainerInteger)

   @internal @a xr1 is not used (except in a call to
             SxPathFirstSelectAxis).
 */
SxmlNode *SxPathEvalFirstAxisAttribute( SxPathExpression *xp1,  SxmlNode  *x1, SxmlNode *pos)
{
  SxmlNode *x2;
  SxmlNode *x3;
  SxmlNode *lAtt;
  SxmlNode *testNameNode;
  SxmlNode *testPredicateNode;

  lAtt=SxmlGetListAttributes(x1);
  if (!lAtt)return NULL;

  SxmlReset(lAtt);
  x3=NULL;

  testNameNode=SxmlFirstChild(xp1);
  if (! testNameNode)
    {
	ExceptSetError("SxPathAxisFirst","SY","invalid SxPath attribute name is mandatory ","","",2);
    }
  testPredicateNode=SxmlNextSibling(testNameNode);

  while((x2=SxmlNextNode(lAtt)))
    {
      SxmlNode *match;

      match=SxPathEvalTestNameInAxis(testNameNode, x2, pos);
      if (!match) continue;

      if (testPredicateNode) match=SxPathEvalListPredicates(testPredicateNode, x2, pos);

      if(match) return x2;
    }
  return NULL;
}


/**
   @brief Returns the first deepest descendant of @a x1.

   Returns the first deepest descendant of @a x1, or, if there is
   still another SxPathExpression after @a xp1, the node matching the
   whole SxPathExpression.

   @param xr1 the result of the evaluation
   @param xp1 the SxPath to evaluate (descendant).
   @param x1  the Sxml tree on which to evaluate @a xp1
   @param pos the position in the @a xp1 (SxmlContainerInteger)

   @internal @a xr1 is unused directly in the function (only calls
             suspected of using this parameter: SxPathFirstSelectAxis)
 */
SxmlNode *SxPathEvalFirstDescendant( SxPathExpression *xp1, SxmlNode *x1, SxmlNode *pos)
{
  SxmlNode *x2;
  SxmlNode *x3;
  SxmlNode *testNameNode;
  SxmlNode *testPredicateNode;
  SxmlNode *xp2;

  SxmlReset(x1);
  x3=NULL;

  testNameNode=SxmlFirstChild(xp1);
  if (! testNameNode)
    {
	ExceptSetError("SxPathAxisFirst","SY","invalid SxPath, descendant name is mandatory ","","",2);
    }
  testPredicateNode=SxmlNextSibling(testNameNode);
  xp2=SxmlNextSibling(xp1);

  while((x2=SxmlNextNode(x1)))
    {
      SxmlNode *x4;

      SxmlNode *match;
      match=SxPathEvalTestNameInAxis(testNameNode, x2, pos);
      if (testPredicateNode) match=SxPathEvalListPredicates(testPredicateNode, x2, pos);

      if(match) 
	{
	  if(xp2)
	    {
	      SxmlNode *x3;
	      x3=SxPathEvalFirstSelectAxis(xp2,x2);
	      if(x3)return x3;
	    }
	  return(x2);
	}
      if((x4=SxPathEvalFirstDescendant(xp1,x2,pos)))return x4;
    }
  return NULL;
}


/**
   @brief Returns the first following sibling matching the SxPath.

   Returns a pointer to the SxmlNode that matches the first following
   sibling of @a x1 and the SxPathExpression @a xp1, and possibly
   continues the evaluation of the SxPath by evaluating the next
   sibling of @a xp1 on the found SxmlNode.

   @param xr1 the result of the evaluation
   @param xp1 the SxPath to evaluate (following-sibling).
   @param x1  the Sxml tree on which to evaluate @a xp1
   @param pos the position in the @a xp1 (SxmlContainerInteger)

   @internal @a xr1 is not directly used by this function.
 */
SxmlNode *SxPathEvalFirstFollowingSibling(SxPathExpression *xp1, SxmlNode *x1, SxmlNode *pos)
{
  SxmlNode *x2;
  SxmlNode *x3;
  SxmlNode *testNameNode;
  SxmlNode *testPredicateNode;
  SxmlNode *xp2;

  x2=x1;
  x3=NULL;

  testNameNode=SxmlFirstChild(xp1);
  if (! testNameNode)
    {
	ExceptSetError("SxPathAxisFirst","SY","invalid SxPath, followingSibling name is mandatory ","","",2);
    }
  testPredicateNode=SxmlNextSibling(testNameNode);
  xp2=SxmlNextSibling(xp1);

  while((x2=SxmlNextSibling(x2)))
    {
      SxmlNode *match;
      match=SxPathEvalTestNameInAxis(testNameNode, x2, pos);
      if (testPredicateNode) match=SxPathEvalListPredicates(testPredicateNode, x2, pos);

      if(match) 
	{
	  if(xp2)
	    {
	      SxmlNode *x3;
	      x3=SxPathEvalFirstSelectAxis(xp2,x2);
	      if(x3) return x3;
	      else continue;
	    }
	  return(x2);
	}
    }
  return NULL;
}




/**
   Returns the first SxmlNode from @a x1 matching the axis contained in
   @a xp1.  

   Select the axis on top of @a xp1 and calls the appropriate
   function.

   @param xr1 the result of the evaluation
   @param xp1 the SxPath expression (here, an axis among @c child::,
              @c attribute::, @c descendant::, and @c following-sibling:: )
   @param x1  the Sxml tree on which @a xp1 is evaluated.

   @see \ref xpath

 */
SxmlNode *SxPathEvalFirstSelectAxis(SxPathExpression *xp1, SxmlNode  *x1) /* translated from  SxPathFirstSelectAxis */
{
  int       xpathNodeType;
  SxmlNode *pos;
  SxmlNode *result;

  xpathNodeType=SxmlNodeSubType(xp1);
  pos=SxmlContainerIntegerCreate(0);  /*  pos is to be incremented by SxPathEvalTestNameInAxis() */
  if(DilibGetTraceLevel()>1)
    {
      printf("*** SxPathFirstSelectAxis \n");
      SxmlDump(xp1);
    }
  switch(xpathNodeType)
    {
    case SXPATH_AXIS_CHILD:
      result=SxPathEvalFirstChild(xp1,x1,pos);
      break;
      
    case SXPATH_AXIS_CHILD_POSITION:
      result=SxPathEvalFirstChildPosition(xp1,x1);
      break;

    case SXPATH_AXIS_ATTRIBUTE:
      result=SxPathEvalFirstAxisAttribute(xp1,x1,pos);
      break;

    case SXPATH_AXIS_DESCENDANT:
      result=SxPathEvalFirstDescendant(xp1,x1,pos);
      break;

    case SXPATH_AXIS_FOLLOWING_SIBLING:
      result=SxPathEvalFirstFollowingSibling(xp1,x1,pos);
      break;

    default:
      {
	ExceptSetError("SxPathAxis","NY","code ",SxmlNodeName(xp1),"not yet implemented",2);
	exit (EXIT_FAILURE);  /* done by ExceptSetError */
      }
    }
  SxmlFree (pos);
  return result;
}

/**
   Launches the evaluation of an SxPathExpression (a location path or a
   union operator) on the @a x1 Sxml tree.

   @param xRes1    the result of the evaluation
   @param locPath1 location path or union operator to evaluate
   @param x1       the Sxml tree on which to evaluate @a locPath1

   @return
            - NULL if @a x1 is NULL.
            - the node resulting from the evaluation

   @see \ref locationpath "Location Path",
        SxPathSetEval

   @internal @a xRes1 is not used!
 */
SxmlNode *SxPathEvalFirst(SxPathExpression *locPath1, SxmlNode *x1)  /* translated from SxPathFirstEval */
{
  SxmlNode *xp1;
  int xpathNodeType;

  if(!x1)return NULL;
  xpathNodeType=SxmlNodeSubType(locPath1);
  switch(xpathNodeType)
    {
    case SXPATH_LOCATION_PATH:
      if((xp1=SxmlFirstChild(locPath1)))
	return SxPathEvalFirstSelectAxis(xp1,x1);
      return x1;
      
    case SXPATH_OPERATOR_UNION:
      {
	SxmlReset(locPath1);
	while((xp1=SxmlNextNode(locPath1)))
	  {
	    SxmlNode *x2;
	    x2=SxPathEvalFirst(xp1,x1);
	    if (x2)return x2;
	  }
	return NULL;
      }
    default:
      ExceptSetError("SxPathEvalFirst","NY","code ",SxmlNodeName(locPath1),"dont fit or not yet implemented",2);
	return NULL;
    }
}
