
/***********************************************************************
*
*      Projet  : DilibXml
*      Module  : SxPath
*      Fichier : SxPathEval.c
*      Auteur  : J. Ducloy
*
*      as SXPATH_RESULT_ITER is deprecated most of this module is deprecated.
*      only FIRST and STEP will have to be used.
*
*      Stabilized part of this file will be moved to SxPathEvalTools.c
*
************************************************************************
* 
*     CopyLeft (NC) 2001 CNRS INIST
*
************************************************************************/
/**
   @file

   @brief Functions for evaluating SxPathExpressions
   
   @author &copy; INIST-CNRS
   @author Jacques DUCLOY <DilibMaster@inist.fr>
 */
#include <stdio.h>
#include "Except.h"
#include "SxPath.h"

/**
   Creates and returns an SxPathResult of various possible @a resultType:
   - SXPATH_RESULT_ITER_NODE
   - SXPATH_RESULT_FIRST_NODE
   - SXPATH_RESULT_NODE_SET

   The SxPathResult returned have a SxmlNodePointer pointing to @a
   proc1, and followed by an element @e #stack in the case of an
   SXPATH_RESULT_ITER_NODE, an SxmlNodePointer named @e #result in the
   case of SXPATH_RESULT_FIRST_NODE or SXPATH_RESULT_NODE_SET.

   @param proc1
   @param resultType
 */

SxPathResult *SxPathResultCreate(SxmlProcessor *proc1,
			       int resultType)
{
  SxPathResult *xr1;
  SxmlNode     *resultNode;
  xr1=SxmlNodeCreate(SXML_NODE_XPATH_RESULT);
  SxmlSetNodeName(xr1,"#pathResult");
  SxmlSetNodeSubType(xr1, resultType);
  SxmlAppendChild(xr1,SxmlNodePointerCreate(proc1));
  switch(resultType)
    {
    case SXPATH_RESULT_ITER_NODE:
      SxmlAppendChild(xr1,SxmlElementCreate("#stack"));
      break;
    case SXPATH_RESULT_FIRST_NODE:
      SxmlAppendChild(xr1,resultNode=SxmlNodePointerCreate(NULL));
      SxmlSetNodeName(resultNode,"#result");
      break;
   case SXPATH_RESULT_NODE_SET:
      SxmlAppendChild(xr1,resultNode=SxmlNodePointerCreate(NULL));
      SxmlSetNodeName(resultNode,"#result");
      break;
    }
  return xr1;
}



/**
   Resets @a xs1 and returns it.

   Sets the ExpressionNode to @a xpe1, the ContextNode to NULL, and
   the NextNode of @a xs1 to NULL.

   @param xs1  SxPathResultStep
   @param xpe1 SxPathExpression
 */
SxmlNode *SxPathResultStepReset(SxPathResultStep *xs1,
			      SxPathExpression *xpe1)
{
  SxPathSetResultStepExpressionNode(xs1,xpe1); 
  SxPathSetResultStepContextNode(xs1,NULL);
  SxmlSetNextNodeProperty(xs1,NULL);
  return xs1;
}


/**
   Creates an SxPathResultStep from @a xr1 and @a xpe1.

   Its name is @e #step, its value is @a xpe1.

   Adds the newly created SxPathResultStep to @a xr1's stack.

   @param xr1  the SxPathResult to which the new SxPathResultStep is added
   @param xpe1 
   @returns the new SxPathResultStep
 */
SxPathResultStep *SxPathResultStepCreate(SxPathResult     *xr1,
				       SxPathExpression *xpe1)
{
  SxPathResultStep *xs1;

  xs1=SxmlContainerCreate(XML_CONTAINER_XML_NODE);
  SxmlSetNodeName(xs1,"#step");
  SxmlSetNodeValue(xs1,(char *)xpe1);
  SxmlAppendChild(SxPathResultStack(xr1),xs1);
  return xs1;
}


/**
   Makes @a x1 the new top, by freeing all its next siblings.

   @param x1
 */
void SxPathResultNewTop(SxPathResultStep *x1)
{
  SxmlNode *x2;
  SxmlNode *x3;
  if(!(x2=SxmlNextSibling(x1)))return;
  do
    {
      x3=SxmlNextSibling(x2);
      SxmlFree(x2);
    }while((x2=x3));
}

/**
   Evaluates an SxPathResultStep on an SxmlNode and adds the result to
   the SxPathResult.

   Then calls the evaluation for the next SxPathResultStep.

   @param xpr1
   @param stepNode
   @param x1
   @returns
   - SXPATH_RESULT_ANY_TYPE when the Expression of @a stepNode was a
     SXPATH_LOCATION_PATH
   - SXPATH_RESULT_NODE_SET
   - 0
 */
