/***************************************************************************

    supportware
    Copyright (C) 2001-2008 by c.holzheuer
    c.holzheuer@sourceworx.org

    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.

***************************************************************************/


#include <ddstring.h>
#include <stdexcept>
#include <ddbaseobject.h>
#include <ddiommap.h>
#include <stdio.h>
#include <cstring>
#include <algorithm>

using namespace std;



/**
	Default constructor.
*/


ddstring::ddstring()
{

}


/**
	Copy constructor.
*/

ddstring::ddstring( const ddstring& src )
: string( src )
{
	
}


/**
	Assignment operator. 
*/

ddstring& ddstring::operator=( const ddstring& src )
{
	if( this == &src )
		return *this;
	assign( src );
	return *this;
}


/**
	Copy constructor.
*/

ddstring::ddstring( const string& src )
: string( src )
{
	
}


/**
	Copy constructor.
*/

ddstring::ddstring( const char* src )
: string( src )
{

}


/**
	Copy constructor.
*/

ddstring::ddstring( size_t len, char entry )
: string( len, entry )
{

}


/**
	Copy constructor.
*/

ddstring::ddstring( const char* entry, size_t len )
: string( entry, len )
{

}


/**
	Destructor.
*/

ddstring::~ddstring()
{

}


/**
	Assignment operator.
*/

ddstring& ddstring::operator=( const string& src )
{
	if( this == &src )
		return *this;
 	assign( src );
 	return *this;
}


/**
	Assignment operator.
*/

ddstring& ddstring::operator=( const char* src )
{
 	assign( src );
 	return *this;
}


/**
	wild: in zahl umwandeln, erhöhen und als string speichern
*/
ddstring& ddstring::operator++( int )
{
	from_int( as_int() + 1 );
 	return *this;
}


/**
	wild: in zahl umwandeln, decrementieren und als string speichern
*/

ddstring& ddstring::operator--( int )
{
	from_int( as_int() - 1 );
 	return *this;
}


/**
	Konvertierung von Bool. Ergibt "true" bzw. "false".
*/

ddstring& ddstring::from_bool( bool num )
{
	assign( num ? DDSTRTRUE : DDSTRFALSE );
	return *this;
}


/**
	Konvertierung von Int.
*/

ddstring& ddstring::from_int( int num )
{
  stringstream converter;
	converter << num;
	converter >> *this;
	return *this;
}


/**
	Konvertierung von unsigned int.
*/

ddstring& ddstring::from_size_t( size_t num )
{
	stringstream converter;
	converter << num;
	converter >> *this;
	return *this;
}


/**
	Konvertierung von double.
*/

ddstring& ddstring::from_double( double num, int precision )
{
	stringstream converter;
  converter.precision( precision );
	converter << num;
	converter >> *this;
	return *this;
}


/**
	Liest den Inhalt einer Datei via mmap in diesen String ein.
	@param filename der Pfad
	@return -1 bei Fehler, sonst die Dateigröße.
	@see ddiommap
*/

int ddstring::from_file( const ddstring& filename )
{
	ddiommap src;
	int res = src.mmap( filename );
	if( res>-1 )
		assign( src.data(), src.max_output() );
  return res;
}



/**
	Konvertierung nach Bool: true falls der String "true" ist, 
	sonst false.
*/

bool ddstring::as_bool() const
{
	return ( *this == DDSTRTRUE ? true : false );
}


/**
	Konvertierung nach int.
*/

int ddstring::as_int() const
{
	int target = 0;
	stringstream converter;
	converter << *this;
	converter >> target;
	return target;
}


/**
	Konvertierung nach double.
*/

double ddstring::as_double() const
{
	double target = 0;
	stringstream converter;
	converter << *this;
	converter >> target;
	return target;
}


/**
	Konvertierung nach size_t.
*/

size_t ddstring::as_size_t() const
{
	size_t target = 0;
	stringstream converter;
	converter << *this;
	converter >> target;
	return target;
}


