/**
 * @class nReadValues
 * file name: nReadValues.cpp
 * @author Betti Oesterholz
 * @date 27.02.2006
 * @mail webmaster@bernd-oesterholz.name
 *
 * System: C++
 *
 * This class represents a basic univers.
 * Copyright (C) @c GPL3 2008  Betti Oesterholz
 *
 * 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 3 of the License, or
 * 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, see <http://www.gnu.org/licenses/>.
 *
 * @description:
 * This class is for reading values, like numbers or path, from an 
 * parameter file.
 */
/*
History:
11.01.2009  Oesterholz  refactoring for doxygen
*/


#include "cstring"
#include "cstdlib"
#include "nReadValues.h"

/**
 * makes the char c to a integer number c represents
 * @param  cDigit the character to convert
 * @return the integer number the char c represents (e.g. "1"=1) or -1 if it
 *		isn't a number
 */
int nReadValues::charToInt(const char cDigit){
	switch (cDigit){
		case '0':return 0;
		case '1':return 1;
		case '2':return 2;
		case '3':return 3;
		case '4':return 4;
		case '5':return 5;
		case '6':return 6;
		case '7':return 7;
		case '8':return 8;
		case '9':return 9;
	}
	return -1;//error
}

/** reads a character substring from the given stream ifstrInput that
 * is terminated with ':' or ';'
 * @param ifstrInput the input stream to read the char string from
 * @return the readed string terminated with 0;
 *		or 0 if a error occurs
 */
char* nReadValues::readChar( ifstream &ifstrInput ){
	unsigned int iCount=0;
	char szReadedString[10000];
	char cInput;
	
	do{
		cInput=ifstrInput.get();
		szReadedString[iCount]=cInput;
		iCount++;
	}while ( (iCount<1000) && (cInput!=':') && (cInput!=';') );
	
	if (iCount>=1000){//error
		return NULL;
	}
	//crate new return string
	char* szReturnString=new char[iCount+1];
	strncpy( szReturnString, szReadedString, iCount );
	szReturnString[iCount-1]=0;//replace terminating character (last character) with 0
	
	return szReturnString;
}


/**
 * reads a path from the given stream ifstrInput;
 * the string for the path should be terminated with ':' or ';'
 * (this character will be deleted);
 * if pathE is true the path will be terminated with '/' (the method 
 * insert '/' at the end of the path if it needs to)
 * @param ifstrInput the input stream to read the path from
 * @param pathE if the path should be terminated with '/'
 * @return the readed path string terminated with 0;
 * 	or NULL if a error occured
 */
char* nReadValues::readPath( ifstream &ifstrInput, bool pathE ){
	unsigned long maxC=0;
	/*read the path string*/
	char* path=readChar( ifstrInput );
	
	if ( path==NULL ){//Error while read
		return NULL;
	}
	
	unsigned int count=0,blanks=0;
	bool beg=true;
	for ( ; (maxC<100000) && (path[count+blanks]!=0) ; maxC++ ){
		//maxC= maximal chars to read; to avoid infinity loops
		if ( beg ){
			//delete begining blanks
			if ( path[count+blanks]==' ' ){
				blanks++;
			}else{
				beg=false;
				path[count]=path[count+blanks];
				count++;
			}
		}else{
			path[count]=path[count+blanks];
			count++;
		}
	}
	path[count]=0;
	if( pathE && (count>0) ){
		//add path seperator to the end of the path
		if ( (path[count-1]!=PATH_SEPERATOR) && (path[count-1]!=NOT_PATH_SEPERATOR) && (count>1) ){
			path[count]=PATH_SEPERATOR;
			path[count+1]=0;
			count++;
		}
	}
	for ( unsigned int i=0 ; i<=count ; i++ ){
		//change Windows '\' to Linux '/' if linux, else other way around
		if ( path[i]==NOT_PATH_SEPERATOR ){
			path[i]=PATH_SEPERATOR;
		}
	}
	if ( maxC>=100000 ){//error
		delete path;
		return NULL;
	}
	return path;
}

/**
 * reads a double number from the given inputstream;
 * the number string should be terminated with ':' or ';',
 * it will be read till (inclusive) ther terminating character
 * @param ifstrInput the input stream to read the double number from
 * @return the readed double number
 */
