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

//#include <iostream>
#ifdef WIN32
#include <direct.h>
#include  <io.h>
#else
#include <dirent.h>
#include <unistd.h>
#endif

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

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdio>
#include <cmath>
#include <cctype>    /*tolower()*/
#include <sys/types.h>
#include <sys/stat.h>
#include <algorithm>
#include <vector>
#include "mystds.h"

/* show Error or Warnings*/
FILE *fpLog=NULL;
bool openLog(const char *szFile, const char *mode)
{
	fpLog = fopen(szFile, mode);
	if(fpLog==NULL)	return false;
	return true;
}

void closeLog()
{
	if(fpLog==NULL)	fclose(fpLog);
	fpLog = NULL;
}

void ShowWarn(const char *szText, FILE *out)
{
#ifndef _WINDOWS
	fprintf(out, "%s", szText); //same as: printf("%s",szText);
#endif
	if(fpLog != NULL)
		fprintf(fpLog, "%s", szText);
}

bool is_float_equal(float a, float b, double epsilon)
{
	return is_float_equal((double)a, (double)b, epsilon);
}

bool is_float_equal(double a, double b, double epsilon)
{
	if (a == b) return true;

	double dif = fabs(a - b);
	return (dif < epsilon);
	
	//if (a == 0 || b == 0) return (dif < epsilon);
	
	//double err = epsilon * max(fabs(a), fabs(b));
	//return (dif < err);
}

bool is_float_equal(struct dPoint2D a, struct dPoint2D b)
{

	return (is_float_equal(a.x, b.x) && is_float_equal(a.y, b.y));
}

/*change string to lower case which is like strlwr in MS C*/
char* str2lwr(char *cbin, unsigned int length)
{
	if(cbin==NULL) return 0;
	
	size_t iLen=strlen(cbin);
	if (iLen > length) iLen = length;

	for(unsigned int i=0; i<iLen; i++) 
		cbin[i] = tolower(cbin[i]);
	
	return cbin;
}

string str2lwr(string &strin)
{
	transform(strin.begin(), strin.end(), strin.begin(), ::tolower);

	return strin;
}

string *strTrim(string &str)
{
	char const* delims = " \t\r\n";

	// trim leading whitespace
	string::size_type  notwhite = str.find_first_not_of(delims);
	str.erase(0, notwhite);

	// trim trailing whitespace
	notwhite = str.find_last_not_of(delims);
	str.erase(notwhite+1);
	return &str;
}

char *strTrim(char *cbIn)
{
	//trim blanks from right to left
	int i;
	for(i=(int)strlen(cbIn)-1; i>=0; i--)
	{
		if(iswgraph(cbIn[i])) 
			break; 
		cbIn[i]=0;
	}

	int iLen = (int)strlen(cbIn);
	char *pcbTmp = new char[iLen+1];
	char *pcb = pcbTmp;
	sprintf(pcbTmp,"%s",cbIn);

	//trim blanks from left to right
	for(i=0;i<iLen;i++)
	{
		if(iswgraph(cbIn[i])) 
			break; 
		pcb++;
	}
	if(pcb!=pcbTmp)
		sprintf(cbIn,"%s",pcb);

	delete []pcbTmp;
	return cbIn;
}

// replace chars in a string
//     e.g., ("geophysics", "eo", '_'): 'e' and 'o' are replaced by '_'
string *strReplace(string &strIn, const char * cbs, char cb)
{
	char * sz = new char[strIn.length() + 1];
	strcpy(sz, strIn.c_str());
	strReplace(sz, cbs, cb);
	strIn = sz;

	delete []sz;
	return &strIn;
}

// replace chars in a string
//     e.g., ("geophysics", "eo", '_'): 'e' and 'o' are replaced by '_'
char *strReplace(char *szIn, const char * cbs, char cb)
{
	char *pcb = strpbrk(szIn, cbs);
	while(pcb != NULL)
	{
		*pcb = cb;
		pcb = strpbrk(pcb+1, cbs);
	}
	return szIn;
}

//fill blanks in the end of the string
char * c_str2fortran_str(char * str, int length)
{
    for(int i=strlen(str); i < length; i++)
       str[i]=' ';
	return str;
}

//cut the blanks (set C-string end) in the end of the string
char * fortran_str2c_str(char * str, int length)
{
	for(int i=length-1; i>=0; i--)
	{
		if(str[i]==' ')
			str[i]=0;
		else
			break;
	}
	return str;
}

