#include <stdio.h>
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#include <dirent.h>
#include <string.h>

using namespace std; 

#ifndef _TStringList_
#define _TStringList_

typedef std::string String;

class TStringList
{
	public:
		int Count;
	    std:: vector<String> Strings;

		TStringList();
		~TStringList();
		
		void Add(String nouveau);
		void Add(TStringList *nouveau);
		void Delete(int position);
		void Insert(int position, String nouveau);
		void Clear();
		void LoadFromFile(String nom_fichier);
		void SaveToFile(String nom_fichier);
};

String SubString(String chaine, int debut, int nb_car);
int    StrPos(String caractere, String chaine);
int    AnsiPos(String caractere, String chaine);
String StrReplace(String chaine, String avant, String apres);
String AnsiReplaceStr(String chaine, String avant, String apres);
String Trim(String chaine);
int    StrLength(String chaine);
int    StrLen(String chaine);
String LowerCase(String chaine);

void   explode(TStringList *&colonnes, String ligne, String delimiteur);
void   explode(TStringList *&colonnes, String ligne, char delimiteur);
void   explode(vector<String> *colonnes, String ligne, char delimiteur);
void   explode_sans_trim(TStringList *colonnes, String ligne, String delimiteur);
void   explode_sans_trim(TStringList *colonnes, String ligne, char delimiteur);
void   explodeGuillemets(TStringList*& colonnes, String ligne, String delimiteur);

String RoundTo(float valeur, int nb_chiffres);
String ToString(float nombre);
bool   is_number( const String & Str );

String Unifier(TStringList *liste);
String Unifier(TStringList *liste, String delimiteur);

String Format(int nbcaracteres, String texte);

//---------------------------------------------------------------------------
// Pour convertir un String en int ou en float
// exemple : valueOf("67", i), avec i int ou float.
template<typename T>
inline bool valueOf(const String &s, T &obj)
{ 
  istringstream is(s); 
  return is >> obj; 
} 

//---------------------------------------------------------------------------
// Verifie qu'un String est bien un nombre
bool is_number( const String & Str )
{
    // creer un flux a partir de la chaîne donnee
    std::istringstream iss( Str );
    // creer un objet temporaire
    float tmp;
    // tenter la conversion en un float
    return iss >> tmp != 0;
}

//---------------------------------------------------------------------------
template<typename T>
String ToString( const T & Value )
{
    // utiliser un flux de sortie pour creer la chaîne
    std::ostringstream oss;
    // ecrire la valeur dans le flux
    oss << Value;
    // renvoyer une String
    return oss.str();
}


//---------------------------------------------------------------------------
String ToString(float nombre)
{
String result;
std::ostringstream oss;
oss << nombre;
result = oss.str();
return result;
}

//---------------------------------------------------------------------------
// Formate une chaine de caractères en rajoutant des espaces au début de la
// chaine, si nécessaire, pour obtenir la taille fournie en nb de caractères
String Format(int nbcaracteres, String texte)
{
String temp = "";
if(texte.length()<nbcaracteres)
	temp.append(nbcaracteres-texte.length(), ' ');
return temp + texte;
}

//---------------------------------------------------------------------------
TStringList::TStringList()
{
	Count = 0;
	Strings.clear();
}

//---------------------------------------------------------------------------
TStringList::~TStringList()
{
	Count  = 0;
	Strings.clear();
}

//---------------------------------------------------------------------------
void TStringList::Add(String nouveau)
{
    Strings.push_back(nouveau);
    Count++;
}

//---------------------------------------------------------------------------
void TStringList::Add(TStringList *nouveau)
{
	int ligne;
	if(nouveau!=NULL)
	{
		for(ligne=0;ligne<nouveau->Count;ligne++)
		{
    		Strings.push_back(nouveau->Strings[ligne]);
    		Count++;
    	}
    }
}


//---------------------------------------------------------------------------
void TStringList::Delete(int position)
{
    if(position<Count)
    {
    	Strings.erase(Strings.begin()+position);
    	Count--;
    }
}

//---------------------------------------------------------------------------
void TStringList::Insert(int position, String nouveau)
{
    if(position<Count)
    	Strings.insert(Strings.begin()+position, nouveau);
    else
    	Strings.push_back(nouveau);
    
    Count++;
}

//---------------------------------------------------------------------------
void TStringList::Clear()
{
	Count = 0;
	Strings.clear();
}

//---------------------------------------------------------------------------
void TStringList::LoadFromFile(String nom_fichier)
{
	this->Clear();
	std::ostringstream stream; 
	String str; 
	std::ifstream fichier(nom_fichier.c_str()); 
	while(std::getline( fichier, str ))
		this->Add(StrReplace(str, "\r", ""));
}

