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

/*********************************************************************
*
*     Module  : SxPath
*     Fichier : SxmlSelect.c
*     Auteur  : J. Ducloy
*     Origine : J. Ducloy
*
**********************************************************************
* 
*     Copyleft
*
**********************************************************************/
#include "SxmlSelect.h"
#include <stdlib.h>
#include <unistd.h> /* Useful when getopt is not in stdlib.h */


/**
   @file

   @brief SxmlSelect command

   Selects and splits Sxml records.

   @section SxmlSelectOPTIONS OPTIONS

   \li @c -g @c xpath...	grep + result	-> @c \@g[1-9]
   \li @c -s @c xpath...	select/split	-> @c \@s[1-9] +
   \li @c -S @c xpath...	select/split result is a set @c \@S[1-9]
   \li @c -c			combination		-> @c \@s[1-9] + @c \@A[1-9]
   \li @c -n @c value		value for following g
   \li @c -p @c format		on each s/a or once by record with tab and 
				new line for the last
   \li @c -P @c format		id but without tab or nl
   \li @c -r @c begin @c record (after grep)
   \li @c -R @c end @c record (once, after p/x/f)
   \li @c -b @c before @c file
   \li @c -e @c at @c the @c end @c of @c file
   \li @c -t @c tracelevel debug trace level (should not be used)

   @author &copy; INIST-CNRS
   @author J. DUCLOY <DilibMaster@inist.fr>
   
   @see @ref xpath "SxPath Syntax"
 */
/*
OPTIONS
  -g xpath...   grep + result -> @g[1-9] 
  -s xpath...   select/split  -> @s[1-9] + 
  -S xpath...   select/split result is a set @S[1-9]
  -c            combination        -> @s[1-9] + @A[1-9]
  -n value      null value for following g (s)
  -p format     on each s/a or once by record with tab and new line for
		the last
  -P format     id but without tab or nl
  -r begin record (after grep)
  -R end record (once, after p/x/f)
  -b before file
  -e at the end of file
  
*/

/*
SxmlProcessor functions:
  xmlSelectGrep -> xmlSelect:grep
  SxmlSelectScriptSplit -> xmlSelect:split
 */
/**
   @param argc Number of parameters
   @param argv Parameter strings

   @todo Add the -d, -l, -v, -E options.
   @anchor xmlSelect-before
   @anchor xmlSelect-end
   @warning \@gn are limited from \@g0 to \@g9
 */

  SxmlProcessor *SxmlSelectProcessor;

  int            SxmlSelectOptionNoInput;  /* 0 input ; 1 no input */
  int            SxmlSelectOptionEcho;

  int            SxmlSelectNumSplit;
  char           **SxmlSelectSplitVarName;  /*   ex SxmlSelectSplitVarName[0] = #s1 for @s1 */
  char           **SxmlSelectSplitSetName;  /*   ex #S1 */
  char           **SxmlSelectCombiName;     /*   ex #c1 for @c1 */ 
  char           *SxmlSelectCombiType;      /* C or c */
  char           *SxmlSelectCombiTypeOn;
  int            *SxmlSelectCombiRankInType;
  SxmlNode       **SxmlSelectSplitCurrentPtrInSet;      /*  toward current node in SxmlList */
  SxmlNode       **SxmlSelectCombiPtr;      /*  toward current node in SxmlList */
  int            SxmlSelectNumCombinations;

  SxmlNode *SxmlSelectLastSplitCombi;
  int SxmlSelectLastSplitCombiType;  /* \0 s or a */

  SxmlNode      *SxmlSelectScriptBefore;
  SxmlNode      *SxmlSelectScriptGrep;	  /* grep function */
  SxmlNode      *SxmlSelectScriptSplit;   /* split function */
  SxmlNode      *SxmlSelectScriptBeginRecord;
  SxmlNode      *SxmlSelectScriptEndRecord;

  SxmlNode      *SxmlSelectNullValue;
  int            SxmlSelectNullValueFrom;

  char     *SxmlSelectGrepVar;
  char     *SxmlSelectSplitVar;
  char     *SxmlSelectSplitVarSet;

  SxmlNode      *SxmlSelectSteps;

void usage()
{
  fprintf (stderr,"usage: SxmlSelect [-s path] [-g path] [-p printSpec] [-P]\n");
  exit(EXIT_FAILURE);
}