//split a string into a vector
int str_split(string astr, vector<string> &v, const char * delim)
{
	string tmp;

	for (string::const_iterator i = astr.begin(); i < astr.end(); i++)
	{
		const char* stmp = strchr(delim, *i);
		if(stmp == NULL) 
			tmp += *i;

		if ((stmp != NULL || (i+1) == astr.end()) && tmp.size()>0)
		{
			v.push_back(tmp);
			tmp = "";
		}
	}

	return v.size();
}

//split a string into a list
int str_split(string astr, list<string>& v, const char* delim)
{
	string tmp;

	for (string::const_iterator i = astr.begin(); i < astr.end(); i++)
	{
		const char* stmp = strchr(delim, *i);
		if (stmp == NULL)
			tmp += *i;

		if ((stmp != NULL || (i + 1) == astr.end()) && tmp.size() > 0)
		{
			v.push_back(tmp);
			tmp = "";
		}
	}

	return v.size();
}

int LookUpBlock(FILE *fp, const char *szBlock)
{
	if(fp == NULL) return 0;
	char szTmp[256];
	sprintf(szTmp,"%s",szBlock);

	str2lwr(szTmp);
	char cbLine[1024];
	while(!feof(fp))
	{
    	if(fgets(cbLine,1024, fp) == NULL) continue;
		str2lwr(cbLine);
    	if(strncmp(cbLine,szTmp,strlen(szTmp))==0)
    		return 1;
	}
	return 0;
}

int LookUpBlock(ifstream &fin, const char *szBlock)
{
	if(!fin.rdbuf()->is_open()) return 0;
	char szTmp[256];
	sprintf(szTmp,"%s",szBlock);

	str2lwr(szTmp);
	char cbLine[1024];
	while(!fin.eof())
	{
    	fin.getline(cbLine, 1024);
    	if(fin.fail())return 0;
		str2lwr(cbLine);
    	if(strncmp(cbLine,szTmp,strlen(szTmp))==0)
    		return 1;
	}
	return 0;
}

int copyFile(const char *fileSrc, const char* fileDes, int iWarn)
{
	int iret=0;
	FILE *fpdes=NULL, *fpsrc=fopen(fileSrc,"rb");
	if(fpsrc==NULL) goto endline;

	char cbLine[512], *cbp;
	if((cbp=(char*)strrchr(fileDes, CBDIR)) != NULL)
	{
		strncpy(cbLine, fileDes, cbp - fileDes);
		cbLine[cbp-fileDes]=0;
		if(!isDir(cbLine))
			CreateDir(cbLine);
	}

	fpdes=fopen(fileDes,"wb");
	if(fpdes==NULL) goto endline;

	size_t size;
	while(!feof(fpsrc))
	{
		size=fread(cbLine,1,512,fpsrc);
		if(fwrite(cbLine,1,size,fpdes)!=size) goto endline;
	}
	iret=1;	

endline:
	if(iret==0&&iWarn==1) 
		SHOW2("Can not copy \"%s\" to \"%s\"\n",fileSrc, fileDes);

	if(fpsrc != NULL) fclose(fpsrc);
	if(fpdes != NULL) fclose(fpdes);

	return iret;
}

//open a FILE like C
FILE *openfile(const char * szDir, const char * szFileName, const char * mode, bool bWarn, bool bExit)
{
	char szName[MAX_PATH_LENGTH];
	strcpy(szName, szDir);
	if( !isDir(szName) ) return NULL;

	strcat(szName, szFileName);
	return openfile(szName, mode, bWarn, bExit);
}

FILE *openfile(const char * szFileName, const char * mode, bool bWarn, bool bExit)
{
	if(szFileName == NULL)
	{
		SHOW("you have not set the file name!\n");
		exit(0);
	}

	FILE * file = fopen(szFileName, mode);
	if(file==NULL)
	{
		if(bWarn) 
			SHOW1("%s can not be opened!\n",szFileName);
		if(bExit) exit(0);
	}
	return file;
}

//open a (i/o)fstream
bool openfile(ifstream *file, const char * szFileName, int mode, bool bWarn, char iosf)
{
	return openfile((fstream *)file, szFileName, mode, bWarn, iosf);
}

bool openfile(ifstream *file, const char * szDir, const char * szFileName, int mode, bool bWarn, char iosf)
{
	char szName[MAX_PATH_LENGTH];
	sprintf(szName, "%s%s", szDir, szFileName);
	return openfile((fstream *)file, szName, mode, bWarn, iosf);
}

bool openfile(ofstream *file, const char * szFileName, int mode, bool bWarn, char iosf)
{
	return openfile((fstream *)file, szFileName, mode, bWarn, iosf);
}