int SxPathResultStepEvaluate(
     SxPathResult     *xpr1,
     SxPathResultStep *stepNode,
     SxmlNode         *x1)
{
  int xpathNodeType;
  SxmlNode *progNode;
  SxmlNode *stepContext;
  SxmlNode *a1;
  SxmlNode *nextStepExpr;
  SxmlNode *nextStep;
  int nextStepResult;

  a1=NULL;
  progNode=SxPathResultStepExpressionNode(stepNode);
  stepContext=SxPathResultStepContextNode(stepNode);
  xpathNodeType=SxmlNodeSubType(progNode);

  if(!stepContext)
    {
      switch(xpathNodeType)
	{
	case SXPATH_LOCATION_PATH:
	  nextStep=SxPathResultStepCreate(xpr1,SxmlFirstChild(progNode));
	  SxPathResultStepEvaluate(xpr1,nextStep,x1);
	  return SXPATH_RESULT_ANY_TYPE;
	case SXPATH_AXIS_CHILD:
	  a1=SxPathChildReset(stepNode,x1);
	  break;
	default:
	  ExceptSetError("SxmlParser","NI","not yet implemented","","",2);
	}
    }
  else
    {
      switch(xpathNodeType)
	{
	case SXPATH_AXIS_CHILD:
	  a1=SxPathChildNextNode(stepNode);
	  break;
	default:
	  ExceptSetError("SxmlParser","NI","not yet implemented","","",2);
	  exit (EXIT_FAILURE);
	}
    }

  if(!a1)return 0;
  if(!(nextStepExpr=SxmlNextSibling(progNode)))
    {
      SxPathResultNewTop(stepNode);
      return SXPATH_RESULT_NODE_SET;
    }
  if((nextStep=SxmlNextSibling(stepNode)))
    SxPathResultStepReset(nextStep,nextStepExpr);
  else nextStep=SxPathResultStepCreate(xpr1,nextStepExpr);
  nextStepResult=SxPathResultStepEvaluate(xpr1,nextStep, a1);
  if(nextStepResult)return SXPATH_RESULT_NODE_SET;
  SxPathResultStepEvaluate(xpr1, stepNode, SxmlNextNodeProperty(a1));
  
  return 0;
}

/**
   Evaluates an SxPathExpression (@a xe1) on an SxmlNode (@a xn1), and
   returns an SxPathResult.

   @param xe1 the SxPathExpression to evaluate.
   @param xn1 the SxmlNode to evaluate @a xe1 on.
   @param resultType
   - SXPATH_RESULT_ITER_NODE
   - SXPATH_RESULT_FIRST_NODE
   - SXPATH_RESULT_NODE_SET
   @param resultNode
   @todo Check why @a resultNode is not used, and if it is useful to
   let it as a parameter.
   @todo Seems to not yet been finished (only SXPATH_RESULT_ITER_NODE
   case is implemented)
   @todo Check if the NULL pointer placed in SxPathResultCreate should
   not be replaced by a SxmlProcessorImplicit!
   @internal Call to SxPathResultStepCreate lacked a parameter!
 */
SxPathResult *SxPathExpressionEvaluate(
     SxPathExpression *xe1,
     SxmlNode         *xn1,
     int              resultType,
     SxPathResult     *resultNode)
{
  SxPathResult     *xpr;
  SxPathResultStep *step1;

/*   This was written as is. There lacked a parameter. */
/*   xpr=SxPathResultCreate(resultType); */
  xpr=SxPathResultCreate(NULL, resultType);
  switch(resultType)
    {
    case SXPATH_RESULT_ITER_NODE:
      step1=SxPathResultStepCreate(xpr,xe1);
      SxPathResultStepEvaluate(xpr,step1,xn1);
      break;
    case SXPATH_RESULT_FIRST_NODE:
      break;
    case SXPATH_RESULT_NODE_SET:
      break;
    }
  return xpr;
}

/**
   Translates @a x1 to an SxPathExpression, creates an SxPathResult of
   type SXPATH_RESULT_FIRST_NODE, including this expression.

   @param x1
   @return a newly created SxPathResult containing the SxPathExpression @a x1
   @warning The returned SxPathResult needs to be freed.
 */
SxPathResult *SxPathFirstCompileOnSxml (SxmlNode *x1)	/* SxmlElement	*/
{
  SxPathResult *xpr;
  SxmlNode     *progNode;	/* SxPathResultExpression ? */

  if(!SxmlProcessorImplicit)SxmlProcessorImplicitCreate();
  xpr=SxPathResultCreate(SxmlProcessorImplicit, SXPATH_RESULT_FIRST_NODE);

  progNode=SxPathExpressionTranslate(SxmlClone(x1));
  SxmlAppendChild(xpr,progNode);

  SxmlSetNodeName(progNode, "#expression");
  return xpr;
}


