#include <stdio.h>
#include <ctype.h>
#include <strstrea.h>
#include <common/string.hpp>
#include <common/block.hpp>
#include <common/macro.hpp>

String::String(const char *npStr)
: mnpStr(0), mLengthBytes(0)
{
	DWORD stringLength;

	if(!npStr)return;
#ifndef _MSC_VER
	try{stringLength=::strlen(npStr);}
	except(0,EXCEPTION_EXECUTE_HANDLER){stringLength=0;}
#else
	__try{stringLength=::strlen(npStr);}
	__except(0,EXCEPTION_EXECUTE_HANDLER){stringLength=0;}
#endif
	if(!stringLength)return;
	stringLength++;
	reserve(stringLength,FALSE);
	::strcpy(mnpStr,npStr);
}

String::String(const String &newString)
: mnpStr(0), mLengthBytes(0)
{
	DWORD stringLength;

	if(!newString.mnpStr)return;
	if(!(stringLength=newString.length()))return;
	stringLength++;
  reserve(stringLength,FALSE);
	::strcpy(mnpStr,newString.mnpStr);
}

String &String::operator=(const String &someString)
{
	int stringLength;

  if(this==&someString)return *this;
	if(someString.isNull()){removeData();return *this;}
	if(!(stringLength=::strlen(someString.mnpStr)))return *this;
	stringLength++;
	if(lengthBytes()>=stringLength)::strcpy(mnpStr,someString.mnpStr);
	else {reserve(stringLength,FALSE);::strcpy(mnpStr,someString.mnpStr);}
	return *this;
}

int String::operator=(const char *someCharStar)
{
	int stringLength;

	if(!someCharStar)return 0;
	if(!(stringLength=::strlen(someCharStar)))return 0;
	stringLength++;
	if(lengthBytes()>=stringLength)::strcpy(mnpStr,someCharStar);
	else {reserve(stringLength,FALSE);::strcpy(mnpStr,someCharStar);}
	return 1;
}

void String::operator+=(char someChar)
{
  DWORD strLength(length());
	if(strLength+2>mLengthBytes)
	{
	  String tmpString(someChar);
		*this+=tmpString;
		return;
	}
	*(mnpStr+strLength)=someChar;
	*(mnpStr+strLength+1)=0;
}

int String::operator+=(const String &someString)
{
	char *lpString;
	DWORD currLength;
	DWORD stringLength;

	if(!someString.mnpStr)return 0;
	stringLength=someString.length();
	stringLength++;
	if(!mnpStr)
	{
		reserve(stringLength,FALSE);
		::strcpy(mnpStr,someString.mnpStr);
		return 1;
	}
	currLength=::strlen(mnpStr);
	if(lengthBytes()-currLength>=stringLength)
	{
		::strcat(mnpStr,someString.mnpStr);
		return 1;
	}
	lpString=mnpStr;
	mnpStr=::new char[currLength+stringLength+MaxString];
	mLengthBytes=currLength+stringLength+MaxString;
	::strcpy(mnpStr,lpString);
	::strcpy(mnpStr+currLength,someString.mnpStr);
	::delete[] lpString;
	return 1;
}

int String::operator+=(const char *someStr)
{
	char *lpString;
	DWORD currLength;
	DWORD stringLength;

	if(!someStr)return 0;
	stringLength=::strlen(someStr);
	stringLength++;
	if(!mnpStr)
	{
		reserve(stringLength,FALSE);
		::strcpy(mnpStr,someStr);
		return 1;
	}
	currLength=::strlen(mnpStr);
	if(lengthBytes()-currLength>=stringLength)
	{
		::strcat(mnpStr,someStr);
    return 1;
  }
	lpString=mnpStr;
	mnpStr=::new char[currLength+stringLength+MaxString];
  mLengthBytes=currLength+stringLength+MaxString;
	::strcpy(mnpStr,lpString);
	::strcpy(mnpStr+currLength,someStr);
	::delete[] lpString;
	return 1;
}

String String::operator+(const String &someString)const
{
	String tmpString(*this);
	tmpString+=someString;
	return tmpString;
}

int String::token(const char *tokenString)
{
	if(!tokenString||!mnpStr)return 0;
	if(::strtok(mnpStr,tokenString))return 1;
	return 0;
}

int String::strchr(char someChar)const
{
  int pos;
  if(!mnpStr)return -1;
	pos=(int)::strchr(mnpStr,someChar);
	if(!pos)return -1;
	return pos-(int)mnpStr;
}

int String::strpos(const char *string)const
{
	int stringPosition;
	if(!mnpStr)return -1;
	stringPosition=(int)::strstr(mnpStr,string);
	if(!stringPosition)return -1;
  return stringPosition-(int)mnpStr;
}

