/*   -*- coding: utf-8 -*-  */
/******************************************************************************
*
* Module    : Hfd
* Fichier   : HfdRecord.c
* Auteur    : Ducloy
*
****************************************************************************
*
* 
*				    
*
*     HfdRecord : Structure intermédiaire pour l'input de données SGML
*
****************************************************************************
*  Notes de programmation les positions sont repérées par offset
*  (la localisation du Buffer ne sont pas stabilisée avant la fin
*  de la lecture d'un record)
****************************************************************************/
#include "Hfd.h"
#include "RegExp.h"
#include "Except.h"

#include "SxmlNode.h"

static RegExp *RE_doctype;
static RegExp *RE_SgmlId;
static int    firstCall=1;
static Buffer *internalBuffer;

void HfdRecordInitModule()
{
  firstCall=0;
  RE_doctype=RegExpCreate("[ ]*[Dd][Oo][Cc][Tt][Yy][Pp][Ee]");
  RE_SgmlId=RegExpCreate("^[^ =/><\t][^ =/><\t]*");
  internalBuffer=BufferCreate(200,200);
}

HfdRecordField *HfdRecordFieldCreate()
{
  HfdRecordField *field;
  if (firstCall)HfdRecordInitModule();
  field = (HfdRecordField *)malloc (sizeof(HfdRecordField));
  field->next=NULL;
  field->type='u';
  field->beginField=NULL;
  return field;
}

HfdRecordField *HfdRecordFieldGet(rec)
     HfdRecord *rec;
{
  HfdRecordField *field;
  if (rec->firstFreeList)
    { 
      field=rec->firstFreeList;
      rec->firstFreeList=field->next;
      field->next=NULL;
    }
  else
    {
      field=HfdRecordFieldCreate();
    }
  if (rec->first)
    {
      rec->last->next=field;
      rec->last=field;
    }
  else
    {
      rec->first=field;
      rec->last=field;
    }
  field->offsetBeginField=rec->offset;
  field->offsetEndField=rec->offset;
  field->offsetEffectiveEndField=rec->offset;
  field->offsetEffectiveBeginField=rec->offset;
  return field;
}

HfdRecord *HfdRecordCreate(int s, int i)
{
  HfdRecord *record;
  record = (HfdRecord *)malloc (sizeof(HfdRecord));
  record->bufferInput=BufferCreate(s,i);
  record->rootNode=NULL;

  record->purged=0;
  record->key        =BufferCreate(100,100);
  record->content    =BufferCreate(100,100);
  record->tag        =NULL;
  record->recordNode=NULL;
  record->first=NULL;
  record->last=NULL;
  record->firstFreeList=NULL;

  return record;
}

HfdRecord *NewHfdRecord()
{
    HfdRecord *record;
    record=HfdRecordCreate(100, 100);
    return record;
}

HfdRecord *HfdRecordPurge(HfdRecord *rec)
{
  HfdRecordField *field;
  if (rec->purged)
    {
      if (rec->recordNode)SxmlFree(rec->recordNode);
      if ((field=rec->first))
	{
	  rec->firstFreeList=field;
	  rec->first=NULL;
          rec->last=NULL;
	}
      rec->recordNode=NULL;
      BufferReset(rec->key);
      rec->purged=0;
    }
  return rec;
}

void HfdRecordFree(HfdRecord *rec)
{
  HfdRecordField *f1;
  HfdRecordField *f2;
  if(rec)
    {
      HfdRecordPurge(rec);
      if (rec->key) BufferFree(rec->key);
      if (rec->content) BufferFree(rec->content);
      if (rec->bufferInput) BufferFree(rec->bufferInput);
      if ((f1=rec->firstFreeList))
	{
	  do
	    {
	      f2=f1->next;
	      free (f1);
	    } while(f2);
	}
      free (rec);
    }
}

HfdRecord *HfdRecordInit(HfdRecord *rec)
{
  HfdRecordPurge(rec);
  rec->offset=0;
  rec->purged=1;
  return rec;
}

HfdRecord *HfdRecordGetDataNULL(HfdRecord *rec, FILE *f1);
HfdRecord *HfdRecordGetData(HfdRecord *rec, FILE *f1);

HfdRecord *HfdRecordNewFieldFromTab(rec,f1)
     HfdRecord *rec;
     FILE      *f1;
{
   rec->offset++;

   switch(rec->stringBuf[rec->offset])
     {
     case '\0':
       HfdRecordGetDataNULL(rec,f1);
       break;
     case '<':
       HfdRecordGetSxml(rec, f1);
       if (rec->stringBuf[rec->offset]=='\t')HfdRecordNewFieldFromTab(rec,f1);
       break;
     case ' ':
       break;
     case '\t':
       HfdRecordGetDataNULL(rec,f1);
       HfdRecordNewFieldFromTab(rec,f1);
       break;
     default:
       HfdRecordGetData(rec,f1);
       if (rec->stringBuf[rec->offset]=='\t')HfdRecordNewFieldFromTab(rec,f1);
       break;
     }
   return rec;
}

