/* ImLib3D
 * Copyright (c) 2001, ULP-IPB Strasbourg.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*!  \file CppTools.hpp */
#ifndef _CppTools_hpp
#define _CppTools_hpp

#include<stdio.h>
#include<stdlib.h>
#include<string>
#include<vector>
#include<typeinfo>
#include"boost/preprocessor/enum_params.hpp"
#include<iostream>
#include<stdarg.h>

using namespace std;

// **************************************************************
// type definitions
// **************************************************************

typedef unsigned char byte;
typedef unsigned int  uint;


// **************************************************************
// portability
// **************************************************************
#ifdef __CYGWIN__
extern "C"{extern double drand48();}
#endif

// **************************************************************
//@{ \name TypeConversion for variable number of arguments
// **************************************************************

//! Convert an argument into  something useable in a vararg call
/*! This is used for converting strings or other types for  calls to printf  
  or similar fcts. You can easily add your own template instatiation for any type you want */
template<class T>
struct VarArgTypeConversion
{
	static const T &Convert(const T&a){return a;}
};
struct VarArgTypeConversion<string>
{
	static const char * Convert(const string& a){return a.c_str();}
};

// these macros are used by boost macros to generate lists of arguments or other lists
#define VARARG_TYPE_CONVERSION_ARGLIST(INDEX,DATA)\
     const BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2,0,DATA),INDEX) & BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2,1,DATA),INDEX) 

// these macros are used by boost macros to generate lists of arguments or other lists
#define VARARG_TYPE_CONVERSION_TYPE_CONVERSION_MACRO(INDEX,DATA)\
     VarArgTypeConversion<BOOST_PP_CAT(T,INDEX)>::Convert(BOOST_PP_CAT(DATA,INDEX))

//@}

// **************************************************************
//@{ \name Error processing
// **************************************************************

enum ErrorManagementType {ERROR_USE_EXCEPTIONS,ERROR_SEGFAULT,ERROR_CALL_HANDLER};
//! Change the error management policy, default is ERROR_SEGFAULT (good for debuging)
extern void SetErrorManagementType(ErrorManagementType _errorManagementType,void(*_ErrorHandler)(string)=NULL);


//! A Fatal error occured, print a message and fail
void ThrowErrorVA(const char *fmt, ...);

#define ThrowError_MACRO(INDEX)\
template< BOOST_PP_ENUM_PARAMS ( INDEX  , class T  )> \
void ThrowError( const char *fmt,BOOST_PP_ENUM(INDEX,VARARG_TYPE_CONVERSION_ARGLIST,(T,a )) ) \
{ ThrowErrorVA(fmt,BOOST_PP_ENUM(INDEX,VARARG_TYPE_CONVERSION_TYPE_CONVERSION_MACRO,a)); }

ThrowError_MACRO(1) ThrowError_MACRO(7)  ThrowError_MACRO(13) 
ThrowError_MACRO(2) ThrowError_MACRO(8)  ThrowError_MACRO(14) 
ThrowError_MACRO(3) ThrowError_MACRO(9)  ThrowError_MACRO(15) 
ThrowError_MACRO(4) ThrowError_MACRO(10) ThrowError_MACRO(16) 
ThrowError_MACRO(5) ThrowError_MACRO(11) ThrowError_MACRO(17) 
ThrowError_MACRO(6) ThrowError_MACRO(12) ThrowError_MACRO(18) 


inline void ThrowError( const char *fmt){ThrowErrorVA(fmt);}

//@}

// **************************************************************
//@{ \name Debug output
// **************************************************************
extern int globalVerbose;

#define mprintf_MACRO(INDEX)\
template< BOOST_PP_ENUM_PARAMS ( INDEX  , class T  )> \
void mprintf( const char *fmt,BOOST_PP_ENUM(INDEX,VARARG_TYPE_CONVERSION_ARGLIST,(T,a )) ) \
{ if(globalVerbose)printf(fmt,BOOST_PP_ENUM(INDEX,VARARG_TYPE_CONVERSION_TYPE_CONVERSION_MACRO,a)); }

mprintf_MACRO(1) mprintf_MACRO(7)  mprintf_MACRO(13) 
mprintf_MACRO(2) mprintf_MACRO(8)  mprintf_MACRO(14) 
mprintf_MACRO(3) mprintf_MACRO(9)  mprintf_MACRO(15) 
mprintf_MACRO(4) mprintf_MACRO(10) mprintf_MACRO(16) 
mprintf_MACRO(5) mprintf_MACRO(11) mprintf_MACRO(17) 
mprintf_MACRO(6) mprintf_MACRO(12) mprintf_MACRO(18) 