double nReadValues::readDouble( ifstream &ifstrInput ){
	unsigned int maxC=0;//maximal chars to read; to avoid infinity loops
	double dnumb=0.0;
	int count=0,exp=0;
	bool sign=false;
	char c;
	
	do{//read till number
		ifstrInput>>c;
		maxC++;
	}while ( (maxC<10000) && (charToInt(c)==-1) && (c!='-') && (c!=';') );
	
	maxC=0;
	if ( c=='-' ){
		//negativ sign readed
		sign=true;
		ifstrInput>>c;
	}
	if ( charToInt(c)!=-1 ){
		dnumb=charToInt( c );
		for ( ; maxC<1000 ; maxC++ ){
			ifstrInput>>c;
			if ( charToInt(c)==-1 ){
				break;
			}
			dnumb= dnumb*10 + charToInt( c );
		}
		if ( c=='.' ){
			for ( ; maxC<1000 ; maxC++ ){
				ifstrInput>>c;
				if ( charToInt(c)==-1 ){
					break;
				}
				dnumb= dnumb*10 + charToInt( c );
				count++;
			}
		}
		if (sign){
			dnumb=-dnumb;
			sign=false;
		}
		if ( (c=='e') || (c=='E') || (c=='^') ){
			//read exponent
			ifstrInput>>c;
			if ( c=='-' ){
				sign=true;
			}else{
				sign=false;
				if ( charToInt(c)!=-1 ){
					exp=charToInt(c);
				}
			}
			for ( ; (maxC<1000) && (c!=';') ; maxC++ ){
				ifstrInput>>c;
				if ( charToInt(c)==-1 ){
					break;
				}
				exp= exp*10 + charToInt(c);
			}
			if ( sign ){
				exp=-exp;
			}
		}
		exp-=count;
		if ( exp>=0 ){
			for ( ; exp>0 ; exp-- ){
				dnumb=dnumb*10;
			}
		}else{
			for ( ; exp<0 ; exp++ ){
				dnumb=dnumb/10;
			}
		}
	}
	for ( ; (maxC<10000) && (c!=';') ; ifstrInput>>c , maxC++ );//read till line end
	return dnumb;
}

/**
 * reads a integer number from the given inputstream;
 * the number string should be terminated with ':' or ';',
 * it will be read till (inclusive) ther terminating character
 * @param ifstrInput the input stream to read the integer number from
 * @return the readed integer number
 */
long nReadValues::readInt( ifstream &ifstrInput ){
	unsigned int maxC=0;//maximal chars to read; to avoid infinity loops
	long dnumb=0;
	bool sign=false;
	char c;
	do{
		ifstrInput>>c;
		maxC++;
	}while( (maxC<10000) && (charToInt(c)==-1) && (c!='-') );
	
	maxC=0;
	if ( c=='-' ){
		sign=true;
		ifstrInput>>c;
	}
	if  (charToInt(c)!=-1 ){
		dnumb=charToInt(c);
		for( ; maxC<1000 ; maxC++ ){
			ifstrInput>>c;
			if (charToInt(c)==-1){
				break;
			}
			dnumb= dnumb*10 + charToInt(c);
		}
		if (sign){
			dnumb=-dnumb;
			sign=false;
		}
	}
	for( ; (maxC<10000) && (c!='.') && (c!=';')&&(c!=':') ; ifstrInput>>c , maxC++ );//read till line end
	return dnumb;
}


/**
 * reads a truth value from the given inputstream;
 * strings for true are: t, true, y, yes, w, wahr, j, ja, 1
 * all other strings are readed as false;
 * all strings can be writen in upper cases, just the first char of the words
 * are checked;
 * the truth value string should be terminated with ':' or ';',
 * it will be read till (inclusive) ther terminating character
 * @param ifstrInput the input stream to read the truth value from
 * @return  the readed truth value
 */
bool nReadValues::readBool( ifstream &ifstrInput ){
	char c;
	unsigned int maxC=0;//maximal chars to read; to avoid infinity loops
	bool value=false;
	
	do{
		ifstrInput>>c;
		maxC++;
	}while( (maxC<10000) && (c!=';') && (c!='t') && (c!='y') && (c!='w') && (c!='j') && (c!='1') );
	
	if ( (c=='t') || (c=='y') || (c=='w') || (c=='j') || (c=='1') ){
		value=true;
	}
	for( ; (maxC<10000) && (c!=';') ; ifstrInput>>c  ,maxC++ );//read till line end
	return value;
}

/**
 * reads a time period from the given inputstream;
 * the period string should be terminated with ':' or ';',
 * it will be read till (inclusive) ther terminating character
 * @param ifstrInput the input stream to read the time period from
 * @return the readed time period as a time_t
 */