HfdRecord *HfdRecordGetDataNULL(HfdRecord *rec, FILE *f1)
{
  HfdRecordField *field;
  field= HfdRecordFieldGet(rec);
    field->type='D';
  return rec;
}

HfdRecord *HfdRecordGetData(rec, f1)
     HfdRecord *rec;
     FILE      *f1;
{
  HfdRecordField *field;
  char *posTab;
  field= HfdRecordFieldGet(rec);
  if((posTab=strchr(BufferString(rec->bufferInput)+rec->offset, '\t')))
    {
      field->offsetEndField=posTab-BufferString(rec->bufferInput);
    }
  else
    {
      field->offsetEndField=BufferLen(rec->bufferInput);
    }
  field->offsetEffectiveEndField=field->offsetEndField;
  field->type='D';
  rec->offset=field->offsetEffectiveEndField;
  return rec;
}


HfdRecord *HfdRecordFgets( HfdRecord *rec, FILE *f1)
{
  HfdRecordField *field;

  HfdRecordInit(rec);
  if ((rec->stringBuf=BufferFgets(rec->bufferInput,f1)))
    { 
     switch(rec->stringBuf[rec->offset])
       {
	case '\0':

	  break;

	case '<':
	  HfdRecordGetSxml(rec, f1);
	  if (rec->stringBuf[rec->offset]=='\t')HfdRecordNewFieldFromTab(rec,f1);
	  break;
	  /*
	case ' ':
           break;
	   */

	case '\t':

	  break;
	default:
	  HfdRecordGetData(rec,f1);
          if (rec->stringBuf[rec->offset]=='\t')HfdRecordNewFieldFromTab(rec,f1);
	  break;
	}

     if ((field=rec->first))
       {
	 do
	   {
	     field->beginField=
	       BufferString(rec->bufferInput)+field->offsetBeginField;
	     field->endField  =
	       BufferString(rec->bufferInput)+field->offsetEndField;
	     field->effectiveBeginField=
	       BufferString(rec->bufferInput)+field->offsetEffectiveBeginField;
	     field->effectiveEndField=
	       BufferString(rec->bufferInput)+field->offsetEffectiveEndField;
	   }
	 while((field=field->next));

	 field=rec->first;
	 if (field->next && (field->type=='D'))
	     {
	       BufferStrncat
		 (rec->key,
		  field->effectiveBeginField,
                  field->effectiveEndField-field->effectiveBeginField);
	     }

       }
     
     return rec;

    }
  else return NULL;
}

HfdRecord *HfdRecordGetUntil(rec, f1, s1)
     HfdRecord *rec;
     FILE      *f1;
     char *s1;
{
  char *posStr;
  if (BufferGets(internalBuffer))
    {
      BufferStrcat(rec->bufferInput,"\n");
      if ((posStr=strstr(BufferString(internalBuffer),s1)))
	{
	  rec->offset=
	    BufferLen(rec->bufferInput)
	    +(posStr-BufferString(internalBuffer));
	  BufferStrcat(rec->bufferInput, BufferString(internalBuffer));
	  rec->stringBuf=BufferString(rec->bufferInput);
	  return rec;
	}
      else
	{
	  BufferStrcat(rec->bufferInput, BufferString(internalBuffer));
	  return HfdRecordGetUntil(rec, f1, s1);
	}
    }
  else
    {
      /* ExceptSetError("HfdRecord","01","string ", s1, "unreachable",1); */
       return 0;
    }
}

HfdRecord *      HfdRecordUntil(rec, f1, s1)
     HfdRecord *rec;
     FILE      *f1;
     char *s1;
{
  char *posStr;
  if ((posStr=strstr(BufferString(rec->bufferInput)+rec->offset,s1)))
    {
      rec->offset=posStr-BufferString(rec->bufferInput);
      return rec;
    }
  else return HfdRecordGetUntil(rec, f1, s1);  
}

HfdRecord *HfdRecordGetSxmlDtd(rec, f1)
     HfdRecord *rec;
     FILE      *f1;
/*                             rec->offset -> !  */
{
  HfdRecordField *field;
  char *posStr;

  switch(rec->stringBuf[rec->offset+1])
    {
    case '-':               /*  commentaire SGML */
      rec->offset+=3;
      field= HfdRecordFieldGet(rec);
      field->offsetEffectiveBeginField=rec->offset;
      if (!(HfdRecordUntil(rec, f1, "-->")))
	{
	  ExceptSetError("HfdRecord","01","string ", "-->", "unreachable",1);
	  return 0;
	}
      field->offsetEffectiveEndField=rec->offset;
      if((posStr=strchr(BufferString(rec->bufferInput)+rec->offset, '\t')))
	{
	  field->offsetEndField=posStr-BufferString(rec->bufferInput);
	}
      else field->offsetEndField=BufferLen(rec->bufferInput);
      rec->offset=field->offsetEndField;
      field->type='C';
      break;

    default:

      break;
    }

  return rec;
}