int String::strncmp(const char *string)const
{
  int srcLength;
	int dstLength;

  if(!mnpStr)return -1;
	srcLength=::strlen(string);
	dstLength=::strlen(mnpStr);
  return ::strncmp(mnpStr,string,srcLength<dstLength?srcLength:dstLength);
}

void String::upper(void)
{
	char *string=mnpStr;

	if(!mnpStr)return;
	while(*string)
  {
		*string=::toupper(*string);
		string++;
  }
}

void String::lower(void)
{
	char *string=mnpStr;

	if(!mnpStr)return;
	while(*string)
  {
		*string=::tolower(*string);
		string++;
  }
}

String String::betweenString(char beginToken,char endToken)const
{
  char nullString[MaxString]={0};
	short stringLength;
	char *lpBegin;
	char *lpEnd;

	if(!mnpStr)return String(nullString);
	stringLength=length();
	if(1>=stringLength)return String(nullString);
	if(beginToken)
	{
		if(0==(lpBegin=::strchr(mnpStr,beginToken)))return String(nullString);
		if(++lpBegin-mnpStr>=stringLength)return String(nullString);
	}
	else lpBegin=mnpStr;
	if(0==(lpEnd=::strchr(lpBegin,endToken)))return *this;
	if(lpBegin==lpEnd)return String(nullString);
	if(sizeof(nullString)<=(lpEnd-lpBegin)+1)
	{
	  String nullString;
	  nullString.reserve((lpEnd-lpBegin)+1,FALSE);
	  ::memcpy(nullString,lpBegin,lpEnd-lpBegin);
	  *((char*)nullString+(lpEnd-lpBegin))=0;
		return nullString;
	}
	::memcpy(nullString,lpBegin,lpEnd-lpBegin);
	*(nullString+(lpEnd-lpBegin))=0;
	return String(nullString);
}

WORD String::makeBlock(Block<String> &receiveStrings,const String &tokenString)const
{
  String stringData(*this);
	String workString;
	LONG stringPos;
  LONG lengthToken;

	receiveStrings.remove();
	if(!(lengthToken=tokenString.length()))return FALSE;
  if(stringData.isNull())return FALSE;
	while(TRUE)
	{
		if(-1==(stringPos=stringData.strpos(tokenString)))
		{
      if(!stringData.isNull())receiveStrings.insert(&stringData);
			return receiveStrings.size();
		}
		else if(stringData.length()!=stringPos+lengthToken)
		{
			if(stringPos)
			{
				workString=stringData.substr(0,stringPos-(lengthToken-1));
			  receiveStrings.insert(&workString);
			}
			stringData=stringData.substr(stringPos+lengthToken,stringData.length());
		}
		else stringData.removeTokens(tokenString);
	}
}

String String::extractDigits(void)const
{
	String tempString;
  String nullString;
	char *lpClampOne;
	char *lpClampTwo;

	if(!mnpStr)return nullString;
	tempString=mnpStr;
	lpClampOne=(char*)tempString;
	while(*lpClampOne&&!isdigit(*lpClampOne))lpClampOne++;
	if(!*lpClampOne)return nullString;
	lpClampTwo=lpClampOne;
	while(*lpClampTwo&&isdigit(*lpClampTwo))lpClampTwo++;
	*lpClampTwo=0;
	return lpClampOne;
}

String String::extractAlpha(void)const
{
	String tempString;
  String nullString;
	char *lpClampOne;
	char *lpClampTwo;

	if(!mnpStr)return nullString;
	tempString=mnpStr;
	lpClampOne=(char*)tempString;
	while(*lpClampOne&&!isalpha(*lpClampOne))lpClampOne++;
	if(!*lpClampOne)return nullString;
	lpClampTwo=lpClampOne;
	while(*lpClampTwo&&isalpha(*lpClampTwo))lpClampTwo++;
	*lpClampTwo=0;
	return lpClampOne;
}

void String::expand(void)
{
  Macro macro;
	if(isNull())return;
  macro.processEmbeddedMacro(*this);
}