void SxmlSelectInit()
{
  int i;
  SxmlSelectSplitVarName=malloc(11*sizeof(char *));
  SxmlSelectSplitSetName=malloc(11*sizeof(char *));
  SxmlSelectCombiName=malloc(11*sizeof(char *));
  SxmlSelectSplitCurrentPtrInSet=(SxmlNode **)malloc(11*sizeof(SxmlNode *));
  SxmlSelectCombiTypeOn=malloc(11);
  SxmlSelectCombiType=malloc(11);
  SxmlSelectCombiRankInType=(int *)malloc(11*sizeof(int));
  i=0;
  while (i<10)
    {
      char *varSplit;
      char *varSet;
      char *varCombi;

      varSplit=malloc(4);
      varSet=malloc(4);
      varCombi=malloc(4);
      strcpy(varSplit, "#s0");
      strcpy(varSet, "#S0");
      strcpy(varCombi, "#c0");
      varSplit[2]+=(i+1);
      varSet[2]+=(i+1);
      varCombi[2]+=(i+1);
      SxmlSelectSplitVarName[i]=varSplit;
      SxmlSelectSplitSetName[i]=varSet;
      SxmlSelectCombiName[i]=varCombi;
      i++;
    }
}

int SxmlSelectProceedCombi(int i) /* returns 0 if no more combination could be expected */
{
  if(i>SxmlSelectNumCombinations-1)return 0;
  if (SxmlSelectCombiTypeOn[i]=='s')
    {
      SxmlNode *ptrPreviousInSet;
      SxmlNode *ptrCombi;
      SxmlNode *elemCombi;
      SxmlNode *elemPrevious;
      ptrPreviousInSet=SxmlSelectSplitCurrentPtrInSet[SxmlSelectCombiRankInType[i]];
      ptrCombi=SxmlNextSibling(ptrPreviousInSet);
      if (!ptrCombi)return 0;
      while (ptrCombi)
	{
	  elemCombi=(SxmlNode *)SxmlNodeValue(ptrCombi);
	  if (SxmlSelectCombiType[i]=='c')
	    {
	      SxmlMemorySetVar(SxmlSelectMemory,SxmlSelectCombiName[i], elemCombi);
	      SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectSteps,NULL);
	    }
	  else
	    {
	      elemPrevious=(SxmlNode *)SxmlNodeValue(ptrPreviousInSet);
	      if (SxmlTextCmp(elemPrevious, elemCombi)<=0)
		{
		  SxmlMemorySetVar(SxmlSelectMemory,SxmlSelectCombiName[i], elemCombi);
		  SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectSteps,NULL);
		}
	      else
		{
		  char *varSplit;
		  SxmlMemorySetVar(SxmlSelectMemory,SxmlSelectCombiName[i], elemPrevious);
		  varSplit=SxmlSelectSplitVarName[SxmlSelectCombiRankInType[i]];
		  SxmlMemorySetVar(SxmlSelectMemory,varSplit, elemCombi);
		  SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectSteps,NULL);
		  SxmlMemorySetVar(SxmlSelectMemory,varSplit, elemPrevious);
		}
	    }
	  ptrCombi=SxmlNextSibling(ptrCombi);
	}
      return 1;
    }
  else
    {
      fprintf(stderr, "**** not yet implemented***** \n");
      exit (EXIT_FAILURE);
    }
  return 0;
}

void SxmlSelectProceedSplit(int i)
{
  /* char *varSet;
  char *varSplit; */
  SxmlNode *resSet;
  SxmlNode *elemSplit;
  SxmlNode *elemSplitPtr;

  /* varSet=malloc(4);
   varSplit=malloc(4); 
  strcpy(varSet, "#S0");
  strcpy(varSplit, "#s0"); 
  varSet[2]+=(i+1);
   varSplit[2]+=(i+1); */

  /* resSet=SxmlMemoryGetVar(SxmlSelectMemory, varSet); */
  resSet=SxmlMemoryGetVar(SxmlSelectMemory, SxmlSelectSplitSetName[i]);
  elemSplitPtr=SxmlFirstChild(resSet); 

  while (elemSplitPtr)
    {
      elemSplit=(SxmlNode *)SxmlNodeValue(elemSplitPtr);
      SxmlMemorySetVar(SxmlSelectMemory,SxmlSelectSplitVarName[i], elemSplit);
      SxmlSelectSplitCurrentPtrInSet[i]=elemSplitPtr;
      /* SxmlMemorySetVar(SxmlSelectMemory,varSplit, elemSplit); */
      if ((i+1)==SxmlSelectNumSplit)
	{  
	  if (SxmlSelectNumCombinations>0) 
	    {
	      int retCode;
	      retCode=SxmlSelectProceedCombi(0);
	      if (retCode==0)return;
	    }
	  else SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectSteps,NULL);
	}
      else
	{
	  SxmlSelectProceedSplit(i+1);
	}
      elemSplitPtr=SxmlNextSibling(elemSplitPtr);
    }
}

