/***************************************************************************
			 mystds.h: interface of common functions
                          -------------------
    begin                : 2005/01/19
    copyright            : (C) 2005 by Meijian An
    email                : meijian@iag.usp.br
  ***************************************************************************/

#ifndef mystds_h
#define mystds_h

#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <vector>
#include <list>

#ifdef _OPENMP
#include <omp.h>
#endif

using namespace std;

//********************************************
//frequently used defines
#define PAI (180.0/3.141592653)
#define pai 3.141592653
#define D2A(x) ((x)/180.0*3.141592653)  
#define A2D(x) ((x)*180.0/3.141592653)
#define DOUBLE_NULL numeric_limits<double>::max()
#define EARTH_RADIUS 6371.0

#define IS_NAN(x) ((x) != (x))

struct dPoint2D {double x, y;};
struct fPoint2D {float  x, y;};
struct iPoint2D {int    x, y;};
template <class Type> struct Point2D{Type x, y;}; //initialzie example: struct Point2D<double> apoint;

struct dPoint3D {double x, y, z;};
struct fPoint3D {float  x, y, z;};
struct iPoint3D {int    x, y, z;};
template <class Type> struct Point3D{Type x, y, z;}; 

struct dPoint4D {double x, y, z, t;};
struct fPoint4D {float  x, y, z, t;};
template <class Type> struct Point4D{Type x, y, z, t;};

struct dCell2D{struct dPoint2D point[4];};
struct fCell2D{struct fPoint2D point[4];};

#ifndef BOOL
#define BOOL int
#ifndef FALSE
#define FALSE 0
#define TRUE !FALSE
#endif
#endif

template <class Type> struct data_range{Type min, max;};

template <class Type> struct data_range_delta{ Type min, max, delta; };

template <class Type> data_range<Type> get_data_range_limits()
{
	struct data_range<Type> value;
	value.max = numeric_limits<Type>::max();
	value.min = -value.max;
	return value;
}

//****************************************
// my binary functions (binaryoperation) for C++
#include <functional>
using namespace std;
template <class Type> struct average : public binary_function<Type, Type, Type> 
{
  Type operator()(const Type& x, const Type& y) const { return (Type) ((x + y) / 2); }
};

template <class Type> struct absolute : public unary_function<Type, Type> 
{
	Type operator()(const Type &x) const { return (x>0) ? x : (-x); }
};

//********************************************
//general functions
double getDistance(double *point1, double *point2, int dimension);

//********************************************
//get parameters from arguments
string get_Parameter(const char* sz_param_switch, string vDefault, int argc, char** argv);

int    get_Parameter(const char* sz_param_switch, int    vDefault, int argc, char** argv);
long   get_Parameter(const char* sz_param_switch, long   vDefault, int argc, char** argv);
float  get_Parameter(const char* sz_param_switch, float  vDefault, int argc, char** argv);
double get_Parameter(const char* sz_param_switch, double vDefault, int argc, char** argv);
bool   get_Parameter(const char* sz_param_switch, bool   vDefault, int argc, char** argv);

bool get_switch(const char* sz_param_switch, bool vDefault, int argc, char** argv);


//string argument: -name="a bc"; then: string name="a bc"
string get_Parameter_str(const char* sz_param_switch, string vDefault, int argc, char** argv);

//other arguments
template <class Type> Type get_Parameter_value(const char* sz_param_switch, Type vDefault, int argc, char** argv)
{
	Type value = vDefault;
	size_t ilen = strlen(sz_param_switch);

	for(int i=1; i<argc; i++)
	{
		if(strncmp( sz_param_switch, argv[i], ilen) == 0)
		{
			string szValue = (argv[i] + ilen);
			istringstream str_in(szValue);
			str_in >> value;
			if (str_in.fail())
			{
				str_in.clear();
				value = vDefault;
			}
			return value;
		}
	}

	return value;
};

template <class Type> data_range<Type> get_Parameter_range(const char* sz_param_switch, data_range<Type> vDefault, int argc, char** argv)
{
	data_range<Type> value = vDefault;
	size_t ilen = strlen(sz_param_switch);
	string str1;

	for(int i=1; i<argc; i++)
	{
		if(strncmp( sz_param_switch, argv[i], ilen) == 0)
		{
			string szValue = (argv[i] + ilen);
			size_t ipos= szValue.find(",");
			if(ipos == string::npos) 
			{
				istringstream str_in(szValue);
				str_in >> value.min;
			}else if(ipos == 0)
			{
				istringstream str_in(szValue);
				str_in >> value.max;
			}else
			{
				str1 = szValue.substr(0, ipos);
				istringstream str_in(str1);
				str_in >> value.min;

				str1 = szValue.substr(ipos+1);
				istringstream str_in2(str1);
				str_in2 >> value.max;
			}
			return value;
		}
	}

	return value;
};

