/***********************************************************************
*
*               Module   : StrDict
*               Fichier  : StrDict.c
*               Auteur   : J. DUCLOY
*               Date     : 2013
*               Modifications
************************************************************************
*
* CopyLeft  CC-by-sa
* 
************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "StrDict.h"
       
void qsort();

char *StrDictLastKey;

int StrDictCompare(node1,node2) 
const void *node1;
const void *node2;
{
 return (strcmp(  ((StrDictDatum*)node1)->key,
		  ((StrDictDatum*)node2)->key   ));
}

StrDict *StrDictReset(StrDict* curTable)
{
  curTable->status=0;	
  curTable->numberOfElements=0;
  curTable->currentPos=-1;
  return curTable;
}

StrDict* StrDictCreate(int number, int incr)
{
  StrDict *newTable;

  newTable = (StrDict *) malloc(sizeof(StrDict));

  newTable->size=number;
  newTable->incr=incr;
  newTable->valueType='p';
  newTable->status=0;
  newTable->table=(StrDictDatum *)malloc(number*sizeof(StrDictDatum));
  
  StrDictReset(newTable);
  
  return(newTable);
}

StrDict*     StrDictSetValueType(StrDict *table, char valueType)
{
  table->valueType=valueType;
  return (table);
}

StrDict*     NewStrDict()
{
  StrDict *d1;
  d1= StrDictCreate(32, 0);
  return d1;
}

void StrDictFree(StrDict *t)
{
  free(t->table);
  free(t);
}

void StrDictFreeKey(StrDict *t)
{
  char *key;
  if(t)
    {
      StrDictIteratorReset(t);
      while ((key=StrDictNext(t)))
	{
	  free(key);
	}
      StrDictFree(t);
    }
}

void StrDictFreeStr(StrDict *t)
{
  char *key;
  char *value;
  if(t)
    {
      StrDictIteratorReset(t);
      while ((key=StrDictNext(t)))
	{
	  free(key);
	  if((value=StrDictValue(t)))free(value);
	}
      StrDictFree(t);
    }
}


StrDictValueUnionTypes *StrDictAddNewDatumWithUnion(StrDict *t, char *s1, StrDictValueUnionTypes *u2)
{

  if(t->size <= t->numberOfElements)
    {
      if (t->incr>0) t->size+=t->incr;
      else 
	{
	  if (t->size>4) t->size+=(t->size/4);
	  else t->size++;
	}
      t->table=(StrDictDatum*)
	realloc(t->table,t->size*(sizeof(StrDictDatum)));
    };
  t->table[t->numberOfElements].key = s1;
  switch (t->valueType)
    {
    case 'p':
    case 'c':
      t->table[t->numberOfElements].value.str = u2->str;
      break;
    case 'i':
      t->table[t->numberOfElements].value.i = u2->i;
      break;
    case 'd':
      t->table[t->numberOfElements].value.d = u2->d;
      break;
    }
  switch(t->status)
    {
    case 0 : 
      t->numberOfElements=1;
      t->greatest=s1;
      t->status=1;
      break;
    case 1 : 
      t->numberOfElements++;
      if (strcmp(s1,t->greatest)>=0)
	{
	  t->greatest=s1;
	}
      else
	{
	  t->status=2;
	}
      break;
    case 2 : 
      t->numberOfElements++;
      break;
    case 3 : 
      t->numberOfElements++;
      if (strcmp(s1,t->greatest)>=0)
	{
	  t->greatest=s1;
	  t->status=1;
	}
      else
	{
	  t->status=2;
	}
       break;
    };
  /* return s2; */
  return  &t->table[t->numberOfElements].value;
}

char *StrDictAddNewDatum(StrDict *t, char *s1, char *s2) 
{
  StrDictValueUnionTypes u2;
  u2.str=s2;
  StrDictAddNewDatumWithUnion(t, s1, &u2);
  return s2;
}

double StrDictAddNewDatumWithDouble(StrDict *t, char *s1, double d2)
{
  StrDictValueUnionTypes u2;
  u2.d=d2;
  StrDictAddNewDatumWithUnion(t, s1, &u2);
  return d2;
}

int StrDictAddNewDatumWithInt(StrDict *t, char *s1, int i1)
{
  StrDictValueUnionTypes u2;
  u2.i=i1;
  StrDictAddNewDatumWithUnion(t, s1, &u2);
  return i1;
}


/*
            StrDictCheckSort (ex StrSearchSortTable)
            checks status and sorts if needed
 */
int StrDictCheckSort(StrDict *t)
{
  switch(t->status)
    {
    case 0 : 
      return 0;
    case 1 : 
      t->status=3;
      return 1;
    case 3 :
      return 1;
    case 2 : 
      qsort(t->table, t->numberOfElements,
	    sizeof(StrDictDatum),StrDictCompare);
      t->status=3;
      t->greatest=t->table[t->numberOfElements-1].key;
      return 1;
    };
  return 0;
}


StrDictValueUnionTypes *StrDictSetUnion(StrDict *t, char *s1,  StrDictValueUnionTypes *u1)
{
  StrDictDatum *resu;
  StrDictDatum datum;
  StrDictValueUnionTypes *u2;

  resu=NULL;

  if (StrDictCheckSort(t))
    {
      datum.key=s1;
      resu=
	(StrDictDatum *)bsearch
	((char *)&datum,t->table,t->numberOfElements,
	 sizeof(StrDictDatum),StrDictCompare);
      if(resu)
	{
	  switch (t->valueType)
	    {
	    case 'p':
	    case 'c':
	      resu->value.str=u1->str;
	      break;
	    case 'i':
	      resu->value.i=u1->i;
	      break;
	    case 'd':
	      resu->value.d = u1->d;
	      break;
	    }

	  return &resu->value;
	}
    }
  u2=StrDictAddNewDatumWithUnion(t,s1,u1);
  return  u2;
}