inline void mprintf( const char *fmt){if(globalVerbose)printf(fmt);}

//@}

// **************************************************************
//@{ \name String related  tools
// **************************************************************

extern string SPrintfVA(const char *fmt, ...);
#define SPrintf_MACRO(INDEX)\
template< BOOST_PP_ENUM_PARAMS ( INDEX  , class T  )> \
string SPrintf( const char *fmt,BOOST_PP_ENUM(INDEX,VARARG_TYPE_CONVERSION_ARGLIST,(T,a )) ) \
{ return SPrintfVA(fmt,BOOST_PP_ENUM(INDEX,VARARG_TYPE_CONVERSION_TYPE_CONVERSION_MACRO,a)); }

SPrintf_MACRO(1) SPrintf_MACRO(7)   SPrintf_MACRO(13)
SPrintf_MACRO(2) SPrintf_MACRO(8)   SPrintf_MACRO(14)
SPrintf_MACRO(3) SPrintf_MACRO(9)   SPrintf_MACRO(15)
SPrintf_MACRO(4) SPrintf_MACRO(10)  SPrintf_MACRO(16)
SPrintf_MACRO(5) SPrintf_MACRO(11)  SPrintf_MACRO(17)
SPrintf_MACRO(6) SPrintf_MACRO(12)  SPrintf_MACRO(18)

inline string SPrintf( const char *fmt) { return fmt;}

extern void Replace(string &res,const string & s1,const string & s2);
//! Replace s1 by s2 in res, s2 can be NULL
inline void Replace(string &res,const string & s1,const char *s2){Replace(res,s1,(string)(s2 ? s2 : ""));}
extern int  Split( const string &s, char c, vector<string> &res);
//! Split string s into result res. separator is ONE whitespace
inline int  Split( const string &s, vector<string> &res ) {return Split(s,' ',res);}
extern int Split( const string &s, const string &sep, vector<string> &res);
extern int Read(const string& fname,string& res,bool fail=false);
extern void *Read(const string& fname,int *size=NULL,bool fail=false);
extern void Write(const string& fname,void *data,int size,bool fail=false);
extern string Chop(const string&s, char c);

//! Convert a string to a long int
inline long int LongInt(const string &s){return strtol( s.c_str(), NULL, 10);}
//! Convert a string to a double
inline double   Double(const string &s){return strtod( s.c_str(), NULL);}
//! Convert a string to a float
inline float   Float(const string &s){return (float)Double(s);}
//@}


//@{ \name Convert a list of string arguments into a vector of strings
extern vector<string> Strings(string s1,string s2,string s3,string s4,string s5,string s6,string s7,string s8,string s9,string s10);
extern vector<string> Strings(string s1,string s2,string s3,string s4,string s5,string s6,string s7,string s8,string s9);
extern vector<string> Strings(string s1,string s2,string s3,string s4,string s5,string s6,string s7,string s8);
extern vector<string> Strings(string s1,string s2,string s3,string s4,string s5,string s6,string s7);
extern vector<string> Strings(string s1,string s2,string s3,string s4,string s5,string s6);
extern vector<string> Strings(string s1,string s2,string s3,string s4,string s5);
extern vector<string> Strings(string s1,string s2,string s3,string s4);
extern vector<string> Strings(string s1,string s2,string s3);
extern vector<string> Strings(string s1,string s2);
extern vector<string> Strings(string s1);
extern vector<string> Strings();
//@}

// **************************************************************
//@{ \name File related  tools
// **************************************************************
extern bool IsSameFile(string filename1,string filename2);
extern string WriteTmpFile(const string &data,string extension="");
extern void   WriteStringToFile(const string& fname,const string& data);
extern string FilenameExtension(const string& fname);
extern string FilenamePath(const string& fname);
extern string FilenameBasename(const string& fname);
extern ostream& operator<<(ostream& out, const vector<string>& v);
extern int FileSize(const string& fname);
extern void AppendToFile(const string &fname,const string &data);
inline bool FileExists(const string &fName){return FileSize(fName)>=0;}
extern string FindFileInPath(const string &path,const string &fname);
extern string FindFileInPath(const vector<string> &path,const string &fname);
inline string FindFileInPath(const char *path,const string &fname)
{return path ? FindFileInPath(string(path),fname) : string("");}
//@}