template <class Type> data_range_delta<Type> get_Parameter_range_delta(const char* sz_param_switch, data_range_delta<Type> vDefault, int argc, char** argv)
{
	data_range_delta<Type> value = vDefault;
	size_t ilen = strlen(sz_param_switch);
	string str1;

	for(int i=1; i<argc; i++)
	{
		if(strncmp( sz_param_switch, argv[i], ilen) == 0)
		{
			string szValue = (argv[i] + ilen);
			size_t ipos  = szValue.find(",");
			size_t ipos2 = (ipos == (string::npos))? (string::npos) : szValue.find(",", ipos+1);
			if(ipos == string::npos)  //no "," 
			{
				istringstream str_in(szValue);
				str_in >> value.min;
			}else
			{
				if (ipos != 0)//v,*
				{
					str1 = szValue.substr(0, ipos);
					istringstream str_in(str1);
					str_in >> value.min;
				}

				if ((ipos2 - ipos) > 1 || ipos2 == string::npos) //,v, or "v,v"
				{
					size_t ipos2 = szValue.find(",", ipos + 1);
					str1 = szValue.substr(ipos + 1, ipos2 - ipos - 1);
					istringstream str_in1(str1);
					str_in1 >> value.max;
				}

				if (ipos2 != string::npos) //"v,v,v"
				{
					str1 = szValue.substr(ipos2 + 1);
					istringstream str_in2(str1);
					str_in2 >> value.delta;
				}
			}
			return value;
		}
	}

	return value;
};

template <class Type> Point2D<Type> get_2D_Parameter(const char* sz_param_switch, Point2D<Type> vDefault, int argc, char** argv)
{
	Point2D<Type> value = vDefault;
	size_t ilen = strlen(sz_param_switch);

	for(int i=1; i<argc; i++)
	{
		if(strncmp( sz_param_switch, argv[i], ilen) == 0)
		{
			string szValue = (argv[i] + ilen);
			size_t ipos= szValue.find(",");
			if(ipos == string::npos) 
			{
				istringstream str_in(szValue);
				str_in >> value.x;
			}else if(ipos == 0)
			{
				istringstream str_in(szValue);
				str_in >> value.y;
			}else
			{
				string str1 = szValue.substr(0, ipos);
				istringstream str_in(str1);
				str_in >> value.x;

				str1 = szValue.substr(ipos+1);
				istringstream str_in2(str1);
				str_in2 >> value.y;
			}
			return value;
		}
	}

	return value;
};

//get parameters from standard input/output device
template <class Type> Type InputParameter(const char* szParamName, Type vDefault)
{
	Type value = vDefault;
#ifndef _WINDOWS
	cout.flush();
	cout<< "Input " << szParamName << "[" << vDefault << "]: ";

	string str;
	getline(cin, str);
	if(!str.empty())
	{
		istringstream szin( str );
		szin >> value;
	}
#endif

	return value;
};

//get parameters from string
template <class Type> Type read_Parameter(const char* sz_src, int string_length, Type vDefault)
{
	Type value = vDefault;
	if(string_length > 1023)
		string_length = 1023;
	else if(string_length < 1)
		return value;

	char szTmp[1024]="";
	strncat(szTmp, sz_src, string_length);

	string str = szTmp;
	istringstream str_in( str );
	str_in >> value;
	
	return value;
};

template <class Type> Type read_Parameter(const char* sz_from, const char* sz_end, Type vDefault)
{
	int string_length = sz_end - sz_from;

	return read_Parameter(sz_from, string_length, vDefault);
};

//********************************************
//define some debug trace
extern FILE *fpLog;
bool openLog(const char *szFile, const char *mode="w");
void closeLog();
void ShowWarn(const char *szText, FILE* out = stdout);

#define ShowErrWarn(x) ShowWarn(x, stderr)

#define SHOW(x) ShowWarn(x)
#define SHOW0(x) ShowWarn(x)
#define SHOW1(x,v1) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1);\
					 	ShowWarn(szErr);\
					 }

#define SHOW2(x,v1,v2) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2);\
					 	ShowWarn(szErr);\
					 }
#define SHOW3(x,v1,v2,v3) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2,v3);\
					 	ShowWarn(szErr);\
					 }
#define SHOW4(x,v1,v2,v3,v4) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2,v3,v4);\
					 	ShowWarn(szErr);\
					 }
#define SHOW5(x,v1,v2,v3,v4,v5) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2,v3,v4,v5);\
					 	ShowWarn(szErr);\
					 }
#define SHOW6(x,v1,v2,v3,v4,v5,v6) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2,v3,v4,v5,v6);\
					 	ShowWarn(szErr);\
					 }

#define SHOWerr(x) ShowErrWarn(x)
#define SHOWerr0(x) ShowErrWarn(x)
#define SHOWerr1(x,v1) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1);\
					 	ShowErrWarn(szErr);\
					 }

#define SHOWerr2(x,v1,v2) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2);\
					 	ShowErrWarn(szErr);\
					 }
#define SHOWerr3(x,v1,v2,v3) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2,v3);\
					 	ShowErrWarn(szErr);\
					 }
#define SHOWerr4(x,v1,v2,v3,v4) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2,v3,v4);\
					 	ShowErrWarn(szErr);\
					 }
