//
// $Id: datacards.cc,v 1.10 2004-09-10 09:54:48 cmv Exp $
//
//
// Eric Aubourg, R. Ansari 1995-2000
//               PEIDA (EROS) - SOPHYA
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA


#include "sopnamsp.h"
#include "machdefs.h"
#include "datacards.h"
#include "pexceptions.h"
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <stdio.h>

//++
// Class	DataCards
// Lib		SUtils
// include	datacards.h
//
//   	Cette classe permet la gestion des parametres d'un programme a partir 
//   	de mot-cle (lecture d'un fichier par exemple)
//--


//++
// Titre	Constructeurs
//--
//++
//
// DataCards()
// DataCards(string const& fn)
//    Createur avec lecture des parametres ds le fichier de nom "fn"
//--

/*! 
    \class SOPHYA::DataCards
    \ingroup SUtils
    This class can be used for decoding program parameters from an ascii
    file. Each line of the ascii contains a keyword starting with \b @ .
    Lines with any other character in the first column are treated as comments.
    Keywords can be followed by 0 or more parameters.
    Processing functions (\b ProcCard) can automatically be called for
    keywords matching a given pattern. (see \b AddProcF )
*/ 

/* Default constructor */
DataCards::DataCards()
{
}

/*! constructor with the specification of a parameter file */
DataCards::DataCards(string const& fn)
{
  ReadFile(fn);
}

//++
// Titre	Methodes
//--
//++
// AddProcF(ProcCard f, string const& mtch="*")
//	Ajoute une fonction de traitement a la liste pour les mots cle
//      compatibles avec la chaine "mtch" ("mtch" peut contenir "*" en debut 
//      fin de mot) 
//
// Clear()
//	Supprime les cartes existantes
//
// ReadFile(string const& fn) 
//      Lit le contenu du fichiers "fn" et ajoute les cartes a la liste
//
// AppendCard(string const& line)
//	Rajoute la carte "line" a la liste
//--

/*! 
    Adds a new processing function for all keywords matching the 
    specified pattern.
    \param mtch : The pattern - can contain * 
*/
void
DataCards::AddProcF(ProcCard f, string const& mtch)
{
CrdPF mpf;
if (f == NULL)  return;
mpf.pf = f;
if (mtch.length() <= 0)  mpf.patt = "*";
else  mpf.patt = mtch;
cpfs.push_back(mpf);

// On applique cette nouvelle fonction aux cartes existantes
CardList::iterator  ic;
for(ic = cards.begin(); ic != cards.end(); ic ++)
  {
  vector<string>::iterator ik;
  string tks;
  for(ik = (*ic).tokens.begin(); ik != (*ic).tokens.end(); ik++)
    tks = tks + " " + (*ik);
  ApplyPF(mpf, (*ic).kw, tks);
  }
}

/*! Resets the objects - Suppresses all cards  */
void DataCards::Clear()
{
cards.erase(cards.begin(), cards.end());
}

/*! Reads the file named \b fn. if fn=="" the value of \b SOPHYA_DATACARDS is used */
void
DataCards::ReadFile(string const& fn)
{
  string file = fn;
  if (file == "") {
    char * efn = getenv("SOPHYA_DATACARDS");
    if (efn != NULL)    file = efn;  
  }
  if (file == "")  return;

  try {
    DoReadFile(file);
  } catch(...) {
    char* wdp = getenv("SOPHYA_WORK");
    if (wdp) {
      cerr << "DataCards::ReadFile() Error reading file " << fn << " (Trying with SOPHYA_WORK) \n";
      file = wdp + file;
      DoReadFile(file);
    }
    else cerr << "DataCards::ReadFile() Error reading file " << fn << "\n";
    throw IOExc("DataCards::ReadFile() Error");  
  } 
}

/*! Appends a card, represented by the string \b crd to the list of cards */
void
DataCards::AppendCard(string const& crd)
{
Card c;
size_t p = 1;
size_t q = crd.find_first_of(" \t");
size_t l = crd.length();

string toks;
if (l < 2)  return;
if (crd[0] != '@')  return;

if (q < l)
  {  c.kw = crd.substr(p,q-p);  toks = crd.substr(q, l-q); }
else { c.kw = crd.substr(p,l-p);  toks = ""; }
//  On applique les ProcFunc's
ApplyPFL(c.kw, toks);
while (q < l) 
  {
  p = crd.find_first_not_of(" \t",q+1); // au debut d'un token
  if (p>=l) break;
  q = crd.find_first_of(" \t",p); // la fin du token;
  string token = crd.substr(p,q-p);
  c.tokens.push_back(token);
  }
// On supprime la carte de la liste, si elle existe deja ...
RemoveCard(c.kw);
cards.push_back(c);
}


//++
// Titre	Acces aux parametres
//--
//++
// int   NbCards()
//	Renvoie le nombre de cartes data
// bool	 HasKey(string const& key) 
//	Indique l'existence d'une carte avec la cle "key"
// int   NbParam(string const& key)
//	Indique le nombre de parametre (separes par des espaces) pour la cle "key"
// string  SParam(string const& key, int num = 0, string def="")
//	Renvoie la valeur du parametre numero "num" ( 0..(NbParam()-1) ) sous forme de
//	chaine de caracteres ("string")
// long    IParam(string const& key, int numero = 0, long def = 0)
//	Renvoie la valeur du parametre numero "num" ( 0..(NbParam()-1) ) convertie 
//	en entier ("long")
// double  DParam(string const& key, int numero = 0, double def = 0)
//	Renvoie la valeur du parametre numero "num" ( 0..(NbParam()-1) ) convertie 
//	en flottant ("double")
//--