/**
   Translates the string @a str to an SxPathExpression.
   
   Creates and returns an SxPathResult of type SXPATH_RESULT_FIRST_NODE
   including this expression.

   @param str an SxPath string
   @returns the SxPathResult created (which needs to be freed)
   @warning Do not forget to free the result
 */
SxPathResult *SxPathFirstCompile(char *str)
{
  SxmlNode         *progNode;	/* pointer to SxPathResultExpression 	*/
  SxPathResult     *xpr;

  progNode=SxPathExpressionCreate(str); 

  if(!SxmlProcessorImplicit)SxmlProcessorImplicitCreate();
  xpr=SxPathResultCreate(SxmlProcessorImplicit, SXPATH_RESULT_FIRST_NODE);
  SxmlAppendChild(xpr,progNode);
  SxmlSetNodeName(progNode, "#expression");
  return xpr;
}


/**
   Translates the string @a str to an SxPathExpression.  

   Creates and returns an SxPathResult of type
   SXPATH_RESULT_NODE_SET including this expression.

   @param str an SxPath string
   @returns the SxPathResult created (which needs to be freed)
   @warning Do not forget to free the result
 */
SxPathResult *SxPathSetCompile(char *str)
{
  SxmlNode         *progNode;	/* SxPathResultExpression	*/
  SxPathResult     *xpr;

  if(!SxmlProcessorImplicit)SxmlProcessorImplicitCreate();

  progNode=SxPathExpressionCreate(str);

  xpr=SxPathResultCreate
    (SxmlProcessorImplicit,SXPATH_RESULT_NODE_SET);
  /*
  SxmlAppendChild(xpr,progNode=SxmlNodePointerCreate(xExpr));
  SxmlSetNodeValueToFree(progNode); */

  SxmlAppendChild(xpr,progNode);
  SxmlSetNodeName(progNode, "#expression");
  return xpr;
}


/**
   Does the same thing than SxPathSetCompile.

   @param str an SxPath string
   @see SxPathSetCompile
 */
/*
SxPathResult *SxPathSetCompileForRecord(char *str)
{
  SxPathExpression *xExpr;
  SxmlNode         *progNode;
  SxPathResult     *xpr;
  
  xExpr=SxPathExpressionCreate(str);
  if(!SxmlProcessorImplicit)SxmlProcessorImplicitCreate();
  xpr=SxPathResultCreate
    (SxmlProcessorImplicit,SXPATH_RESULT_NODE_SET);
  SxmlAppendChild(xpr,progNode=SxmlNodePointerCreate(xExpr));
  SxmlSetNodeValueToFree(progNode);
  SxmlSetNodeName(progNode, "#expression");
  return xpr;
}
*/

/**
   Translates the SxmlNode @a x1 into an SxPathExpression.
  
   Creates and returns an SxPathResult of type
   SXPATH_RESULT_NODE_SET including this expression (@a x1).

   @param x1
   @returns the SxPathResult created (which needs to be freed)
   @warning Do not forget to free the result
 */
SxPathResult *SxPathSetCompileOnSxml(SxmlNode *x1)
{
  SxPathResult *xpr;
  SxmlNode     *progNode;	/* SxPathResultExpression ? */

  /*  SxPathExpressionTranslate(x1); */
  progNode=SxPathExpressionTranslate(SxmlClone(x1));

  if(!SxmlProcessorImplicit)SxmlProcessorImplicitCreate();
  xpr=SxPathResultCreate
    (SxmlProcessorImplicit,SXPATH_RESULT_NODE_SET);

  /*
  SxmlAppendChild(xpr,progNode=SxmlNodePointerCreate(x1));
  SxmlSetNodeValueToFree(progNode);
  */

  SxmlAppendChild(xpr,progNode);
  SxmlSetNodeName(progNode, "#expression");
  return xpr;
}

/**
   Creates a list of nodes matching @a xr1 in @a x1

   @param xr1 SxPath compiled
   @param x1  document on which the xpath is applied
   @return SxmlNodeList of @a SxmlNode s matching @a xr1.
 */
SxmlNode *SxPathSetResultListCreate(
     SxPathResult *xr1,
     SxmlNode     *x1)
{
  SxmlNode *x2;
  /*
  x2=SxPathSetEval
    (xr1,(SxmlNode *)SxmlNodeValue
     (SxmlGetFirstChildByTagName(xr1,"#expression")),x1);
  */
  x2=SxPathSetEval
    (xr1, SxmlGetFirstChildByTagName(xr1,"#expression"),x1);
  return x2;
  
}

/**
   Apply the search of the first node in @a x1 which satisfies the
   SxPath contained in @a xr1.

   @param xr1 SxPathResult built with @ref SxPathFirstCompile
   @param x1  document on which to apply the SxPath contained in @a xr1

   @return the first node in @a x1 matching @a xr1
 */
