//  Classe Dynamic Variable List (DVList) de PEIDA
//                  R. Ansari  1997
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include "machdefs.h"
#include <stdlib.h>
#include <stdio.h>

#include "dvlist.h"
#include "strutil.h"

//++
// Class	DVList
// Lib		Outils++
// include	dvlist.h
//
//	Cette classe permet de gérer une ensemble de variables (ou paramètres)
//	pouvant être définies dynamiquement à l'execution. Le nom des 
//	variables ne doit pas contenir de blancs ("<espace>") et est 
//	limité à 64 caractères maximum. Cette classe 
//	offre la possibilité de sauvegarder l'ensemble 
//	des variables (Nom, Type, Valeur) dans un fichier, ou de
//	recréer l'objet DVList et l'ensemble de ses variables à 
//	partir d'un fichier (Objet PPersist). Une zone commentaire (max=320 c.) 
//	est associée à chaque objet DVList, accessible à travers  
//	la méthode "Comment()". Les objets de cette classe sont  
//	en particulier destinés à être inclus dans d'autres objets 
//	PPersist plus complexes. La classe DVList gère des 
//	variables de type entier ("int_4"), réél double précision ("double")
//	et de type chaine de caracteres ("string, char*", maxi 30 caracteres ).
//	Une classe intermédiaire (*MuTyV*) est utilisée pour représenter une 
//	variable et fournit les services de conversion entre les différents types.
//--
//--
//++
// Links	Parents
// PPersist
//--

char MuTyV::myStrBuf[64];   // Declare static ds le .h 

static MuTyV ddvdum(-9.e19);


//++
// Titre	Constructeurs
//--

//++
// DVList()
//	Constructeur par défaut
// DVList(DVList& cfd)
//	Constructeur par copie. Le nouvel objet est une copie complète de "cfd"
// DVList(char* flnm)
//	Constructeur avec initialisation à partir du contenu du fichier (PPF)
//	"flnm". Le fichier doit avoir été créé par la méthode "Write()"
//--

/* --Methode-- */
DVList::DVList()
{
comment = "";
}

/* --Methode-- */
DVList::DVList(const DVList& dvl)
{
Merge(dvl);
}

/* --Methode-- */
DVList::DVList(char *flnm)
{
PInPersist s(flnm);
ObjFileIO<DVList> fiodvl(this);
fiodvl.Read(s);
}


/* --Methode-- */
DVList::~DVList()
{
}

//++
// Titre	Gestion des variables et opérateurs
//--

//++
// void  Clear()
//	Supprime la définition de toutes les variables de l'objet.
// DVList&  Merge(const DVList& lv)
//	Fusionne l'objet avec la liste des variables de l'objet "lv"
// DVList&  operator= (const DVList& cofr)
//	Remplace la liste des variables de l'objet par celle de l'objet "cofr".   
//--

/* --Methode-- */
DVList&     DVList::operator= (const DVList& dvl)
{
Clear();
return(Merge(dvl));
}


/* --Methode-- */
void        DVList::Clear()
{
mvlist.erase(mvlist.begin(), mvlist.end());
comment = "";
}

/* --Methode-- */
DVList&     DVList::Merge(const DVList& dvl)
{
ValList::const_iterator it;
for(it = dvl.mvlist.begin(); it != dvl.mvlist.end(); it++)
  {
  switch ((*it).second.typ) 
    {
    case 'I' :
      SetI((*it).first, (*it).second.mtv.iv);
      break;
    case 'D' : 
      SetD((*it).first, (*it).second.mtv.dv);
      break;
    case 'S' : 
      SetS((*it).first, (*it).second.mtv.strv);
      break;
    default :
      break;
    }
  }
comment = comment + "\n" + dvl.comment;
return(*this);
}


//++
// int_4   GetI(string const& key, int_4  def=-1)
// double  GetD(string const& key, double def=-9.e19)
// string  GetS(string const& key, char* def="")
//	Retourne la valeur de la variable de nom "key" et de type entier, réél, 
//	chaine de caracteres.
//	Si la variable n'existe pas, la valeur par défaut "def" est renvoyée.
//--

/* --Methode-- */
int_4       DVList::GetI(string const& key, int_4 def)
{
ValList::iterator it = mvlist.find(key);
if (it == mvlist.end())  return(def);
if ( (*it).second.typ != 'I') return(def);
return((*it).second.mtv.iv);
}