void SxmlSelectProceedInput()
{
  SxmlNode *inputRecord;
  if (SxmlSelectOptionNoInput)return;
  while ((inputRecord=SxmlStdinNextRecord()))
    {
      SxmlNode *inputRecordItem;
      char     *nameVarInputRecordItem;
      int       numGrep;
      int charNumGrep;
      nameVarInputRecordItem=strdup("#0");
      numGrep=0;
      charNumGrep='0';
      SxmlReset(inputRecord);
      while ((inputRecordItem=SxmlNextNode(inputRecord)))
	{
	  nameVarInputRecordItem[1]++;
	  SxmlMemorySetVar(SxmlSelectMemory, nameVarInputRecordItem , inputRecordItem);
	}
      free(nameVarInputRecordItem);

      if( DilibGetTraceLevel() >1)
	{
	  printf("----- New record ----------\n");
	  SxmlPrint(SxmlStdinRecord);
	}
     if (SxmlSelectScriptBeginRecord)
	{
	  SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectScriptBeginRecord,NULL);
	}
      if (SxmlSelectScriptGrep)
	{
	  SxmlNode *resGrep;
	  numGrep++;
	  charNumGrep++;
	  resGrep=SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectScriptGrep,NULL);
	  /* if(!(SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectScriptGrep,NULL)))
	     continue; */
	  if (!resGrep)
	    {
	      static char *localVarGrep=NULL;
	      if(!localVarGrep) localVarGrep=strdup("#g1");
	      if ((SxmlSelectNullValue&&(SxmlSelectNullValueFrom>=numGrep)))
		{
		  localVarGrep[2]=charNumGrep;
		  SxmlMemorySetVar(SxmlSelectMemory,localVarGrep, SxmlSelectNullValue);
		}
	      else continue;
	    }
	}
      if (SxmlSelectScriptSplit)
	{
	  if(!(SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectScriptSplit,NULL)))
	    continue;
	}
      if (SxmlSelectSteps)
	{
	  if(SxmlSelectScriptSplit)
	    {
	      SxmlSelectProceedSplit(0);
	    }
	  else
	    {
	      SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectSteps,NULL); 
	    }
	}
      if (SxmlSelectScriptEndRecord)
	{
	  SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectScriptEndRecord,NULL);
	}
      if (SxmlSelectOptionEcho)SxmlPrint(SxmlStdinRecord);
      if( DilibGetTraceLevel() >1)printf("------ dump after record ----\n");
      if( DilibGetTraceLevel() >1)SxmlDump(SxmlSelectMemory);
    }
}