HfdRecord *HfdRecordGetSxmlNode(rec, f1)
     HfdRecord *rec;
     FILE      *f1;
{
  HfdRecordField *field;
  char *afterMark;
  char *beginMark;
  char *endMark;
  char *posTab;
  int lenMark;

  beginMark=BufferString(rec->bufferInput)+rec->offset+1;
  field= HfdRecordFieldGet(rec);
  if ((afterMark=RegExpAfter(RE_SgmlId,beginMark)))
    {
      lenMark=afterMark-beginMark;
      endMark= malloc(lenMark+4);
      strcpy(endMark,"</");
      strncat(endMark, beginMark, afterMark-beginMark);
      endMark[lenMark+2]='>';
      endMark[lenMark+3]=0;
      field->offsetEffectiveBeginField=rec->offset;
      
      if(afterMark[0]=='>')
	rec->offset+=lenMark+1;
      else
	{
	  char *effectiveAfterMark;
	  if (!(effectiveAfterMark=strchr(afterMark,'>')))
	    ExceptSetError("HfdRecord","01","start tag ",beginMark , "not terminated",1);
	  rec->offset+=(effectiveAfterMark-beginMark);
	  afterMark=effectiveAfterMark;
	}
      if (afterMark[-1]=='/')
	{
	  /* empty element record */
	  field->offsetEffectiveEndField=rec->offset+2;
	  
	}
      else
	{
	  /* HfdRecord_until(rec, f1, endMark ); */
	  {
	    char *posStr;
	    char c1;
	    
	    rec->pileMark=0;
	    posStr=afterMark;
	    while (posStr++)
	      {
		if((c1=*posStr))
		  {
		    if(c1=='<')
		      {
			if (*(posStr+1)=='/')
			  {
			    if (strncmp(endMark+2,posStr+2,lenMark+1)==0)
			      {
				if (rec->pileMark==0)
				  {
				    rec->offset=posStr-BufferString(rec->bufferInput);
				    break;
				  }
				else rec->pileMark--;
			      }		    
			  }
			else
			  {
			    if (strncmp(endMark+2,posStr+1,lenMark)==0)
			      {
				char cEnd;
				cEnd=*(posStr+1+lenMark);
				if((cEnd=='>')||(cEnd==' '))rec->pileMark++;
			      }
			  }
		      }
		  }
		else
		  {
		    HfdRecordGetUntil(rec, f1, endMark);
		    break;
		  }	    
	      }
	  }
	  
	  field->offsetEffectiveEndField=rec->offset+lenMark+3;
	}
      if((posTab=strchr(BufferString(rec->bufferInput)+rec->offset, '\t')))
	{
	  field->offsetEndField=posTab-BufferString(rec->bufferInput);
	}
      else field->offsetEndField=BufferLen(rec->bufferInput);
      rec->offset=field->offsetEndField;
    }
  else ExceptSetError
    ("HfdRecord","01","Sgml tag not fit: ",beginMark-1,"" ,1);
  
  field->type='S';
  free(endMark);
  return rec;
}


HfdRecord *HfdRecordGetSxml(HfdRecord *rec, FILE *f1)
{

  switch(rec->stringBuf[rec->offset+1])
    {
    case '!':
      rec->offset++;
      HfdRecordGetSxmlDtd(rec, f1);
      break;
    default:
      HfdRecordGetSxmlNode(rec, f1);
      break;
    }
  return rec;
}


SxmlNode* HfdRecordToSxml(HfdRecord *rec)
{
  HfdRecordField *field;
  char *str;
  char memchar;
  int l;
  if(rec->recordNode)SxmlFree(rec->recordNode);
  rec->recordNode=SxmlElementCreate("");
  rec->recordNode->dilibSgmlSubType='R';             /* To be improved ? */
  /*  SgmlSubType(rec->recordNode)='R'; */

  if(!(field=rec->first))return rec->recordNode;
  do
    {
      switch (field->type)
	{
	case 'D':
	  l=field->effectiveEndField-field->effectiveBeginField;
	  str=malloc (l+1);
          strncpy(str, field->effectiveBeginField,l);
	  str[l]='\0';
	  /* SgmlAddData(rec->recordNode,str); */
	  SxmlAppendChild(rec->recordNode, SxmlTextCreate(str));
          free(str);
	  break;

	case 'C':
	  memchar=field->effectiveEndField[0];
	  field->effectiveEndField[0]='\0';
	  SxmlAppendChild
	    (rec->recordNode
	     ,SxmlCommentCreate( field->effectiveBeginField));
          field->effectiveEndField[0]=memchar;
	  break;

	case 'S':
	  memchar=field->effectiveEndField[0];
	  field->effectiveEndField[0]='\0';
	  SxmlAppendChild
	    (rec->recordNode
	     ,SxmlFromString( field->effectiveBeginField));
          field->effectiveEndField[0]=memchar;
	  break;
	}
    }
  while ((field=field->next));
  return rec->recordNode;
}

char *HfdRecordAfterKey(HfdRecord *record)
{
  HfdRecordField *f;
  if(record)
    {
      if ((f=record->first->next))
	{
	  return f->beginField ;
	}
    }
  return NULL;
}