// ******************************************************
//@{ \name  Unix  commands
// ******************************************************

//! Unix rm commands
extern void RemoveFile(string fname);
extern void RemoveFiles(const vector<string>& fnames);
//! execute a command and put it's output in string
extern string Command(const string &cmd);
//@}

// ******************************************************
//@{ \name External Unix programs
// ******************************************************
extern void GnuPlot(const vector<double> &x,const vector<double> &y);
extern void GnuPlot(const vector<double> &y);
extern void GnuPlot(const vector<float> &x,const vector<float> &y);
extern void GnuPlot(const vector<float> &y);
//@}



// **************************************************************
//@{ \name Time  related  tools
// **************************************************************
#include <sys/time.h>
#include <time.h>

//! Current time in seconds
inline double DTime()
{
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return(tv.tv_usec/1000000.0+tv.tv_sec);
}
//! Current time in seconds relative to T0 
inline double DTime(double T0)
{
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return((tv.tv_usec/1000000.0+tv.tv_sec)-T0);
}

// ***** Periodic *******
//! Do something every x seconds
/*! Use () operator to know if time has elapsed
 */
class Periodic
{
public:
    Periodic(double nperiod=.1)
    {
		period=nperiod;
		t0=DTime();
		last=0;
    }
    double total(){return(DTime(t0));}
    void change_period(double nperiod){period=nperiod;}
    double real_period(){return rperiod;}
    int operator()(double *prperiod=NULL)
    {
		double t=DTime(t0);
		rperiod=t-last;
		if(prperiod){*prperiod=rperiod;}
		if((t-last)>period)
		{
			last=t;
			return(1);
		}
		return(0);
    }
private:
    double t0;
    double last;
    double period;
    double rperiod;
};
//@}

// **************************************************************
//@{ \name  Runtime type information
// **************************************************************
//! runtime type compare
template<class T1,class T2> 
inline bool IsSameType(){return typeid(T1)==typeid(T2);}


template<class T> inline bool IsFloat            () {return false;}
template<>        inline bool IsFloat  <float   >() {return true ;}
template<class T> inline bool IsDouble           () {return false;}
template<>        inline bool IsDouble <double  >() {return true ;}
template<class T> inline bool IsByte             () {return false;}
template<>        inline bool IsByte   <byte    >() {return true ;}
template<class T> inline bool IsInt              () {return false;}
template<>        inline bool IsInt    <int     >() {return true ;}
template<class T> inline bool IsShortInt         () {return false;}
template<>        inline bool IsShortInt<short int>() {return true ;}


extern string TypeNameParser(string typeName);
template<class T> 
inline string TypeName(){return TypeNameParser(typeid(T).name());}
template<> inline string TypeName<float  >(){return "float" ;}
template<> inline string TypeName<double >(){return "double";}
template<> inline string TypeName<byte   >(){return "byte"  ;}
template<> inline string TypeName<int    >(){return "int"   ;}
template<> inline string TypeName<short int>(){return "short int"   ;}
template<> inline string TypeName<string >(){return "string";}

//@}


// **************************************************************
//@{ \name  Endianess
// **************************************************************
//! Returns true if most significant byte is the last byte on this system
inline bool IsSmallEndian()
{
	int a=0xff00;
	byte *p=(byte *)&a;
	p++;
	if(*p==0xff){return true;}
	return false;	   
}
template<class T>
inline void ReverseEndian(const T &src,T &dst)
{
	const byte *psrc=((const byte *)&src)+sizeof(T)-1;
	      byte *pdst=((byte *)&dst);
	for(uint i=0;i<sizeof(T);i++){*pdst++=*psrc--;}
}
//@}

#ifdef __GNUC__ 
#ifndef HPUX
#define USE_EXPLICIT_INSTANTIATION
#endif // HPUX
#endif // __GNUC__ 

#ifdef  USE_EXPLICIT_INSTANTIATION
// explicit instantiation of common templates, only for faster compilation
#if __GNUC__==3
extern template class std::basic_string<char>;
#endif// __GNUC__==3
extern template class std::vector<float>;
extern template class std::vector<int>;
extern template class std::vector<byte>;

#if __GNUC__==3
extern template bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*);
extern template bool std::operator< <char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&);
extern template bool std::operator!=<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*);
#endif // __GNUC__==3

#endif // USE_EXPLICIT_INSTANTIATION

#endif // _CppTools_hpp
