/* 
 * Copyright (C) 2002 - David W. Durham
 * 
 * This file is not part of any particular application.
 * 
 * istring 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.
 * 
 * istring 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 */

#ifndef __istring_h__
#define __istring_h__

#include "../../config/common.h"

/* --- istring class - "improved" string
 * 
 * - This file contains a derived class, istring, to be used in place of the
 * normal C++ STL string whenever a string with more functionality is required.
 * It was written in response to the STL string's utter lacking of useful
 * methods, for instance: converting a number to a string, or trimming white
 * space .  Many of the operations provided in istring are possible with the
 * STL but are often quite awkward, roundabout, and wordy to accomplish.  -
 * Hopefully, this class will become obsolete when/if the
 * C++-ANSI-standard-making-people add more functionality to the STL string
 * class.
 *
 * - TODO: - Make this a template derived from basic_string and typedef istring
 * as i_basic_string<char> just like string is I didn't do this already because
 * I need to make sure that if I did that, string and istring would be
 * completly interchangable just as it is being *derived* from string.  -
 * Depending on the method, it should probably use the char_traits class to do
 * some work, but I'm not familiar enough with that class to use it.  - The
 * formating of numbers to string has a problem when you right justify and
 * zeropad a negative number, the '-' sign comes after the padding.  I don't
 * know how this should be fixed other than having the code handling the sign
 * character itself.
 */

#include <stddef.h>
#include <ctype.h>

#include <string>

#include <sstream>

namespace std {

class istring : public std::string
{
public:
	// --- constructors
	explicit istring(const allocator_type &a=allocator_type()) : string(){}
	istring(const string &str,size_type pos=0,size_type n=npos, const 
		allocator_type &a=allocator_type()) : string(str,pos,n){}
	istring(const char *s,size_type n,const allocator_type 
			&a=allocator_type()) : string(s,n) { }
	istring(const char *s,const allocator_type &a=allocator_type()) 
		: string(s) { }
	istring(size_type n,char c,const allocator_type &a=allocator_type()) 
		: string(n,c) { }
	template<class InputIterator> istring(InputIterator begin,InputIterator
		end,const allocator_type &a=allocator_type()) 
		: string(begin,end) { }
	
	// --- explicit number conversion constructors
		// integer or floating point type to string
	template<class T> explicit istring(const T val) { 
		ostringstream ss; 
		ss << val; assign(ss.str()); 
	}
		// formatted integer type to string (min_width can be <0 to left justify or >=0 to right justify)
	template<class T> explicit istring(const T val,const int min_width,const bool zero_pad=false) { ostringstream ss; ss.width((min_width<0) ? -min_width : min_width); ss.setf((min_width<0) ? ios::left : ios::right); ss.fill((zero_pad && min_width>=0) ? '0' : ' '); ss << val; assign(ss.str()); }
		// formatted floating point type to string (min_width can be <0 to left justify or >=0 to right justify, precision is the number of place to show after the decimal place)
	template<class T> explicit istring(const T val,const int min_width,const int precision,const bool zero_pad=false) { ostringstream ss; ss.width((min_width<0) ? -min_width : min_width); ss.precision(precision); ss.setf(ios::fixed); ss.setf((min_width<0) ? ios::left : ios::right); ss.fill((zero_pad && min_width>=0) ? '0' : ' '); ss << val; assign(ss.str()); }


	// --- auto-cast to const char *
	operator const char * const() const { return c_str(); }


	// --- trim/pad operations
		// remove white-space from the left side of the string
	istring &ltrim() { const size_type len=length(); size_type i=0; while(i<len && isspace(operator[](i))) i++; erase(0,i); return *this; }
		// remove white-space from the right side of the string
	istring &rtrim() { const size_type len=length(); size_type i=len; if(i>0) { i--; while(i>0 && isspace(operator[](i))) i--; if(i==0 && isspace(operator[](0))) erase(); else erase(i+1,(len-i)-1); } return *this; }
		// remove white-space from the left and right side of the string
	istring &trim() { return ltrim().rtrim(); }

		// add pad_char characters to the left side of the string such that the string is no shorter than min_length
	istring &lpad(const size_type min_length,const char pad_char=' ') { const size_type len=length(); if(len<min_length) insert((size_type)0,min_length-len,pad_char); return *this; }
		// add pad_char characters to the right side of the string such that the string is no shorter than min_length
	istring &rpad(const size_type min_length,const char pad_char=' ') { const size_type len=length(); if(len<min_length) insert(len,min_length-len,pad_char); return *this; }

		// removes any character which has a position that is at or beyond max_length
	istring &truncate(const size_type max_length) { if(size()>max_length) erase(max_length,size()-max_length); return *this; }


	// --- case manipulation
		// make all the characters in the string upper case
	istring &upper(const size_type pos=0,const size_type len=npos) { const size_type last= (len==npos) ? pos+length() : pos+len; for(size_type t=pos;t<last;t++) operator[](t)=toupper(operator[](t)); return *this; }
		// make all the characters in the string lower case
	istring &lower(const size_type pos=0,const size_type len=npos) { const size_type last= (len==npos) ? pos+length() : pos+len; for(size_type t=pos;t<last;t++) operator[](t)=tolower(operator[](t)); return *this; }


	// --- count characters
		// returns how many times ch appears in the string from the optional pos and len parameters
	const size_type count(const char ch,const size_type pos=0,size_type len=npos) { if(len==npos) len=size()-pos; const size_type last=pos+len; size_type count=0; for(size_type t=pos;t<last;t++) if(operator[](t)==ch) count++; return count; }

	// --- count strings
		// returns how many times str appears in the string from the optional pos and len parameters
	const size_type count(const string str,const size_type pos=0,size_type len=npos) { if(len==npos) len=size()-pos; size_type last=pos+len; size_type str_len=str.length(); if(last>str_len) last-=str_len; else return 0; size_type count=0; for(size_type t=pos;t<last;t++) { if(substr(t,str_len)==str) { t+=str_len; count++; } } return count; }
	

	// --- search and replace
		// returns self; replaces needle in the this, the haystack, with replacement; looks again if recur is true
	string &searchAndReplace(const string needle,const string replacement,bool recur=false) { size_type pos=0; while((pos=find(needle,pos))!=string::npos) { replace(pos,needle.size(),replacement); if(!recur) break; } return *this; }
	
};



/* 
 *	- mnn -- 'make not null'
 *
 *	- this inline funciton basically return "" if the parameter is NULL such that:
 *		string s;
 *		char *p=...;
 *		s=mnn(p);
 *	  will not cause a segfault when and if p is a NULL pointer
 *
 *	- used for the stupid property that the STL string cannot accept NULL as any of the char * arguments
 *
 *	- nor does it make a distinction between "" and NULL which I believe it should
 */
inline const char * const mnn(const char * const p) { return p==NULL ? "" : p; }


}

using std::istring;
using std::mnn;

#endif