#define SHOWerr5(x,v1,v2,v3,v4,v5) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2,v3,v4,v5);\
					 	ShowErrWarn(szErr);\
					 }
#define SHOWerr6(x,v1,v2,v3,v4,v5,v6) {\
						char szErr[1024];\
					 	sprintf(szErr,x,v1,v2,v3,v4,v5,v6);\
					 	ShowErrWarn(szErr);\
					 }

//********************************************
// float operations
bool is_float_equal( float a,  float b, double epsilon = 1.0e-6); //float: 1.0e-6; double:1.0e-12 
bool is_float_equal(double a, double b, double epsilon = 1.0e-12); //float: 1.0e-6; double:1.0e-12 
bool is_float_equal(struct dPoint2D a, struct dPoint2D b);

//********************************************
// string or byte operations
char *str2lwr(char *cbin, unsigned int length = -1); //unsigned -1 = maximal positive
string str2lwr(string &cbin);

char *strTrim(char *cbIn);
string *strTrim(string &str);

char *strReplace(char *szIn, const char * cbs, char cb);// e.g., ("geophysics", "eo", '_'): 'e' and 'o' are replaced by '_'
string *strReplace(string &strIn, const char * cbs, char cb);

int Sun2PC( char *cbData, int nSize, int ibyte);

char * c_str2fortran_str(char * str, int length);//fill blanks in the end of the string
char * fortran_str2c_str(char * str, int length);//cut the blanks (set C-string end) in the end of the string

int str_split(string astr, vector<string>& v, const char* delim = " \t\n");
int str_split(string astr,   list<string>& v, const char* delim = " \t\n");

//convert unsinged long to the bite position, from bite position to long
#define BITEof(i) (0x00000001<<(i))

//********************************************
// file operations
int LookUpBlock(FILE *fp, const char *szBlock);
int LookUpBlock(ifstream &fin, const char *szBlock);
int copyFile(const char *fileSrc, const char* fileDes, int iWarn=1);

FILE *openfile(const char * szFileName, const char * mode, bool bWarn=true, bool bExit=false);
FILE *openfile(const char * szDir, const char * szFileName, const char * mode, bool bWarn=true, bool bExit=false);

bool openfile(fstream *file,  const char * szFileName,                     int mode, bool bWarn=true, char iosf='f');
bool openfile(fstream *file,  const char * szDir, const char * szFileName, int mode, bool bWarn=true, char iosf='f');

bool openfile(ofstream *file, const char * szFileName, int mode=ios_base::out|ios_base::trunc, bool bWarn=true, char iosf='o');
bool openfile(ofstream *file, const char * szDir, const char * szFileName, int mode=ios_base::out|ios_base::trunc, bool bWarn=true, char iosf='o');

bool openfile(ifstream *file, const char * szFileName, int mode=ios_base::in, bool bWarn=true, char iosf='i');
bool openfile(ifstream *file, const char * szDir, const char * szFileName, int mode=ios_base::in, bool bWarn=true, char iosf='i');

//********************************************
// directory/file system functions 
#ifndef WIN32
#define CBDIR '/'
#define DIRRIGHT S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH |S_IXOTH
#else
#define CBDIR '\\'
#endif

#define MAX_PATH_LENGTH 1024
bool isDir(string &strDir); //check if string is a directory.
bool isDir(char *szDir);    //check if string is a directory.
int CreateDir(char *szDir);

bool isFile(string strFile); //check if string is a file.
bool isFile(char *szFile);    //check if string is a file.
int sizeofFile(const char *szFile);

int remove_file_dir(const char* szFile);

char* get_ith_column(const char *Src, char *szColumn, int ith, const char *szToken = "\t, \n");
int get_Tokens(char *Src, const char *szToken = "\t, \n");
int getTokens(char *sz, char cb='.');

bool CheckExePrograms(const char *szExe);

//verify whether value in a range
template <class Type> bool Inside_range(Type v, Type v1, Type v2)
{
	if(v1 > v2) swap(v1, v2);

	if(v > v2 || v < v1) return false;
	return true;
};

//verify that an angle is between -180~180 or 0~360.
template <class Type> Type Verify_angle_inside_range(Type &v, double v1 = 0.0, double v2=360.0)
{
	if(v < v1) return v += 360.0;
	if(v > v2) return v -= 360.0;
	return v;
};

#ifdef _OPENMP
int set_openmp_parallel_process_num(int nproc=0, bool force=false);
int set_openmp_parallel_process_num(bool silent, int nproc=0, bool force=false);
#endif


#ifndef WIN32
//get total free memory in MB
float get_free_memory();
#endif

double angle_mean(double *angles, int nangles, int istep = 1);
double angle_mean(double angle1, double angle2);
double angle_diff(double angle1, double angle2);
bool is_angle_equal(float angle1,   float angle2, double epsilon = 1.0e-6 );
bool is_angle_equal(double angle1, double angle2, double epsilon = 1.0e-12);

#endif //end of this file