//---------------------------------------------------------------------------
void TStringList::SaveToFile(String nom_fichier)
{
std::ofstream fe (nom_fichier.c_str());
int i;
if(fe!=NULL)
{
	i=0;
	while(i<this->Count)
	{
		fe << this->Strings[i].c_str() << "\r\n";
		i++;
	}
}
fe.close();
}

//---------------------------------------------------------------------------
String SubString(String chaine, int debut, int nb_car)
{
	String sortie;
	
	if(debut!=0)
		debut = debut-1;	// Car sous C++ Builder, [1] correspond a la 1ere lettre
	
	sortie=chaine.substr(debut, nb_car);
	
	/*
	int l;
	sortie="";
	l=debut;
	while((l<strlen(chaine.c_str()))&&((l-debut)<nb_car))
	{
		sortie = sortie + chaine[l];
		l++;
	}*/
	
	return sortie;
}

//---------------------------------------------------------------------------
// Retourne la position d'un caractere dans une chaine :
// par exemple, StrPos("AB", "ABCDE") retourne 1
// Si le caractere n'est pas trouve, StrPos retourne -1 (au lieu de 0 pour AnsiPos)
int StrPos(String caractere, String chaine)
{
	int pos;
	pos=chaine.find(caractere);
	if(pos!=-1)
		pos=pos+1;
	return pos;
}

//---------------------------------------------------------------------------
// Retourne la position d'un caractere dans une chaine :
// par exemple, AnsiPos("AB", "ABCDE") retourne 1
// Si le caractere n'est pas trouve, AnsiPos retourne 0 (au lieu de -1 pour StrPos)
int AnsiPos(String caractere, String chaine)
{
	int pos;
	pos=chaine.find(caractere);
	pos=pos+1;
	return pos;
}

//---------------------------------------------------------------------------
// Retourne la longueur d'une chaine
int StrLength(String chaine)
{
	return strlen(chaine.c_str());
}

//---------------------------------------------------------------------------
// Retourne la longueur d'une chaine
int StrLen(String chaine)
{
	return StrLength(chaine);
}

//---------------------------------------------------------------------------
String StrReplace(String chaine, String avant, String apres)
{
int pos;
pos=StrPos(avant, chaine);
while(pos!=-1)
{
	chaine.replace(pos-1, avant.length(), apres);
	pos=StrPos(avant, chaine);
}
return chaine;
}

//---------------------------------------------------------------------------
String AnsiReplaceStr(String chaine, String avant, String apres)
{
	return StrReplace(chaine, avant, apres);
}

//---------------------------------------------------------------------------
String Trim(String chaine)
{
	int l=1;
	String car;
	
	// On supprime les espaces en debut de chaine
	car=SubString(chaine, l, 1);
	while(car==" ")
	{
		chaine = SubString(chaine, 2, strlen(chaine.c_str()));
		car=SubString(chaine, l, 1);
	}
	
	// On supprime les espaces en fin de chaine
	car=SubString(chaine, strlen(chaine.c_str()), 1);
	while(car==" ")
	{
		chaine = SubString(chaine, 1, strlen(chaine.c_str())-1);
		car=SubString(chaine, strlen(chaine.c_str()), 1);
	}
	
	return chaine;
}

//---------------------------------------------------------------------------
// Separe une chaine de caracteres ligne en une liste de chaines
// de caracteres colonnes grace au delimiteur defini
// Exemple : explode(col, "il    fait beau", " ");
//           retournera col avec col->Strings[0]="il"
//                               col->Strings[1]="fait"
//                               col->Strings[2]="beau"
void explode(TStringList *&colonnes, String ligne, String delimiteur)
{
  char str[1000000];
  char * pch;
  colonnes->Clear();
  strcpy(str, ligne.c_str());
  pch = strtok (str,delimiteur.c_str());
  while (pch != NULL)
  {
    if(Trim(pch)!="")
    	colonnes->Add(Trim(pch));
    pch = strtok (NULL, delimiteur.c_str());
  }
}

void explode(TStringList *&colonnes, String ligne, char delimiteur)
{
    istringstream iss(ligne);
    string mot;
    colonnes->Clear();
    while ( std::getline( iss, mot, delimiteur ) )
        colonnes->Add(Trim(mot));
}

void explode(vector<String> *colonnes, String ligne, char delimiteur)
{
    istringstream iss(ligne);
    string mot;
    colonnes->clear();
    while ( std::getline( iss, mot, delimiteur ) )
        colonnes->push_back(Trim(mot));
}