time_t nReadValues::readPeriod( ifstream &ifstrInput ){
	unsigned int maxC=0;//maximal chars to read; to avoid infinity loops
	long dnumb=0;
	bool sign=false;
	char c;
	
	do{//read chars in front 
		ifstrInput>>c;
		maxC++;
	}while( (maxC<10000) && (charToInt(c)==-1) && (c!='-') );
	
	maxC=0;
	if ( c=='-' ){//read possible sign
		sign=true;
		ifstrInput>>c;
	}
	if ( charToInt(c)!=-1 ){//read numbers
		dnumb=charToInt(c);
		for( ; maxC<1000 ; maxC++ ){
			ifstrInput>>c;
			if ( charToInt(c)==-1 ){
				break;
			}
			dnumb=dnumb*10 + charToInt(c);
		}
		for( ; maxC<1000 ; maxC++ ){//read following blanks
			if( c==' ' ){
				ifstrInput>>c;
			}else{
				break;
			}
		}
		switch (c){
			case 'm'://minutes
			case 'M'://minutes
				dnumb*=60;
			break;
			case 'h'://hours
			case 'H'://hours
			case 's'://hours
			case 'S'://hours
				dnumb*=60*60;
			break;
			case 'd'://days
			case 'D'://days
			case 't'://days
			case 'T'://days
				dnumb*=60*60*24;
			break;
		}
		if (sign){
			dnumb=-dnumb;
			sign=false;
		}
	}
	for( ; (maxC<10000) && (c!='.') && (c!=';') && (c!=':') ; ifstrInput>>c  ,maxC++ );//read till line end
	return (time_t)(dnumb);
}

/**
 * checks if a file for the given path exists and can be opened
 * @param path a string for the path to be cheked
 * @return true if the path exists, else false
 */
bool nReadValues::checkPath( char* path ){

	if  ( path==NULL ){
		return false;
	}
	ifstream test( path );
	if( test ){
		test.close();
		return true;
	}
	return false;
}

/**
 * if the path folders are not existing, this function creats them
 * @param path the string of the path that should be existing
 */
void nReadValues::cratePath(char* path){

	unsigned long maxC=0,count=0,count2=0;//maxC= maximal chars to read; to avoid infinity loops
	if  ( path==NULL ){
		return;
	}
	//begin with the first folder, check if the folder exists and if not creat it
	char folder[1000],mkDir[1020];
	for ( ; (maxC<100000) && (path[count]!=0) ; maxC++ ){
		//check next folder
		for ( ; (maxC<100000) && (path[count]!=0) ; maxC++ ){
			//read folder from path
			if ( path[count]==PATH_SEPERATOR){
			
				folder[count2]=PATH_SEPERATOR;
				count++;
				count2++;
				break;
			}else{
				folder[count2]=path[count];
				count++;
				count2++;
			}
		}
		folder[count2]=0;
		if ( (count2!=0) &&
				!( (count2==1) && (folder[0]==PATH_SEPERATOR) ) &&
				!( (count2==2) && (folder[0]=='.') && (folder[1]==PATH_SEPERATOR) )
				&& !(checkPath(folder)) 
				){
			// if in folder stands a valid path
			sprintf( mkDir, "mkdir %s ", folder );
			system( mkDir );
		}
		if ( path[count]==0 ){
			return;
		}
	}
}

/**
 * searches in a tabel for the line with the given number or the next 
 * grater number in front and lets the fstream point to the end of the 
 * line;
 * the rows in the tabel must end with a ";";
 * if no data is in the file, the pointers of the stream are set to the 
 * file begin;
 * if ther is data in the file, but not front number greater or equal 
 * the given number, the file pointers are set to the end of the file
 * @param ifstrInput the stream str to search in
 * @param lNumber the number to search for 
 * @param uiRows the number of rows in the tabel in the stream
 * @return true if the number was found and the stream output pointer 
 * 	pointing to the end of the line wich begins with number, or false
 */
bool nReadValues::searchForLine( fstream *ifstrInput, long lNumber, unsigned int uiRows ){
	unsigned long ulLine=0 , r ;
	long lNumb = -2000000000;
	char c;
	ifstrInput->seekg(0);//set input position to file begin
	
	for( ; !(ifstrInput->eof()) ; ulLine++ ){
		//read next line
		if ( ulLine ) {
			/*don't read number when the line is the tabel head*/
			(*ifstrInput)>>lNumb;
		}
		for ( r=0 ; r<uiRows ; ){
			//read the rest of the line
			c=0; 
			for( ; ( c!=';' ) && !( ifstrInput->eof() ) ; ){
				//read till next ';'
				(*ifstrInput)>>c;
			}
			r++;
		}
		if( lNumb >= lNumber ){
		
			ifstrInput->clear();
			ifstrInput->seekp( ifstrInput->tellg() );//set outpout position to input position
			return true;
		} else if ( (ulLine>=2) && (ifstrInput->eof()) ){
			/*some datalines are readed, but end of file reached before the correct number;
			set outputposition to the end of file (append next data after a free line)*/
			ifstrInput->clear();
			ifstrInput->seekp( ifstrInput->tellg() );//set outpout position to input position
			return true;
		}
	}
	ifstrInput->clear();
	ifstrInput->seekp(0);//set outposition to file begin
	ifstrInput->seekg(0);//set input position to file begin
	return false;
}





