SxmlNode *SxPathFirstResultNode(SxPathResult *xr1,
			      SxmlNode     *x1)
{
  SxmlNode *x2;
  /*
  x2=SxPathFirstEval
    (xr1,(SxmlNode *)SxmlNodeValue
     (SxmlGetFirstChildByTagName(xr1,"#expression")),x1);
  */
  x2=SxPathEvalFirst
    (SxmlGetFirstChildByTagName(xr1,"#expression"),x1);
  return x2; 
}

/**
   Returns the ContextNode of the top of @a xr1's stack.

   @param xr1 
   @returns SxmlNode which is the context of the @a xr1's stack top.
 */
SxmlNode *SxPathResultNode(SxPathResult *xr1)
{
  SxmlNode *top;
  if(!(top=SxmlLastChild(SxPathResultStack(xr1))))return NULL;  
  if ( SxPathExpressionIsAxis(top))
    return SxPathResultStepContextNode(top);
  return NULL;
}

/**
   Returns the NextNode of @a xr1's stack top.
   If it is not null, evaluates the SxPathResultStep (@a top) on the result.

   @param xr1
   @returns the NextNode of @a xr1's stack top.
 */
SxmlNode *SxPathResultNextNode(SxmlNode *xr1)
{
  SxmlNode *x2;
  SxmlNode *top;		/* SxPathResultStep	*/
  SxmlNode *progNode;	/* SxPathExpression ?	*/

  if(!(top=SxmlLastChild(SxPathResultStack(xr1))))return NULL;
  x2=SxmlNextNodeProperty(top);
  if(!x2)return NULL;
  progNode=SxPathResultStepExpressionNode(top);
  SxPathResultStepEvaluate(xr1, top, x2);
  return x2;
  /*
  stepType=SxmlNodeSubType(progNode);
  if ( SxPathExpressionIsAxis(progNode))
    {
       switch(stepType)
	 {
	 case SXPATH_AXIS_CHILD_NAME:
	   x3=SxPathChildNameNextNode(top);
	   return x3;
	 }
    }
  */
}

/**
   @param proc1 
   @param x1 (SxPathExpression? or SxmlNode which first child is an
   SxPathExpression?)
   @returns
   - NULL when the @a mode attribute of @a x1 does not equal @e dilib
   - the SxmlNode result of the evaluation of ... ?
   @todo finish the documentation
   @internal Call to SxPathResultStepCreate lacked one parameter!
 */
SxmlNode *SxPathProcFirst(SxmlProcessor *proc1,
			SxmlNode      *x1)
{
  SxmlNode *xpr;		/* SxPathResult */
  SxmlNode *progNode;
  SxmlNode *res1;
  
  /*       This was written as is. There lacked one parameter, which
	   should be an SxmlProcessor. */

  xpr=SxPathResultCreate(proc1,SXPATH_RESULT_FIRST_NODE);
  /*
  SxmlAppendChild
    (xpr,progNode=SxmlNodePointerCreate(SxmlFirstChild(x1)));
  */
  progNode=SxmlFirstChild(x1);
  if (!progNode)return NULL;
  SxmlAppendChild(xpr,progNode);
  SxmlSetNodeName(progNode, "#expression");

  /*
  res1=SxPathFirstEval
    (xpr,SxmlFirstChild(x1),
     SxmlProcessorEvalToNode(proc1,SxmlLastChild(x1)));
  */
  res1=SxPathEvalFirst
    (SxmlFirstChild(x1),
     SxmlProcessorEvalToNode(proc1,SxmlLastChild(x1)));

  return res1;
}

/**
   @param mem1
   @param x1
   @internal Not yet finished?
 */
SxmlNode *SxPathProcSet(SxmlProcessor *proc1,
		      SxmlNode *x1)
{
  SxmlNode *xpr;		/* SxPathResult */
  SxmlNode *progNode;
  SxmlNode *res1;
  xpr=SxPathResultCreate(proc1,SXPATH_RESULT_NODE_SET);
  SxmlAppendChild
    (xpr,progNode=SxmlNodePointerCreate(SxmlFirstChild(x1)));
  SxmlSetNodeName(progNode, "#expression");
  res1=SxPathSetEval    (xpr,SxmlFirstChild(x1),
     SxmlProcessorEvalToNode(proc1,SxmlLastChild(x1)));
  return res1;
}
    
/**
   Initializes @a mem1. Adds the @ref SxPathProcFirst function,
   associated to the name @c xpath:first into @a mem1's symbol table.

   @param mem1
   @returns mem1
 */
SxmlMemory *SxPathMemoryInit(SxmlMemory *mem1)
{
  SxmlMemorySetXmlBuiltin(mem1,"xpath:first",SxPathProcFirst);
  SxmlMemorySetXmlBuiltin(mem1,"xpath:set",SxPathProcSet);
  return mem1;
}