//---------------------------------------------------------------------------
// Separe une chaine de caracteres ligne en une liste de chaines
// de caracteres colonnes grace au delimiteur defini, sans effacer
// les colonnes vides
// Exemple : explode(col, "il;fait;beau; ;non?", ";");
//           retournera col avec col->Strings[0]="il"
//                               col->Strings[1]="fait"
//                               col->Strings[2]="beau"
//                               col->Strings[3]=" "
//                               col->Strings[4]="non?"

void explode_sans_trim(TStringList *colonnes, String ligne, String delimiteur)
{
  char str[10000];
  char * pch;
  colonnes->Clear();
  strcpy(str, ligne.c_str());
  pch = strtok (str,delimiteur.c_str());
  while (pch != NULL)
  {
    colonnes->Add(pch);
    pch = strtok (NULL, delimiteur.c_str());
  }
}

void explode_sans_trim(TStringList *colonnes, String ligne, char delimiteur)
{
    istringstream iss(ligne);
    string mot;
    colonnes->Clear();
    while ( std::getline( iss, mot, delimiteur ) )
        colonnes->Add(mot);
}

//---------------------------------------------------------------------------
// Separe une chaine de caracteres ligne en une liste de chaines
// de caracteres colonnes grace au delimiteur defini, mais en tenant
// compte des guillemets
// Exemple : explode(col, "il fait beau", " ");
//           retournera col avec col->Strings[0]="il"
//                               col->Strings[1]="fait"
//                               col->Strings[2]="beau"

void explodeGuillemets(TStringList*& colonnes, String ligne, String delimiteur)
{
   int num     = 1;
   int tailleDelimiteur = strlen(delimiteur.c_str());

   // Creation d'une liste
   colonnes = new TStringList();

   while(num <= strlen(ligne.c_str()))
   {
      // Si la lettre courante est un guillemet ...
      if (ligne[num] == '\"')
      {
         // ... alors recherche du guillemet de fermeture
         // Tous les caracteres entre les guillemets sont ignores
         while (++num <= strlen(ligne.c_str()) && ligne[num] != '\"');
         // Passe au caractere suivant le guillemet de fermeture
         if (num <= strlen(ligne.c_str()))
            ++num;
      }
      // Si la lettre courante correspond au debut du delimiteur ...
      else if (ligne[num] == delimiteur[1])
      {
         // ... alors verification que ce n'est pas le delimiteur
         int cpt = 1;
         while (++cpt <= tailleDelimiteur && ++num <= strlen(ligne.c_str()) && ligne[++num] == delimiteur[cpt]);
         // Si le delimiteur a ete trouve
         if (cpt == tailleDelimiteur + 1)
         {
            // Recuperation du mot precedent le delimiteur
            String temp = SubString(ligne, 1, num - tailleDelimiteur);
            // Supression du mot recupere de la ligne
            ligne = SubString(ligne, num + tailleDelimiteur, strlen(ligne.c_str()));
            // Insertion du mots dans la liste
            colonnes->Add(Trim(temp));
            // Remise a jour du numero de la lettre courante
            num = 1;
         }
      }
      // C'est une lettre qqconque
      else
         ++num;
   }

   // Ajout du dernier elt
//   if(Trim(ligne)!="")
      colonnes->Add(Trim(ligne));
}

//---------------------------------------------------------------------------
String RoundTo(float valeur, int nb_chiffres)
{
if(nb_chiffres<0)
	nb_chiffres = nb_chiffres*-1;	// Car sous C++ Builder, pour arrondir a
	                                // 2 chiffres apres la virgule, on met
	                                // RoundTo(valeur, -2)
float d = pow(10, float(nb_chiffres)); 
float c = valeur * d; 
float f = floor(c); 

return ToString(floor((f + ((c - f) * 2))) / d); 
}

//---------------------------------------------------------------------------
String Unifier(TStringList *liste)
{
	return Unifier(liste, "");
}

//---------------------------------------------------------------------------
String Unifier(TStringList *liste, String delimiteur)
{
	int i;
	String sortie = "";
	
	if(liste->Count!=0)
		sortie = liste->Strings[0];
	
	for(i=1;i<liste->Count;i++)
		sortie = sortie + delimiteur + StrReplace(StrReplace(liste->Strings[i], "\r", ""), "\n", "");
	
	return sortie;
}

//---------------------------------------------------------------------------
String LowerCase(String chaine)
{
String sortie;
char ch[100000], ch1[100000];
int i;

sortie = "";
if(StrLength(chaine)<100000)
{
	strcpy(ch, chaine.c_str());
	for(i=0;i<StrLength(chaine);i++)
		ch1[i] = tolower(ch[i]);
	sortie = ch1;
	sortie = SubString(sortie, 0,StrLength(chaine));
}
else
	cout << "Erreur, chaine trop longue pour la fonction LowerCase" << endl;

return sortie;
}

#endif