/**
	Macht einen Dirnamen aus diesem String, indem ggf. '/'
	angehängt wird.
*/

ddstring& ddstring::to_dir_name()
{
	if( empty() || (*this).last_byte() != '/' )
		(*this) += '/';
	return *this;
}

	
/**
	String in Großschreibung umwandeln.
*/

ddstring& ddstring::to_upper()
{
  for( ddstring::size_type count=0; count < length(); count++ )
		(*this)[count] = toupper( (*this)[count] );
	return *this;
}


/**
	String in Kleinschreibung umwandeln.
*/

ddstring& ddstring::to_lower()
{
	for( ddstring::size_type count=0; count < length(); count++ )
		(*this)[count] = tolower( (*this)[count] );
	return *this;
}


/**
	Schreibt den Inhalt des Strings in eine Datei.
	@param filename die Zieldatei
*/
bool ddstring::to_file( const ddstring& filename )
{
	ofstream outstream( filename.c_str(), ios::out );
	if( !outstream )
		return false;
	outstream << *this;
	outstream.close();
	return true;
}	


ddstring& ddstring::to_split_text( size_t asize )
{
	assign( as_split_text( asize ) );
	return *this;
}
	
/**
	Returns true if the ddstring "src" represents a numeric value.
	@param src the source ddstring	
*/

bool ddstring::is_number() const
{
	if( empty() )
		return false;
	for( size_t i=0; i<size(); ++i )
	{
		if( isdigit( (*this)[i] )
			|| (*this)[i] == '.'
		  || (*this)[i] == ','
			|| (*this)[i] == '-'
			|| (*this)[i] == '+')
			continue;
		return false;
	}
	return true;
}

/**
	Decode a hex value.
	Based on code from thttpd: Jef Poskanzer <jef@acme.com>
*/

int hex2int( char c )
{
  if ( c >= '0' && c <= '9' )
		return c - '0';
  if ( c >= 'a' && c <= 'f' )
		return c - 'a' + 10;
  if ( c >= 'A' && c <= 'F' )
		return c - 'A' + 10;  
  /* shouldn't happen, we're guarded by isxdigit() */
  return 0;
}


/**
	Sog. 'evil characters' wie & etc. ersetzen.
	FIX! Fragwürdig! hier wird zuviel ersetzt!
*/

ddstring& ddstring::trim_evil()
{
	ddstring result;
	
	//Warum ?
	//src.replace_all( "&nbsp;", "" );
	
	for( size_t i=0; i< size(); ++i )
	{
		// strip codes, but keep \n and \t
		if( (size_t) (*this)[i] < 32 && (*this)[i] != '\t' && (*this)[i] != '\r' && (*this)[i] != '\n' ) 			
			continue;		
				
		// neuer  Versuch: einfach das
		// amp escapen
		switch( (*this)[i] )
		{
			case '&' : 
				result += "&amp;";
				break;
			
			case  '"' :
				result += "&quot;";
				break;
				
			case  '<' :
				result += "&lt;";
				break;
				
			case  '>' :
				result += "&gt;";					
				break;
				
			case  '\\' :
				result += "&#92;";		
				break;
				
			case  '\'' :
				result += "&#39;";		
				break;
				
			default : 
				result += (*this)[i];
		}
	}
	assign ( result );
	return *this;	 
}




ddstring& ddstring::trim_all_of( const ddstring& pattern )
{
 	const char* deny = pattern.c_str();
	ddstring dest;
	
	for( size_t i=0; i<size(); ++i )
	{
		if( !::strchr( deny, (*this)[i] ) )
			dest += (*this)[i];
	}
	assign( dest );
 	return *this;
}


ddstring& ddstring::blank_all_of( const ddstring& pattern )
{
 	const char* deny = pattern.c_str();
	ddstring dest;
	
	for( size_t i=0; i<size(); ++i )
	{
		if( ::strchr( deny, (*this)[i] ) )
			dest += ' ';
		else
			dest += (*this)[i];
	}
	assign( dest );
 	return *this;
}