int String::hex(void)const
{
	String workString(*this);
	DWORD stringLength;
	DWORD multiplier(0x01);
	DWORD value(0L);
  char *ptrString;
	
	if(workString.isNull())return FALSE;
	if(workString.strstr(" "))workString.removeTokens(" ");
  if(!(stringLength=workString.length()))return FALSE;
	workString.upper();
	if(0!=(ptrString=(char*)workString.strstr("0X")))
	{
    ptrString+=2;
		stringLength=::strlen(ptrString);
    ptrString+=(stringLength-1);
	}
  else if('H'==*((char*)workString+(stringLength-1)))
	{
    stringLength--;
    ptrString=(char*)workString+(stringLength-1);
	}
	else ptrString=(char*)workString+(stringLength-1);
	for(LONG stringIndex=stringLength-1;stringIndex>=0;stringIndex--)
	{
    switch(*(ptrString--)) 
		{
			case '0' : break;
			case '1' : {value+=multiplier;break;}
			case '2' : {value+=multiplier*2;break;}
			case '3' : {value+=multiplier*3;break;}
			case '4' : {value+=multiplier*4;break;}
			case '5' : {value+=multiplier*5;break;}
			case '6' : {value+=multiplier*6;break;}
			case '7' : {value+=multiplier*7;break;}
			case '8' : {value+=multiplier*8;break;}
			case '9' : {value+=multiplier*9;break;}
			case 'A' : {value+=multiplier*10;break;}
			case 'B' : {value+=multiplier*11;break;}
			case 'C' : {value+=multiplier*12;break;}
			case 'D' : {value+=multiplier*13;break;}
			case 'E' : {value+=multiplier*14;break;}
			case 'F' : {value+=multiplier*15;break;}
			case '-' : {value*=-1;break;}
     default : return value;
		}
    multiplier*=16;
	}
  return value;
} 

void String::spaceTerm(void)
{
	size_t strLen;

	if(isNull()||(0==(strLen=length())))return;
	for(short index=0;index<strLen;index++)if(Blank==*(mnpStr+index)){*(mnpStr+index)=0;return;}
}

String &String::trimRight(void)
{
	size_t strLen;

	if(isNull()||(0==(strLen=length())))return *this;
	for(short index=strLen-1;index>=0;index--)
  {
		if(Blank==*(mnpStr+index))*(mnpStr+index)=0;
		else break;
	}
	return *this;
}

String &String::trimLeft(void)
{
  size_t strLen;

	if(isNull()||(0==(strLen=length())))return *this;
	for(short index=0;index<strLen;index++)if(Blank!=*(mnpStr+index))break;
	if(!index)return *this;
	*this=substr(index);
	return *this;
}

void String::removeTokens(String someTokens)
{
	int length(this->length());
	int tokens(someTokens.length());
	String tempString;

	if(!length||!tokens)return;
	for(int token=0;token<tokens;token++)
		for(int pos=0;pos<length;pos++)if(*(mnpStr+pos)==*(someTokens.mnpStr+token))*(mnpStr+pos)=0;
	for(int pos=0;pos<length;pos++)
	  if((mnpStr+pos))tempString+=*(mnpStr+pos);
	*this=tempString;
}

void String::replaceToken(BYTE tokenFind,BYTE tokenReplace)
{
	int length(this->length());
	String tempString;

	if(!length)return;
	for(int pos=0;pos<length;pos++)if(*(mnpStr+pos)==tokenFind)*(mnpStr+pos)=tokenReplace;
	for(pos=0;pos<length;pos++){if((mnpStr+pos))tempString+=*(mnpStr+pos);}
	*this=tempString;
}

void String::length(short newLength)
{
	short currLength(length());

	if(currLength==newLength)return;
	if(currLength>newLength)
	{
		String tempString;
		tempString.reserve(newLength+1);
		::memcpy(tempString,*this,newLength);
    *this=tempString;
	}
  else while(length()<newLength)*this+=" ";
}

void String::convert(double doubleValue,const char *formatString)
{
	if(!formatString)return;
	reserve(MaxString);
	::sprintf(mnpStr,formatString,doubleValue);
	return;
}

void String::convert(int integerValue,const char *formatString)
{
	if(!formatString)return;
  reserve(MaxString);
	::sprintf(mnpStr,formatString,integerValue);
	return;
}

void String::convert(long longValue,const char *formatString)
{
	if(!formatString)return;
  reserve(MaxString);
	::sprintf(mnpStr,formatString,longValue);
	return;
}

WORD String::remove(WORD removePosition)
{
  WORD stringLength(length());
	String tempString;

	if(!mnpStr)return FALSE;
	if(stringLength<=removePosition)return FALSE;
	if(!removePosition&&1==stringLength){*(mnpStr)='\0';return TRUE;}
	if(!removePosition){*this=(mnpStr+1);return TRUE;}
	*(mnpStr+removePosition)=0;
	tempString=mnpStr;
	tempString+=String((mnpStr+removePosition+1));
	*this=tempString;
  return TRUE;
}

String String::substr(WORD startPosition,WORD endPosition)const
{
  WORD stringLength(length());
	String resultString;

	if(!stringLength)return resultString;
	if(startPosition>endPosition)return resultString;
	if(endPosition>stringLength)endPosition=stringLength;
	resultString.reserve(endPosition-startPosition+2);
	::memcpy(resultString,mnpStr+startPosition,(endPosition-startPosition)+1);
	return resultString;
}