bool openfile(ofstream *file, const char * szDir, const char * szFileName, int mode, bool bWarn, char iosf)
{
	char szName[MAX_PATH_LENGTH];
	sprintf(szName, "%s%s", szDir, szFileName);
	return openfile((fstream *)file, szName, mode, bWarn, iosf);
}

bool openfile(fstream *file, const char * szDir, const char * szFileName, int mode, bool bWarn, char iosf)
{
	char szName[MAX_PATH_LENGTH];
	sprintf(szName, "%s%s", szDir, szFileName);
	return openfile(file, szName, mode, bWarn, iosf);
}

bool openfile(fstream *file, const char * szFileName, int mode, bool bWarn, char iosf)
{
	file->clear();

	if(szFileName == NULL)
	{
		SHOW("you have not set the file name!\n");
		exit(0);
	}

	if(iosf == 'i')
		((ifstream*)file)->open(szFileName, (ios_base::openmode)mode);
	else if(iosf == 'o')
		((ofstream*)file)->open(szFileName, (ios_base::openmode)mode);
	else
	{
		file->open(szFileName, (ios_base::openmode)mode);

		if(!(*file))
		{
			file->clear();
			file->open(szFileName, (ios_base::openmode)(mode | ios_base::trunc));
		}
	}

	if(!(*file))
	{
		if(bWarn) 
			SHOW1("%s can not be opened!\n", szFileName);
		return false;
	}

	file->clear();
	return true;
}

/*
   Convert data between Sun (ieee) format to PC format
   modified from ieee2intel in program of Herrmann
*/
int Sun2PC( char *cbData, int nSize, int ibyte)
/*	cbData:	address of the data.
	nSize:	array size of the data.
	ibyte:	varible byte size. eg.
	
	example: double aa[100]; ...; Sun2PC((char*)aa,100,sizeof(double));
*/
{
	char cb[32];
	int i,j;
	if(ibyte>32)
	{
		SHOW("varible byte is more than 32");
		return 0;
	}
	for (i=0; i < nSize*ibyte; i+=ibyte )
	{
		for(j=0;j<ibyte;j++)
			cb[j]=cbData[i+j];
		for(j=0;j<ibyte;j++)
			cbData[i+j]=cb[ibyte-j-1];
	}
	return 1;
}

//check if it is a directory
bool isDir(string &strDir)
{
	if(strDir.length() < 1)	strDir = "."; //or getcwd to set currect directory

	//check and add CBDIR in the last.
	if( (*strDir.rbegin()) != CBDIR ) //directory name should be ended by CBDIR ('/' or '\')
	{
		;//strDir.insert( strDir.end(), CBDIR );
	}
	else 
		strDir.erase( strDir.end()-1 ); //remove the last CBDIR ('/' or '\') which is for WIN32

	//check if it is directory
	struct stat fst;
	bool ret = false; 
#ifndef WIN32
	//check home symbol in linux/unix
	if(strDir[0] == '~')
	{
		char *pHome = getenv("HOME");
		if(pHome != NULL)
			strDir = string(pHome) + strDir.substr(1);
	}

	if(lstat(strDir.c_str(), &fst) == 0)
	{
		if(S_ISDIR(fst.st_mode)) ret = true;
	}
#else
	if(stat(strDir.c_str(), &fst) == 0)
	{
		if(_S_IFDIR & fst.st_mode)  ret = true;
	}
#endif

	strDir.insert( strDir.end(), CBDIR );
	return  ret;
}

//check if it is a directory
bool isDir(char *szDir)
{
	if(szDir == NULL) return false;

	std::string strDir = szDir;
	bool ret = isDir(strDir);
	strcpy(szDir, strDir.c_str());

	return ret;
}