/**
	Trim a string. This function removes all spaces and tabs on the left and right side of the
	string.
	@return the stripped string.
*/

ddstring& ddstring::trim()
{
  ddstring::size_type pos1 = find_first_not_of("\t\n\r ");
  // es gibt nur blanks
  if( pos1 == ddstring::npos )
  {
    assign("");
    return *this;
  }
  ddstring::size_type pos2 = find_last_not_of("\t\r\n "); 	
  if( pos2 == ddstring::npos )
  {
    assign("");
    return *this;
  }
  //??
  assign( substr( pos1,pos2-pos1+1 ) );
  return *this;	
}


/**
	Kürzt den String und hängt ggf. "..." an.
	@param asize die neue (maximale) Größe.	
*/

ddstring& ddstring::trim_text( size_t asize )
{
  asize -= 3;
  if( asize>=size() )
		return *this;
	while( asize && !isblank( (*this)[asize] )  )
		asize--;	
	resize( asize );
  append( "..." );
  return *this;		
}

	
/**
	Säbelt den string ab dem substring 'pattern' ab, fall vorhanden
	@param asize die neue (maximale) Größe.	
*/
	
ddstring ddstring::trim_pattern( const ddstring& pattern ) const
{
	size_t dash = find( pattern );
	if( dash != ddstring::npos )
		return substr( 0, dash );
	return *this;
}


/**
	Replace parts in a ddstring. This function can be used to replace all 
	occurences of the sub ddstring <b>oldstr</b> in the ddstring <b>src</b> 
	with the new sub ddstring <b>newstr</b>.
	The function returns the resulting ddstring.
*/


ddstring& ddstring::replace_all( const ddstring& oldstr, const ddstring& newstr )
{
 	int pos, index = 0, offset = newstr.length() - oldstr.length();
 	while( ( pos =  find( oldstr,index ) ) >= 0 )
 	{
 		replace( pos,oldstr.length(),newstr );
 		index = pos + offset;
 	}
 	return *this;
}


size_t ddstring::count_all( const ddstring& txt )
{
 	int pos, index = 0, offset = txt.length();
 	size_t found = 0;
	while( ( pos =  find( txt,index ) ) >= 0 )
	{
 		found ++;
		index = pos + offset;
	}
 	return found;
}


/**
	Zählt die worte in einem Textblock.
*/

size_t ddstring::count_words()
{
	
 	size_t i = 0, found = 0; 
 	
	while( i<size() )
	{
		// skip mode: whitespace überlesen
		while( i<size() && !isalnum( (*this)[i] ) )
	  	i++;
		
		// wortanfang gefunden
		found++;
		
		// read mode: wort überlesen
		while( i<size() && !isblank( (*this)[i] )  )
			i++;
	
	}
	return found;

}


/**
	Replace parts in a ddstring. This function can be used to replace all 
	occurences of the sub ddstring <b>oldstr</b> in the ddstring <b>src</b> 
	with the new sub ddstring <b>newstr</b>.
	The function returns the resulting ddstring.
*/

ddstring& ddstring::replace_all( char oldchar, char newchar )
{
 	for( size_t i=0; i<size(); ++i )
	{
		if( (*this)[i] == oldchar )
			(*this)[i] = newchar;
	}
 	return *this;
}
	


/**
	Zerlegt den String in 2 Substrings anhand eines Trennmusters.
*/

bool ddstring::split( const ddstring& pattern, ddstring& part1, ddstring& part2 ) const
{
	size_t dot = find( pattern );
	if( dot != ddstring::npos )
	{
		size_t patlen = pattern.size();
		part1 = substr( 0, dot );
		part2 = substr( dot+patlen );		
		return true;
	}
	return false;		
}