/* --Methode-- */
double      DVList::GetD(string const& key, double def)
{
ValList::iterator it = mvlist.find(key);
if (it == mvlist.end())  return(def);
if ( (*it).second.typ != 'D') return(def);
return((*it).second.mtv.dv);
}

/* --Methode-- */
string      DVList::GetS(string const& key, char* def)
{
ValList::iterator it = mvlist.find(key);
if (it == mvlist.end())  return(def);
if ( (*it).second.typ != 'S') return(def);
return((*it).second.mtv.strv);
}

//++
// void  SetI(string const& key, int_4  val)
// void  SetD(string const& key, double val)
// void  SetS(string const& key, char*  val)
// void  SetS(string const& key, string val)
//	Crée la variable de nom "key", de type entier, double, string et 
//	lui attribue la valeur "val". Si une variable du même nom existe, 
//	sa valeur et eventuellement son type sont modifiés.  
//--

/* --Methode-- */
void        DVList::SetI(string const& key, int_4 val)
{
Get(key) = (int_4)val;
}

/* --Methode-- */
void        DVList::SetD(string const& key, double val)
{
Get(key) = (double)val;
}

/* --Methode-- */
void        DVList::SetS(string const& key, char const* val)
{
MuTyV div(val);
Get(key) = div;
}

/* --Methode-- */
void        DVList::SetS(string const& key, string val)
{
MuTyV div(val);
Get(key) = div;
}


//++
// MuTyV&  Get(string const& key) 
//	Renvoie une référence sur l'objet "MuTyV" de la liste avec le nom "key".
//	Si cet objet (variable) n'existe pas, il est créé.
// MuTyV&     operator()  (string const& key) 
// MuTyV&     operator[]  (string const& key) 
//
//	Renvoie la variable de nom "key". Equivalent à "Get(key)". 
// string&  Comment()
//	Renvoie une référence sur le champ commentaire de l'objet.
//--

/* --Methode-- */
MuTyV&      DVList::Get(string const& key)
{
size_t l = key.length();
if ( (l < 1) || (key.find_first_of(" ") < l) )  return(ddvdum);
ValList::iterator it = mvlist.find(key);
if (it == mvlist.end()) mvlist[key] = (int_4) 0;  // $CHECK$ EA. Ambigu si pas de cast...
it = mvlist.find(key);
if (it == mvlist.end()) return(ddvdum); 
else return((*it).second);
}

//++
// Titre	Entrée-Sortie
//--

//++
//  void  Print()
//	Imprime (sur "cout") la liste des variables et leurs valeurs.
//  void  Print(ostream& os)
//	Imprime sur le flot "os" la liste des variables et leurs valeurs.
//  ostream&    operator << (ostream& s, DVList& dvl)
//	sortie sur flot "s" (Appel a "Print(s)").
//  void  Write(string const& fn)
//	Ecriture d'un fichier PPersist de nom "fn"
//  void  Read(string const& fn)
//	Lecture d'un fichier PPersist de nom "fn"
//--


/* --Methode-- */
void        DVList::Print(ostream& os) const
{
os << "DVList::Print() - NVar= " << (int)mvlist.size() << "\n";
os << "Comment: " << comment << "\n";
char buff[128];
ValList::const_iterator it;
for(it = mvlist.begin(); it != mvlist.end(); it++)  {
  switch ((*it).second.typ) 
    {
    case 'I' :
      sprintf(buff, "%s = %d (int) \n", (*it).first.substr(0,64).c_str(), (*it).second.mtv.iv );
      break;
    case 'D' : 
      sprintf(buff, "%s = %.20g (double) \n", (*it).first.substr(0,64).c_str(), (*it).second.mtv.dv );
      break;
    case 'S' : 
      sprintf(buff, "%s = %s (string) \n", (*it).first.substr(0,64).c_str(), (*it).second.mtv.strv );
      break;
    default :
      break;
    }
  os << (string)buff;
  }
os << endl;
}