int main(int argc, char **argv)
{
  int cod_arg;
  int waitingSep;

  SxmlMemory    *memorySxmlSelect		= NULL;
  SxmlNode *styleSheet			= NULL;
  SxmlNode *xmlSelectEnd			= NULL;
  SxmlNode *SxmlSelectScriptGrepInsert		= NULL;
  SxmlNode *SxmlSelectScriptSplitInsert		= NULL;
  SxmlNode *insertNode1			= NULL;
  SxmlNode *insertNode2			= NULL;
  SxmlNode *paramExpr			= NULL;

  SxmlSelectProcessor = SxmlSelectProcCreate();
  memorySxmlSelect    = SxmlProcessorMemory(SxmlSelectProcessor);
  waitingSep         = 0;
  SxmlSelectOptionNoInput               = 0;
  SxmlSelectScriptBefore		= NULL;
  SxmlSelectScriptGrep	        	= NULL;
  SxmlSelectScriptSplit  		= NULL;
  SxmlSelectScriptBeginRecord		= NULL;
  SxmlSelectScriptEndRecord	        = NULL;
  SxmlSelectSteps		        = NULL;
  SxmlSelectNullValue                   = NULL;
  SxmlSelectNullValueFrom                = 100;    /* > 10 !!! */
  styleSheet         = SxmlMemoryScripts(memorySxmlSelect);
  SxmlSelectOptionEcho           = 1;
  SxmlSelectGrepVar   = strdup("#g0");
  SxmlSelectSplitVarSet  = strdup("#S0"); 
  SxmlSelectSplitVar  = strdup("#s0"); 
  SxmlSelectNumSplit=0;
  SxmlSelectNumCombinations=0;
  SxmlSelectLastSplitCombiType=0;
  SxmlSelectInit();

  while ((cod_arg = getopt(argc,argv,"b:cCg:Nn:p:r:R:t:de:ls:v:EP"))!=EOF)
    {
      char *nextOptArg;
      int multipleArg;

      multipleArg=0;
      if (optind<=argc)
	{
	  nextOptArg=argv[optind];
	  if (nextOptArg && (nextOptArg[0]!='-')) multipleArg=1;
	}

      switch(cod_arg)
	{ 
	case 'b':
	  SxmlSelectScriptBefore =
	    SxmlSelectMemoryAddScript
	    (SxmlSelectMemory,"xmlSelect:before",optarg);
	  break;

	case 'c':
	case 'C':
	  if (SxmlSelectNumSplit<1)
	    {
	      fprintf(stderr, "**** SxmlSelect error : At least one option -s is mandatory before -c or -C ****\n"); 
	      exit(EXIT_FAILURE);
	    }
	  if (SxmlSelectLastSplitCombiType=='c')
	    {
	      SxmlSelectCombiTypeOn[SxmlSelectNumCombinations]='c';
	      SxmlSelectCombiRankInType[SxmlSelectNumCombinations]=SxmlSelectNumCombinations-1;
	    }
	  else
	    {
	      SxmlSelectCombiTypeOn[SxmlSelectNumCombinations]='s';
	      SxmlSelectCombiRankInType[SxmlSelectNumCombinations]=SxmlSelectNumSplit-1;
	    }
	  SxmlSelectCombiType[SxmlSelectNumCombinations]=cod_arg;
	  SxmlSelectNumCombinations++;
	  SxmlSelectLastSplitCombiType=cod_arg;
	  break;
	  
	case 'e':
	  xmlSelectEnd =
	    SxmlSelectMemoryAddScript
	    (memorySxmlSelect,"xmlSelect:end",optarg);
	    break;
	    
	case 'g':
	  if ( DilibGetTraceLevel() >1)
	    {
	      printf("gOption= %s\n", optarg);
	    }
	  if(!SxmlSelectScriptGrep)
	    {
	      SxmlSelectScriptGrep =
		SxmlMemoryFunctionCreate(memorySxmlSelect,
					"xmlSelect:grep",NULL,NULL);
	      SxmlMemoryFunctionAppendBody
		(SxmlSelectScriptGrep,
		 SxmlSelectScriptGrepInsert = SxmlElementCreate("dom:and"));
	    }
	  SxmlAppendChild
	    (SxmlSelectScriptGrepInsert,
	     insertNode1 = SxmlElementCreate("dom:setVar"));
	  SxmlSelectGrepVar[2]++;	/* warning:  no more than 9 */
	  SxmlSetAttribute(insertNode1,"name",SxmlSelectGrepVar);
	  if (multipleArg==0)
	    {
	      paramExpr = SxPathExpressionCreate(optarg);
	      SxmlAddFirstChild
		(paramExpr,
		 SxPathExpressionTranslate(SxmlElementCreate("child")));
	      SxmlAppendChild
		(insertNode1,
		 insertNode2 = SxmlElementCreate("xpath:first"));
	      /* SxmlSetAttribute(insertNode2,"mode","dilib"); */
	      SxmlAppendChild(insertNode2,paramExpr);
	    }
	  else
	    {
	      SxmlNode *unionNode;
	      char *nextParam;
	      unionNode=SxPathExpressionTranslate(SxmlElementCreate("union"));
	      SxmlAppendChild
		(insertNode1,
		 insertNode2 = SxmlElementCreate("xpath:first"));
	      SxmlAppendChild(insertNode2, unionNode);

	      nextParam= optarg;
	      while (nextParam)
		{
		  paramExpr = SxPathExpressionCreate(nextParam);
		  SxmlAddFirstChild
		    (paramExpr,
		     SxPathExpressionTranslate(SxmlElementCreate("child")));
		  SxmlAppendChild (unionNode,paramExpr);
		  nextParam=NULL;
		  if (optind<=argc)
		    {
		      nextOptArg=argv[optind];
		      if (nextOptArg[0]!='-')
			{
			  optind++;
			  nextParam=nextOptArg;
			}
		    }
		}
	      SxmlAppendChild(insertNode2, unionNode);
	    }

	  SxmlAppendChild(insertNode2,SxmlElementCreate("dom:inputRecord"));
	  break;

	case 'N':
	  SxmlSelectOptionNoInput=1;
	  break;

	case 'n':
	  SxmlSelectNullValue=SxmlTextCreate(optarg);
	  SxmlSelectNullValueFrom=SxmlSelectGrepVar[2];
	  break;

	case 'p':
	  SxmlSelectOptionEcho = 0;
	  if(waitingSep)
	    SxmlSelectMemoryAddPrint(memorySxmlSelect,"\t");
	  SxmlSelectSteps = SxmlSelectMemoryAddPrint(memorySxmlSelect,optarg);
	  waitingSep=1;
	  break;

	case 'R':
	  SxmlSelectOptionEcho = 0;
	  if(!SxmlSelectScriptEndRecord)
	    SxmlSelectScriptEndRecord =
	      SxmlMemoryFunctionCreate(memorySxmlSelect,
				      "xmlSelect:endRecord",NULL,NULL);
	  SxmlMemoryFunctionAppendBody
	    (SxmlSelectScriptEndRecord, SxmlFromString(optarg));
	    break;

	case 'r':
	  SxmlSelectOptionEcho = 0;
	  if(!SxmlSelectScriptBeginRecord)
	    SxmlSelectScriptBeginRecord =
	      SxmlMemoryFunctionCreate(memorySxmlSelect,
				      "xmlSelect:beginRecord",NULL,NULL);
	  SxmlMemoryFunctionAppendBody
	    (SxmlSelectScriptBeginRecord, SxmlFromString(optarg));
	    break;

	case 's':
	  SxmlSelectNumSplit++;
	  if(!SxmlSelectScriptSplit)
	    {
	      SxmlSelectScriptSplit =
		SxmlMemoryFunctionCreate(memorySxmlSelect,
					 "xmlSelect:split",NULL,NULL);
	      SxmlMemoryFunctionAppendBody
		(SxmlSelectScriptSplit,
		 SxmlSelectScriptSplitInsert = SxmlElementCreate("dom:and"));
	    }
	  SxmlAppendChild
	    (SxmlSelectScriptSplitInsert,
	     insertNode1 = SxmlElementCreate("dom:setVar"));
	  SxmlSelectSplitVarSet[2]++;
	  SxmlSetAttribute(insertNode1,"name",SxmlSelectSplitVarSet);
	  SxmlAppendChild
	    (insertNode1,
	     insertNode2 = SxmlElementCreate("xpath:set"));
	  
	  if (multipleArg==0)
	    {
	      paramExpr = SxPathExpressionCreate(optarg);
	      SxmlAddFirstChild
		(paramExpr,
		 SxPathExpressionTranslate(SxmlElementCreate("child")));
	      SxmlAppendChild(insertNode2,paramExpr);
	    }
	  else
	    {
	      SxmlNode *unionNode;
	      char *nextParam;
	      unionNode=SxPathExpressionTranslate(SxmlElementCreate("union"));
	      nextParam= optarg;
	      while (nextParam)
		{
		  paramExpr = SxPathExpressionCreate(nextParam);
		  SxmlAddFirstChild
		    (paramExpr,
		     SxPathExpressionTranslate(SxmlElementCreate("child")));
		  SxmlAppendChild(unionNode,paramExpr);
		  nextParam=NULL;
		  if (optind<=argc)
		    {
		      nextOptArg=argv[optind];
		      if (nextOptArg[0]!='-')
			{
			  optind++;
			  nextParam=nextOptArg;
			}
		    }
		}
	      SxmlAppendChild(insertNode2, unionNode);
	    }

	  SxmlAppendChild(insertNode2,SxmlElementCreate("dom:inputRecord"));
	  SxmlSelectLastSplitCombiType='s';
	  break;

	case 't':
	  DilibSetTraceLevel(atoi(optarg));
	  break;

	case '?':
	default:
	  usage();
	  break;

	}
    }
  if(waitingSep)
    SxmlSelectMemoryAddPrint(memorySxmlSelect,"\n");


  if( DilibGetTraceLevel() >1)printf("------ begin ----\n");
  if( DilibGetTraceLevel() >1)SxmlDump(memorySxmlSelect);
  if (SxmlSelectScriptBefore)
    {
      SxmlProcessorEvalFunction(SxmlSelectProcessor,SxmlSelectScriptBefore,NULL);
    }
  if( DilibGetTraceLevel() >1)printf("------ end before----\n");
  SxmlSelectProceedInput();
  if (xmlSelectEnd)
    {
      SxmlProcessorEvalFunction(SxmlSelectProcessor,xmlSelectEnd,NULL);
    }
  exit(EXIT_SUCCESS);
}