ddstring ddstring::as_codeless_text() const
{
	
	// hässlich & böse sünde
	ddstring tmp = *this;
	size_t pos;
 	while( ( pos =  tmp.find( "[img", 0 ) ) != ddstring::npos )
 	{
 		size_t nxt = tmp.find( "[/img", pos ); 
 		// ende gefunden? dann löschen
 		// ende nicht gefunden wg. schlamperei von sapse? dann raus
 		if( nxt != ddstring::npos )
 			tmp.erase( pos, nxt-pos );
 		else
 			break;
 	}
 	
 	ddstring result;
	bool skip = false;	
	
	for( size_t i=0; i<tmp.size(); ++i )
	{
		char c = (char) tmp[i];
		
		if( c == '\n' || c == '\r')
			continue;
		
		if( c == '[' )
			skip = true;
		else if( c == ']' )
			skip = false;
		if( !skip && c != ']')		
			result += c;
	
	}
	
	
	return result;
	
}


/**
	Escape URL fields.
	@param src the source ddstring
*/

ddstring& ddstring::escape()
{
	
	static const char* allow = "_-.!~'*()";

	if( empty() )
		return *this;
	
	char buf[10];
	ddstring result;
	for( size_t i = 0; i < size(); i++ )
	{
		char c = (char) (*this)[i];
		// falls unreserved: anhängen
		// sonst: codieren
		if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (::strchr( allow, c ) != 0) )
			result += c;
		else
		{
			snprintf( buf, 9, "%%%2X", (unsigned char) c );
			result += buf;
		}
	}
	assign( result );
	return *this;
}


/**
	Unescape URL fields.
	@param src the source string
	Based on code from thttpd: Jef Poskanzer <jef@acme.com>
*/

ddstring& ddstring::unescape()
{
	
	if( empty() )
		return *this;
	
	size_t i=0;		
	size_t j=0;		
	
	size_t max = 0;
	if( size()>=3)
		max = size()-3;
	
	for(; i<size(); i++ )
	{
		char c = (*this)[i];
		if( c=='+' )
		{
			(*this)[j++] = ' ';
		}
		else if( c == '%' )
		{
			if( i<=max && ::isxdigit( (*this)[i+1] ) && ::isxdigit( (*this)[i+2] ) )
			{
				(*this)[j++] = ::hex2int( (*this)[i+1] ) * 16 + ::hex2int( (*this)[i+2] );
				i+=2;
			}
		}		
		else 
		{
			(*this)[j++] = c;
		}
	}
	resize( j );	
	return *this;
}


/**
	Fügt Blanks in einen String ein, um beim Rendern
	Umbrüche zu ermöglichen.
	@param asize die Anzahl bytes, nach denen ein Blanks eingefügt wird.	
*/

ddstring ddstring::as_split_text( size_t asize ) const
{
	size_t chars=0;
	ddstring result;
	bool in_tag = false;
	for( size_t i=0; i<size(); ++i )
	{
		char c = (*this)[i];
		
		// nicht in tags umbrechen
		if( c == '&' )
			in_tag = true;
		if( c == ';' && in_tag )
			in_tag = false;
		
		// nicht-blanks zählen
		if( isblank( c ) )
			chars=0;
		else
			chars++;
		
		// maximale wortläge erreicht
		if( chars >= asize &&!in_tag )
		{
			result += ' ';
			chars=0;
		}
		result += c;
		
	}
	return result;		
}


/**
	Nicht-Ascii-Zeichen absäbeln.
	@param asize die neue (maximale) Größe.	
*/

ddstring ddstring::as_cleaned_text( size_t asize ) const
{	
	static const char* allow = "öäüßÄÖÜ@`´~^©®";
	ddstring dest;
	int to = DDMIN( size(), asize );
	for( int i=0; i<to; ++i )
	{
		if( !isascii( (*this)[i] ) && !::strchr( allow, (*this)[i] ) )
			continue;
		dest += (*this)[i];
	}
	return dest;		
}