WORD String::insert(const String &insertString,WORD insertPosition)
{
	WORD insertLength(insertString.length());

	if(!insertLength)return FALSE;
	if(!mnpStr)
	{
		reserve(insertLength+insertPosition+1);
		::memset(mnpStr,Blank,insertLength+insertPosition);
		::memcpy(mnpStr+insertPosition,insertString,insertLength);
	}
	else
	{
		WORD currentLength(length());
		String tempString;

		if(insertLength+insertPosition>currentLength)
		{
			tempString.reserve(insertLength+insertPosition+currentLength+1);
			::memset(tempString,Blank,insertLength+insertPosition+currentLength);
		}
		else
		{
			tempString.reserve(currentLength+insertLength+1);
			::memset(tempString,Blank,currentLength+insertLength);
		}
		::memcpy(tempString,mnpStr,currentLength);
		if(!(insertPosition>currentLength))shiftRight(tempString,insertPosition,currentLength+insertLength-1,insertLength,currentLength);
		::memcpy(((char*)tempString)+insertPosition,insertString,insertLength);
		*this=tempString;
	}
	return TRUE;
}

WORD String::insert(char *lpInsertString,WORD insertPosition)
{
	WORD insertLength;

	if(!lpInsertString||!(insertLength=::strlen(lpInsertString)))return FALSE;
	if(!mnpStr)
	{
		reserve(insertLength+insertPosition+1);
		::memset(mnpStr,Blank,insertLength+insertPosition);
		::memcpy(mnpStr+insertPosition,lpInsertString,insertLength);
	}
	else
	{
		WORD currentLength(length());
		String tempString;

		if(insertLength+insertPosition>currentLength)
		{
			tempString.reserve(insertLength+insertPosition+currentLength+1);
			::memset(tempString,Blank,insertLength+insertPosition+currentLength);
		}
		else
		{
			tempString.reserve(currentLength+insertLength+1);
			::memset(tempString,Blank,currentLength+insertLength);
		}
		::memcpy(tempString,mnpStr,currentLength);
		if(!(insertPosition>currentLength))shiftRight(tempString,insertPosition,currentLength+insertLength-1,insertLength,currentLength);
		::memcpy(((char*)tempString)+insertPosition,lpInsertString,insertLength);
		*this=tempString;
	}
	return TRUE;
}

void String::shiftRight(String &shiftString,WORD startPos,WORD endPos,WORD insertLength,WORD originalLength)
{
	WORD shiftLength(originalLength-startPos);
	for(WORD shiftIndex=0;shiftIndex<shiftLength;shiftIndex++,endPos--)
		*((char*)shiftString+endPos)=*((char*)shiftString+endPos-insertLength);
}

int String::toInt(void)const
{
  if(isNull())return 0;
	return ::atoi(str());
}

short String::toShort(void)const
{
  if(isNull())return 0;
	return short(::atoi(str()));
}

float String::toFloat(void)const
{
  if(isNull())return 0.00;
	return ::atof(str());
}

double String::toDouble(void)const
{
  if(isNull())return 0.00;
	return ::atof(str());
}

long String::toLong(void)const
{
  if(isNull())return 0;
	return ::atol(str());
}

String &String::fromInt(int value)
{
  reserve(MaxString);
	::sprintf(mnpStr,"%d",value);  
	return *this;
}

String &String::fromShort(short value)
{
  reserve(MaxString);
	::sprintf(mnpStr,"%d",value);
	return *this;
}

String &String::fromUShort(unsigned short value)
{
  reserve(MaxString);
	::sprintf(mnpStr,"%d",value);
	return *this;
}

String &String::fromFloat(float value)
{
  reserve(MaxString);
	::sprintf(mnpStr,"%9.2lf",value);
	return *this;
}

String &String::fromDouble(double value)
{
  reserve(MaxString);
	::sprintf(mnpStr,"%9.2lf",value);
	return *this;
}

String &String::fromLong(long value)
{
  reserve(MaxString);
	::sprintf(mnpStr,"%ld",value);
	return *this;
}

String &String::fromBool(bool boolValue)
{
  *this=boolValue?"T":"F";
	return *this;
}

// non-member

istream &operator>>(istream &stream,String &someString)
{
  char charData;

	someString.reserve(String::MaxString);
	while(stream.get(charData))
	{
	  if(charData=='\n')
		{
		  if(someString.length())break;
			continue;
		}
		someString+=charData;
	}
	someString+='\0';
  return stream;
}

ostream &operator<<(ostream &stream,const String &someString)
{
  return stream<<(char*)((String&)someString);
}

String operator+(const char *str,const String &string)
{
  return String(str)+string;
}