//++
// Titre	Exemples
//	Utilisation des objets *MuTyV* :
//|	MuTyV mvu;         // Declaration d'une variable 
//|	mvu = 60;          // mvu est de type entier (= 60)
//|	mvu = 66.6;        // et double (= 66.6) maintenant ...   
//|	MuTyV mvi(14);     // On construit une variable entiere = 14
//|	float x = mvi;     // x vaut 14.0
//|	MuTyV mvd(44.4);   // Variable double = 44.4
//|	int k = mvd;       // k vaut 44  
//|	MuTyV mvs("Bonjour, Ca va ?");   // Variable chaine de caracteres 
//|	string s = mvs;    // s vaut "Bonjour, Ca va ?"  
//	Utilisation des *DVList* :		 
//|	DVList  dvl;
//|	dvl("toto") = 14;
//|	dvl("titi") = 25.5;
//|	dvl("tata") = "Bonjour, Ca va ?";
//	Majuscules et minuscules sont differenciees pour les noms, pas de blanc ...
//|	dvl("hello") = 88;
//|	dvl("Hello") = 77.77;
//|	dvl.Comment() = "Test d'objet DVList, avec variables hello, Hello ";
//|	dvl.Write("dvlist.ppf");
//	Plus loin, ou dans un autre programme, on relit le fichier fabrique plus haut 
//|	DVList dvlr("dvlist.ppf");
//|	int k = dvlr["toto"] ;     //  k = 14
//|	double b = dvlr["titi"] ;  //  b = 25.5
//|	string s =  dvlr["tata"] ; //  s = "Bonjour, Ca va ?"
//|	float c = dvlr["Hello"] ;  //  c = 77.77
//|	int l =  dvlr["Hello"] ;   //  l = 77
//|	int m =  dvlr["hello"] ;   //  m = 88
//--


//----------------------------------------------------------
// Classe pour la gestion de persistance
// ObjFileIO<DVList>
//----------------------------------------------------------

/* --Methode-- */
void        ObjFileIO<DVList>::WriteSelf(POutPersist& s) const
{
char buf[320];

int lc = dobj->Comment().length();
if (lc > 319) lc = 319;
if (lc > 0) {
  sprintf(buf,"Comment: ( %6d ) ", lc); 
  s.PutLine(buf);
  s.PutBytes(dobj->Comment().c_str(), lc);
  }
s.PutLine("----Variable-List---------------");
DVList::ValList::const_iterator it;
for(it = dobj->Begin(); it != dobj->End(); it++)  {
  switch ((*it).second.typ) 
    {
    case 'I' :
      sprintf(buf,"I %s %d", (*it).first.substr(0,64).c_str(), (*it).second.mtv.iv );
      s.PutLine(buf); 
      break;
    case 'D' : 
      sprintf(buf,"D %s %.20g", (*it).first.substr(0,64).c_str(), (*it).second.mtv.dv );
      s.PutLine(buf); 
      break;
    case 'S' : 
      sprintf(buf,"S %s %s", (*it).first.substr(0,64).c_str(), (*it).second.mtv.strv );
      s.PutLine(buf); 
      break;
    default :
      break;
    }
  }

s.PutLine("ZZZZZ--End-of-Varible-List------");
}

/* --Methode-- */
void        ObjFileIO<DVList>::ReadSelf(PInPersist& s)
{
char buf[320];
int_4 j,iv;
double dv;
bool ok=true;
buf[0] = '\0';
dobj->Clear();

s.GetLine(buf, 319);  // Pour lire les "------- "
if (buf[0] != '-') {  // Il y a un champ commentaire a lire 
  buf[18] ='\0';
  int lc = atoi(buf+11);
  if (lc > 319)  { 
    cerr << "DVList::ReadSelf() Pb/Bug ??  CommentLength= " << lc << endl;
    lc = 319;
    }
  s.GetBytes(buf, lc);
  buf[lc] ='\0';
  dobj->Comment() = buf;
  }

while(ok) {
  s.GetLine(buf, 319);
  buf[319] = '\0';
  if (strncmp(buf,"ZZZZZ",5) == 0)  { ok=false; break; }
  j = posc(buf+2, ' ')+2;
  buf[j] = '\0';
  switch (buf[0])
    {
    case 'I' :
      iv = (int_4)atol(buf+j+1);
      dobj->SetI(buf+2, iv);
      break;
    case 'D' :
      dv = atof(buf+j+1);
      dobj->SetD(buf+2, dv);
      break;
    case 'S' :
      dobj->SetS(buf+2, buf+j+1);
      break;
    default :
      break;
    }
  } 
}

#ifdef __CXX_PRAGMA_TEMPLATES__
#pragma define_template ObjFileIO<DVList>
#endif

#if defined(ANSI_TEMPLATES) || defined(GNU_TEMPLATES)
template class ObjFileIO<DVList>;
#endif