char* StrDictSet(StrDict *t, char *s1, char *s2)
{
  StrDictValueUnionTypes u2;
  u2.str=s2;
  StrDictSetUnion(t, s1,  &u2);
  return s2;
}

double StrDictSetDouble(StrDict *t, char *k1, double d1)
{
  StrDictValueUnionTypes u2;
  u2.d=d1;
  StrDictSetUnion(t, k1,  &u2);
  return d1;
}

int StrDictSetInt(StrDict *t, char *k1, int i1)
{
  StrDictValueUnionTypes u2;
  u2.i=i1;
  StrDictSetUnion(t, k1,  &u2);
  return i1;
}

StrDictValueUnionTypes *StrDictSearchUnion(StrDict *t, char *s1)
{
  StrDictDatum *resu;
  StrDictDatum datum;
  if (!s1)return NULL;
  if (StrDictCheckSort(t))
    {
      datum.key=s1;
      resu=
	(StrDictDatum *)bsearch
	((char *)&datum,t->table,t->numberOfElements,
	 sizeof(StrDictDatum),StrDictCompare);
      if(resu)
	{
	  StrDictLastKey=resu->key;
	  return &resu->value;
	}
      else 
	{
	  StrDictLastKey=NULL;
	  return NULL;
	}
    }
  else return NULL;
}

StrDictValueUnionTypes * StrDictCheck(StrDict *t, char *s1)
{
  return StrDictSearchUnion(t, s1);
}

char *StrDictSearch(StrDict *t, char *s1)
{
  StrDictValueUnionTypes *u2;
  u2=StrDictSearchUnion(t, s1);
  if (u2) return u2->str;
  return NULL;
}

int  StrDictSearchInt(StrDict *t, char *k1)
{
  StrDictValueUnionTypes *u2;
  u2=StrDictSearchUnion(t, k1);
  return u2->i;
}

double  StrDictSearchDouble(StrDict *t, char *k1)
{
  StrDictValueUnionTypes *u2;
  u2=StrDictSearchUnion(t, k1);
  return u2->d;
}

char *StrDictFist(StrDict *curTable)
{
  if (curTable->numberOfElements<=0)return NULL;
  curTable->currentPos=0;
  return curTable->table[curTable->currentPos].key;
}

char *StrDictNext(StrDict *curTable)
{
  if(curTable->currentPos+2>curTable->numberOfElements)return NULL;
  curTable->currentPos++;
  return curTable->table[curTable->currentPos].key;
}


char *StrDictPrevious(StrDict *curTable)
{
  if(curTable->currentPos<=0)return NULL;
  curTable->currentPos--;
  return curTable->table[curTable->currentPos].key;
}



void StrDictIteratorReset(StrDict* curTable)
{
  curTable->currentPos=-1;
  StrDictCheckSort(curTable);
}

int StrDictNumLessEqual(StrDictDatum *datumTable,
			  char *elem,
			  int beginPos,
			  int endPos)
{
  int cmpVal;
  int mediumPos;

  if (endPos<0)return -1;
  if(beginPos==endPos)
    {
      cmpVal=strcmp(elem,datumTable[endPos].key);
      if(cmpVal>=0)return endPos;
      else return -1;
    }
  if(endPos==(beginPos+1))
    {      
      cmpVal=strcmp(elem,datumTable[beginPos].key);
      if(cmpVal==0)return beginPos;
      if(cmpVal<0)return -1;
      cmpVal=strcmp(elem,datumTable[endPos].key);
      if(cmpVal>=0)return endPos;
      return beginPos;
    }
  mediumPos=(beginPos+endPos)/2;
  cmpVal=strcmp(elem,datumTable[mediumPos].key);
  if(cmpVal==0)return mediumPos;
  if(cmpVal>0)
    return StrDictNumLessEqual(datumTable, elem, mediumPos, endPos);
  return StrDictNumLessEqual(datumTable, elem, beginPos, mediumPos-1);
}


char *StrDictKeyLessEqual(StrDict *curTable, char *s1)
{
  int indice;
  if (StrDictCheckSort(curTable))
    {
      indice=StrDictNumLessEqual
	(curTable->table, 
	 s1,
	 0, 
	 curTable->numberOfElements-1);
      if (indice<0)return NULL;
      curTable->currentPos=indice;
      return (StrDictKey(curTable));
    }
  else return NULL;
}

/*
             functions usefull when value has type char
 */

char *StrDictListOfStrChar=NULL;

char *StrDictListOfStrCharCreate()
{
  int i;
  StrDictListOfStrChar=malloc(512);
  i=0;
  while(i<=255)
    {
      StrDictListOfStrChar[2*i]=(unsigned char)i;
      StrDictListOfStrChar[2*i+1]='\0';
      i++;
    }
  return StrDictListOfStrChar;
}

char *StrDictAddNewDatumChar(StrDict *t, char *k1, unsigned char vc1){
  if(!StrDictListOfStrChar)StrDictListOfStrCharCreate();
  return StrDictAddNewDatum(t,k1, StrDictListOfStrChar+2*vc1);
}

char *StrDictSetChar(StrDict *t, char *k1, char c1){
  if(!StrDictListOfStrChar)StrDictListOfStrCharCreate();
  return StrDictSet(t,k1, StrDictListOfStrChar+2*c1);
}

char StrDictGetChar(StrDict *t, char *k1){
  char *str;
  if ((str=StrDictSearch(t, k1))) return str[0];
  return '\0';
}