int CreateDir(char *szDir)
{
	if(strlen(szDir)<1) return -1;

#ifndef WIN32
	if(mkdir(szDir,DIRRIGHT)==0) 
	{
		chmod(szDir,DIRRIGHT);
#else
	if(_mkdir(szDir)==0) 
	{
#endif
		return 0;
	}
		
	char *cbp = strrchr(szDir,CBDIR);
	if(cbp==NULL)return -1;
		
	char szDirTemp[MAX_PATH_LENGTH];
	strncpy(szDirTemp, szDir,cbp-szDir);
	szDirTemp[cbp-szDir]=0;
	if(CreateDir(szDirTemp)!=-1)
	{
		if(cbp[0]==CBDIR&&cbp[1]==0) return 0;
		
#ifndef WIN32
		if(mkdir(szDir,DIRRIGHT)!=0) return -1;
		chmod(szDir,DIRRIGHT);
#else
		if(_mkdir(szDir)!=0) return -1;
#endif
		return 0;	
	}	

	return -1;
}

int remove_file_dir(const char* szFile)
{
	remove(szFile);
	char *cp=(char *)strrchr(szFile,CBDIR);
	if(cp != NULL)
	{
		char cbdir[MAX_PATH_LENGTH];
		strncpy(cbdir, szFile, cp - szFile);
		cbdir[cp-szFile]=0;
#ifndef WIN32
		rmdir(cbdir);
#else
		_rmdir(cbdir);
#endif
	}

	return 1;
}

int sizeofFile(const char *szFile)
{
    if(szFile == NULL || strlen(szFile)<1) return 0;

	struct stat fst;
	if(stat(szFile, &fst)==0)
	{
#ifndef WIN32
		if(!S_ISDIR(fst.st_mode))
#else
		if(!(_S_IFDIR & fst.st_mode))
#endif
			return fst.st_size;
	}

	return 0;
}

//check if it is a file
bool isFile(string strFile)
{
	if(strFile.length() < 1) return false;

	//check if it is file
	struct stat fst;
#ifndef WIN32
	//check home symbol in linux/unix
	if(strFile[0] == '~')
	{
		char *pHome = getenv("HOME");
		if(pHome != NULL)
			strFile = string(pHome) + strFile.substr(1);
	}

	if(lstat(strFile.c_str(), &fst) == 0)
	{
		if(S_ISREG(fst.st_mode))
			return true;
	}
#else
	if(stat(strFile.c_str(), &fst) == 0)
	{
		if(_S_IREAD & fst.st_mode)
			return true;
	}
#endif

	return false;
}

//check if it is a file
bool isFile(char *szFile)
{
	if(szFile == NULL) return false;

	return isFile(string(szFile));
}

char* get_ith_column(const char *Src, char *szColumn, int ith, const char *szToken)
{
	//Notice: the first element has a position of 1, not 0.
	char szTmp[1024];
	if(strlen(Src) > 1023) sprintf(szTmp, "%1023s", Src);
	else strcpy(szTmp, Src);
	
	char *token = strtok( szTmp, szToken);
	for(int i=1; i< ith && token != NULL; i++)
	{
		token = strtok( NULL, szToken );
	}

	if(token != NULL)
	{
		strcpy(szColumn, token);
		return szColumn;
	}

	return NULL;
}

int get_Tokens(char *Src, const char *szToken)
{
	char szTmp[1024];
	if(strlen(Src) > 1023) sprintf(szTmp, "%1023s", Src);
	else strcpy(szTmp, Src);
	
	int ntoken = 0;
	for(char *token = strtok( szTmp, szToken); token != NULL && ntoken < 1024; )
	{
		ntoken ++;
		token = strtok( NULL, szToken );
	}

	return ntoken;
}

int getTokens(char *sz, char cb)
{
	int iret=0;
	while((sz=strchr(sz,cb)) != NULL)
	{
		iret++;
		sz++;
	}
	return iret;
}

double getDistance(double *point1, double *point2, int dimension)
{
	double dist = 0.0;
	for(int idim = 0; idim < dimension; idim++)
		dist += pow(point1[idim] - point2[idim], 2);
	return sqrt(dist);  //pow(dist, 0.5);	
}

//Check existence of an executive program
bool CheckExePrograms(const char *szExe)
{
	const char *pDelimit, *pc1;
#ifdef WIN32
	pDelimit = ";";
	pc1      = "\\";
	if(_access(szExe,4)==0) return true;
#else
	pDelimit = ":";
	pc1      = "/";
	if(access(szExe,4)==0) return true;
#endif

	//search joint96 in all searched paths.
	bool bFound = false;
	char *token, paths[1024], file[256];
	char *pdir=getenv("PATH");
	if(pdir==NULL)
	{
		SHOW("Cannot get the value of PATH envirement varible. \n"
		       "\tThis can be caused by using setenv/putenv anywhere in this program. \n");
		return false;
	}

	sprintf(paths,"%s",pdir);
	token = strtok(paths, pDelimit);
	while(token != NULL )
	{
		long iend = (long)strlen(token) - 1;
		if(token[(iend>=0)?iend:0] != pc1[0])
			sprintf(file,"%s%s%s",token, pc1, szExe);
		else
			sprintf(file,"%s%s",token, szExe);

#ifndef WIN32
		if(access(file,4)==0)
#else
		if(_access(file,4)==0)
#endif
		{
			//the joint96 is found.
			bFound = true;
			break;
		}

		/* Get next token: */
		token = strtok( NULL, pDelimit );
	}

	//do not find joint96
	if(!bFound)
	{
		SHOW1("\"%s\" cannot be found in all searched paths!\n", szExe);
		return false;
	}
	return true;
}

//get parameters from arguments
//for a argument: -name="a bc"; then: string name="a bc"
string get_Parameter_str(const char* sz_param_switch, string vDefault, int argc, char** argv)
{
	string 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)
			return string(argv[i] + ilen);
	}

	return value;
}