/**
	Erzeugt einen Ascii-Text in dem Umlaute etc. umgewandelt oder
	weggeschmissen werden.
*/

ddstring ddstring::as_plain_ascii() const
{
	static const char* allow = "._-";
	ddstring result;
	int c;
	for( size_t i=0; i < size(); ++i )
	{		
		c = (*this)[i];	
		switch( c )
		{
			case '@':
				result += "A";
				continue;
		
			case 'ü':
				result += "ue";
				continue;
				
			case 'ä':
				result += "ae";
				continue;
				
			case 'ö':
				result += "oe";
				continue;
				
			case 'Ü':
				result += "Ue";
				continue;
			
			case 'Ä':
				result += "Ae";
				continue;
				
			case 'Ö':
				result += "Oe";
				continue;
				
			case 'ß':
				result += "ss";
				continue;
				
			default:
				// ok
				if( isalnum( c ) || ::strchr( allow, c ) )
					result += c;
				else
					result += '_';

		} // switch
		
	} // for


	return result;
}


/**
	Erzeugt aus diesem String einen gültigen 
	Nickname, indem Umlaute, verbotene Zeichen etc.
	konvertiert werden.
*/

ddstring ddstring::as_valid_nickname() const
{		
	return as_plain_ascii().substr( 0, DDNICKMAXLEN );
}






/***************************************************************************

    supportware
    Copyright (C) 2001-2008 by c.holzheuer
    c.holzheuer@sourceworx.org

    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.

***************************************************************************/


namespace ddstrutil
{
	
	/**
		Konvertiert eine Zahl in einen hex-ddstring.
		@param num die Zahl
		@param bytes die Anzahl stellen
		@param uppercase Gross- oder Kleinschreibung. Default ist Grossschreibung.
	*/

	ddstring to_hex( int num, int bytes, bool upcase )
	{
		
		ddstring result;
		stringstream converter;
	
		// FIX!
		// geht das oder wirkt das nur einmal?
	
		if( upcase )
			uppercase( converter );
		else
			nouppercase( converter );
	
	
		if( bytes )
			converter << setw(bytes) << setfill('0') << hex << num;
		else
			converter << hex << num;
		converter >> result;
		
		return result;
			
	}
	
	
	/**
		Konvertierung von Bool.
		@param num ein boolscher Wert.
	*/

	ddstring from_bool( bool num )
	{
		return ddstring( num ? DDSTRTRUE : DDSTRFALSE );
	}


	/**
		Konvertierung von Int.
		@param num ein Integer Wert.
	*/

	ddstring from_int( int num )
	{
		ddstring newstr;
    newstr.from_int( num );      
		return newstr;
	}

	
	/**
		Konvertierung von unsigned int.
		@param num ein Size_t Wert.
	*/

	ddstring from_size_t( size_t num )
	{
    ddstring newstr;
    newstr.from_size_t( num );
		return newstr;
	}


	/**
		Konvertierung von Double.
		@param num ein Double Wert.
		@param precision Die Anzahl Stellen.
	*/

	ddstring from_double( double num, int precision )
	{
		ddstring newstr;
    newstr.from_double( num, precision );
		return newstr;
	}
	
	
	/**
		Konvertierung von Hex.
	*/
	
	ddstring from_hex( int num, int bytes )
	{
		stringstream converter;
		ddstring result;
		if( bytes )
			converter << setw(bytes) << setfill('0') << hex << num;
		else
			converter << hex << num;
		converter >> result;
		return result;
	}
	
	
	/**
		Liest den Inhalt einer Datei via mmap in diesen String ein.
		@param filename der Pfad
		@return -1 bei Fehler, sonst die Dateigröße.
		@see ddiommap
	*/
	
	ddstring from_file( const ddstring& filename )
	{
		ddstring newstr;
		newstr.from_file( filename );
		return newstr;
	}

}