/*! Returns true if \b key is present in the list */
bool
DataCards::HasKey(string const& key) const
{
  return (FindKeyC(key) != cards.end());
}

//! Returns the total number of cards 
int 
DataCards::NbCards() const
{
return(cards.size());
}

/*! Returns the number of parameters for card \b key. (-1) if \b key not present  */
int 
DataCards::NbParam(string const& key) const
{
  DataCards::CardList::const_iterator it = FindKeyC(key);
  if (it == cards.end()) return(-1);
  else return((*it).tokens.size());
}

/*! Returns the key for card number \b num. */ 
string 
DataCards::GetKey(int num) const
{
  if ((num < 0) || (num >= cards.size()) )  return("");
  int k = 0;
  CardList::const_iterator i;
  for(i=cards.begin(); i != cards.end(); i++) {
    if (k == num)  return((*i).kw);
    else k++;
  }
  return("");
}

/*! Returns the list of parameters for card \b key. */ 
string 
DataCards::GetParams(string const& key) const
{
  string rs;
  DataCards::CardList::const_iterator it = FindKeyC(key);
  if (it == cards.end()) return(rs);
  for (int k=0; k<(*it).tokens.size(); k++) {
    rs += ' ';  rs += (*it).tokens[k];
  }
  return(rs);
}

/*! Returns the parameter number \b numero for card \b key */
string
DataCards::SParam(string const& key, int numero, string def) const
{
  DataCards::CardList::const_iterator it = FindKeyC(key);
  if (it == cards.end()) return(def);
  if ( (numero < 0) || (numero >= (*it).tokens.size()) )  return def;
  return (*it).tokens[numero];
}

/*! Returns the parameter number \b numero for card \b key, converted to type long */
long
DataCards::IParam(string const& key, int numero, long def) const
{
  string p = SParam(key, numero, "");
  if (p == "") return def;
  long i;
  //istrstream(p.c_str(), p.length()) >> i;
  sscanf(p.c_str(),"%ld",&i);
  return i;
}

/*! Returns the parameter number \b numero for card \b key, converted to type double */
double
DataCards::DParam(string const& key, int numero, double def) const
{
  string p = SParam(key, numero, "");
  if (p == "") return def;
  double i;
  //istrstream(p.c_str(), p.length()) >> i;
  sscanf(p.c_str(),"%lg",&i);
  return i;
}

   
/*! Prints the list of cards on the output stream \b s */
void
DataCards::Print(ostream& s) const
{
  for (CardList::const_iterator i = cards.begin(); i != cards.end(); i++) {
    s << setw(10) << (*i).kw << " ";
    for (vector<string>::const_iterator j = (*i).tokens.begin(); j != (*i).tokens.end(); j++)
      s << (*j) << " ";
    s << endl;
  }
}

/*! Reads the file named \b fn. */
void
DataCards::DoReadFile(string const& fn)
{
  char line_buff[512];
  FILE *fip;

  if ( (fip = fopen(fn.c_str(),"r")) == NULL )  
    throw IOExc("DataCards::DoReadFile() fopen Error ") ;
  while (fgets(line_buff,511,fip) != NULL) {
    line_buff[strlen(line_buff)-1] = '\0';   /*  LF/CR de la fin */
    string line(line_buff);
    AppendCard(line);
  }
  fclose(fip);
  return;
}

int
DataCards::ApplyPF(CrdPF & cpf, string const& key, string const& toks)
{
int l,lk;
int rc = 0;
// On verifie si le "pattern" correspond
bool mtch = false;
l = cpf.patt.length(); 
if (cpf.patt == "*")  mtch = true;
else if (cpf.patt[0] == '*')   
  {
  lk = key.length();
  if (cpf.patt[l-1] != '*')
    {
    if (strcmp(key.c_str()+(lk-l+1), cpf.patt.c_str()+1) == 0)   mtch = true;
    }
  else if  (key.find(cpf.patt.substr(1,l-2)) < lk)  mtch = true;
  }
else if (cpf.patt[l-1] == '*')
  {
  if ( strncmp(key.c_str(), cpf.patt.c_str(),l-1) == 0)  mtch = true;
  }
else if (key == cpf.patt)  mtch = true;

// Si oui, on appelle la fonction correspondante
if (mtch)  rc = cpf.pf(key, toks); 

return(rc);
}


int
DataCards::ApplyPFL(string const& key, string const& toks)
{
int rc = 0;
CrdPFList::iterator icf;
for(icf = cpfs.begin(); icf != cpfs.end(); icf++)
  rc += ApplyPF((*icf), key, toks);
return(rc);
}

void  
DataCards::RemoveCard(string const& key)
{
CardList::iterator i;
for(i=cards.begin(); i != cards.end(); i++)
  if ((*i).kw == key) { cards.erase(i);  break;  }
}

DataCards::Card *
DataCards::FindKey(string const& key) 
{
/*
  CardList::iterator i = find_if(cards.begin(), cards.end(), bind2nd(KeyEq(),key));
  if (i == cards.end() ) return NULL;
*/
  CardList::iterator i;
  for(i=cards.begin(); i != cards.end(); i++)
    if ((*i).kw == key) return &(*i);

  return NULL;
}


DataCards::CardList::const_iterator
DataCards::FindKeyC(string const& key) const
{
  CardList::const_iterator i;
  for(i=cards.begin(); i != cards.end(); i++)
    if ((*i).kw == key) return i;
  return cards.end();
}