string get_Parameter(const char* sz_param_switch, string vDefault, int argc, char** argv)
{
	return get_Parameter_str(sz_param_switch, vDefault, argc, argv);
}

int get_Parameter(const char* sz_param_switch, int vDefault, int argc, char** argv)
{
	return get_Parameter_value(sz_param_switch, vDefault, argc, argv);
}

long get_Parameter(const char* sz_param_switch, long vDefault, int argc, char** argv)
{
	return get_Parameter_value(sz_param_switch, vDefault, argc, argv);
}

float get_Parameter(const char* sz_param_switch, float vDefault, int argc, char** argv)
{
	return get_Parameter_value(sz_param_switch, vDefault, argc, argv);
}

double get_Parameter(const char* sz_param_switch, double vDefault, int argc, char** argv)
{
	return get_Parameter_value(sz_param_switch, vDefault, argc, argv);
}

bool get_Parameter(const char* sz_param_switch, bool vDefault, int argc, char** argv)
{
	return get_Parameter_value(sz_param_switch, vDefault, argc, argv);
}

bool get_switch(const char* sz_param_switch, bool vDefault, int argc, char** argv)
{
	bool value = vDefault;
	for(int i=1; i<argc; i++)
	{
		if(strcmp( sz_param_switch, argv[i]) == 0)
		{
			value = true;
			break;
		}
	}
	
	return value;
}

#ifdef _OPENMP
int set_openmp_parallel_process_num(int nproc, bool force)
{
	return set_openmp_parallel_process_num(false, nproc, force);
}

int set_openmp_parallel_process_num(bool silent, int nproc, bool force)
{
	if(nproc <= 0 && force == false)
	{
		int ncore = omp_get_num_procs(); //get the number of cores
		nproc = (ncore >2) ? (ncore - 1):1;
	}

	if(force==true || nproc>1)
	{
		omp_set_num_threads( nproc );
		if(silent == false) 
			fprintf(stderr, "Notice: This program is running in parallel mode and using %d threads!!\n", nproc);
	}

	return nproc;
}
#endif

#ifndef WIN32
float get_free_memory()
{
	float free_memory = 0.0f; //units: kbyte
	FILE *fmem = NULL;
	if( ( fmem = popen( "top -n 1 | grep Mem | awk '{print $6}'", "rt" )) == NULL )
		return free_memory;

	fscanf(fmem, "%f", &free_memory);

	pclose(fmem);
	return free_memory/1000;
}
#endif

double angle_mean(double *angles, int nangles, int istep)
{
	if(nangles < 1)
		return 0.0;

	double sin_sum=0.0, cos_sum=0.0;
	for(int j=0; j < nangles; j += istep)
	{
		double angle = D2A( *(angles+j) );
		sin_sum += sin( angle );
		cos_sum += cos( angle );
	}

	//如何求取角度的平均，参见http://en.wikipedia.org/wiki/Mean_of_circular_quantities
	double mean=A2D( atan2(sin_sum/nangles, cos_sum/nangles) );
	return mean;
}

double angle_mean(double angle1, double angle2)
{
	double angles[2] = {angle1, angle2};

	return angle_mean(angles, 2);
}

double angle_diff(double angle1, double angle2)
{
	double diff = angle1 - angle2;

	if (diff >= 360.0 || diff <= -360.0) diff = fmod(diff, 360.0);

	// output is between 180 and -180
	if(diff >  180.0) diff -= 360.0;
	if(diff < -180.0) diff += 360.0;

	return diff;
}

bool is_angle_equal(float angle1, float angle2, double epsilon)
{
	return is_angle_equal((double)angle1, (double) angle2, epsilon);
}

bool is_angle_equal(double angle1, double angle2, double epsilon)
{
	double dif = fmod(fabs(angle1 - angle2), 360.0);
	if (dif > 180.0) dif = 360.0 - dif;

	return is_float_equal(dif, 0.0, epsilon);
}
