//==================================================================================================
// B + +   L o w - L e v e l   S t r i n g                                                Interface
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library 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 Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* Provides basic functions to manipulate strings. They are useful when the standard
   <CODE>sprintf</CODE> and <CODE>sscanf</CODE> functions are not working correctly (for instance,
   when loading Cygwin's dynamic library inside a Java environment). */

// Guardian //--------------------------------------------------------------------------------------
#ifndef _BPP_LOW_STRING_
#define _BPP_LOW_STRING_

// Headers //---------------------------------------------------------------------------------------
#include <cctype>
#include <cfloat>
#include <cmath>

namespace bpp { namespace low {

// Macrocommands //---------------------------------------------------------------------------------
#define BUFFER_SIZE 64

// Types & Classes //-------------------------------------------------------------------------------
typedef BOOLEAN_TYPE        tyBoolean;
typedef BYTE_TYPE           tyByte;
typedef CARDINAL_TYPE       tyCardinal;
typedef CHARACTER_TYPE      tyCharacter;
typedef INTEGER_TYPE        tyInteger;
typedef void *              tyPointer;
typedef REAL_TYPE           tyReal;
typedef tyCharacter *       tyString;
typedef const tyCharacter * tcString;

template <int prDummy> class clString;

/*TYPE*/ /* Represents an instantiation of the parameterized class <CODE>clString</CODE>. */
typedef clString<0> string;

// S t r i n g  Interface //------------------------------------------------------------------------
/*CLASS string */ /* Provides basic functions to manipulate strings. */
template <int prDummy> class clString {
 //------------------------------------------------------------------------------------------Private
 protected: static tcString atHexaCodeS;
 //-------------------------------------------------------------------------------------------Public
 public: static void       concatenate(tcString,tcString);
 public: static void       copy(tcString,tcString);
 public: static tyCardinal length(tcString);

 public: static tcString toString(tyString,tyCardinal);
 public: static tcString toString(tyString,tyInteger);
 public: static tcString toString(tyString,tyPointer);
 public: static tcString toString(tyString,tyReal);

 public: static tcString toCardinal(tcString,tyCardinal &);
 public: static tcString toInteger(tcString,tyInteger &);
 public: static tcString toPointer(tcString,tyPointer &);
 public: static tcString toReal(tcString,tyReal &);
};

// S t r i n g  Implementation //-------------------------------------------------------------------

template <int prDummy> tcString clString<prDummy>::atHexaCodeS = "0123456789abcdef";

//---------------------------------------------------------------------------------------Concatenate
/*METHOD string */
/* Concatenates two strings. There must be enough space in the first string to copy the second
   one. */
template <int prDummy> void clString<prDummy>::concatenate(tcString agTarget,tcString agSource) {
 tyString lcTarget = tyString(agTarget);

 while (*lcTarget!=0) ++lcTarget;
 copy(lcTarget,agSource);
}
//----------------------------------------------------------------------------------------------Copy
/*METHOD string */
/* Copies a string. There must be enough space in the first string to copy the second one. */
template <int prDummy> void clString<prDummy>::copy(tcString agTarget,tcString agSource) {
 tyString lcTarget = tyString(agTarget);

 if (agSource!=0) while (*agSource!=0) *(lcTarget++)=*(agSource++);
 *lcTarget=0;
}
//--------------------------------------------------------------------------------------------Length
/*METHOD string */ /* Returns the number of characters in a string. */
template <int prDummy> tyCardinal clString<prDummy>::length(tcString agString) {
 tyCardinal lcLength = 0;

 if (agString!=0) while (agString[lcLength]!=0) ++lcLength;
 return (lcLength);
}

//-------------------------------------------------------------------------------ToString (Cardinal)
/*METHOD string */
/* Converts a cardinal number into a string. There must be enough space in the string given as
   argument to store the result. */
template <int prDummy>
tcString clString<prDummy>::toString(tyString agString,tyCardinal agNumber) {
 tyCharacter lcBuffer[BUFFER_SIZE];

 tyCardinal lcCounter = 0;
 tyString   lcString  = agString;

 do {
  lcBuffer[lcCounter++]=tyCharacter(tyCharacter('0')+tyCharacter(agNumber%10));
  agNumber/=10;
 }
 while (agNumber>0);

 while (lcCounter>0) *(lcString++)=lcBuffer[--lcCounter];
 *lcString=0;

 return (agString);
}
//--------------------------------------------------------------------------------ToString (Integer)
/*METHOD string */
/* Converts an integer number into a string. There must be enough space in the string given as
   argument to store the result. */
template <int prDummy> tcString clString<prDummy>::toString(tyString agString,tyInteger agNumber) {
 tyCharacter lcBuffer[BUFFER_SIZE];

 tyCardinal lcCounter = 0;
 tyInteger  lcModSign = (agNumber<0 ? (-1)%2 : 1);
 tyString   lcString  = agString;

 if (agNumber<0) *(lcString++)='-';

 do {
  lcBuffer[lcCounter++]=tyCharacter(tyCharacter('0')+tyCharacter(lcModSign*(agNumber%10)));
  agNumber/=10;
 }
 while (agNumber!=0);

 while (lcCounter>0) *(lcString++)=lcBuffer[--lcCounter];
 *lcString=0;

 return (agString);
}
//--------------------------------------------------------------------------------ToString (Pointer)
/*METHOD string */
/* Converts a pointer into a string. There must be enough space in the string given as argument to
   store the result. */
template <int prDummy> tcString clString<prDummy>::toString(tyString agString,tyPointer agNumber) {
 tyCardinal lcCounter = sizeof(tyPointer);
 tyString   lcString  = agString;
 tyByte *   lcNumber  = (tyByte *)(&agNumber);

 *(lcString++)='0';
 *(lcString++)='x';

 while (lcCounter>0) {
  *(lcString++)=atHexaCodeS[lcNumber[--lcCounter]/16];
  *(lcString++)=atHexaCodeS[lcNumber[lcCounter]%16];
 }

 *lcString=0;

 return (agString);
}
//-----------------------------------------------------------------------------------ToString (Real)
/*METHOD string */
/* Converts a real number into a string. There must be enough space in the string given as argument
   to store the result. */
template <int prDummy> tcString clString<prDummy>::toString(tyString agString,tyReal agNumber) {
 tyByte      lcBuffer[BUFFER_SIZE];
 tyCardinal  lcComma;
 tyCardinal  lcCounter;
 tyByte      lcDigit;
 tyInteger   lcExp;
 tyCharacter lcSign;
 tyCardinal  lcLast;
 tyInteger   lcLog;

 tyReal     lcDigits = 0.0;
 tyByte *   lcNumber = lcBuffer;
 tyString   lcString = agString;

 // Sign //
 if (agNumber<0) {
  lcSign='-';
  agNumber*=-1.0;
 }
 else lcSign='+';

 // Logarithm //
 if (agNumber==0.0) lcLog=0;
 else lcLog=tyInteger(std::floor(std::log(agNumber)/std::log(10.0)));

 if (lcLog<0) agNumber*=std::pow(tyReal(10.0),tyReal(-lcLog));
 else if (lcLog>0) agNumber/=std::pow(tyReal(10.0),tyReal(lcLog));

 // Digits //
 lcNumber[0]=0;
 lcCounter=0;

 while (lcCounter<=REAL_DECIMAL_PRECISION) {
  lcDigit=tyByte(std::floor(agNumber-lcDigits));
  lcDigits=10*(lcDigits+lcDigit);
  agNumber*=10.0;
  lcNumber[++lcCounter]=lcDigit;
 }

 // Amount Carried //
 if (lcNumber[lcCounter]>=5) {
  lcDigit=1;

  while (lcCounter>0 && lcDigit==1) {
   lcNumber[--lcCounter]+=lcDigit;

   if (lcNumber[lcCounter]==10) {
    lcNumber[lcCounter]=0;
    lcDigit=1;
   }
   else lcDigit=0;
  }
 }

 if (lcNumber[0]==1) ++lcLog;
 else ++lcNumber;

 // String Generation //
 if (lcSign=='-') *(lcString++)='-';

 if (lcLog>=0 && lcLog<6) {
  lcComma=tyCardinal(lcLog)+1;
  lcExp=0;
 }
 else if (lcLog<0 && lcLog>-5) {
  *(lcString++)='0';
  *(lcString++)='.';

  lcCounter=0;
  while (++lcCounter<tyCardinal(-lcLog)) *(lcString++)='0';

  lcComma=0;
  lcExp=0;
 }
 else {
  lcComma=1;
  lcExp=lcLog;
 }

 lcLast=REAL_DECIMAL_PRECISION;
 while (lcLast>1 && lcLast>lcComma && lcNumber[lcLast-1]==0) --lcLast;

 lcCounter=0;

 while (lcCounter<lcLast) {
  *(lcString++)=tyCharacter(tyCharacter('0')+tyCharacter(lcNumber[lcCounter]));
  if (lcCounter==lcComma-1 && lcCounter+1<lcLast) *(lcString++)='.';
  ++lcCounter;
 }

 if (lcExp!=0) {
  *(lcString++)='e';
  if (lcExp>0) *(lcString++)='+';
  toString(lcString,lcExp);
 }
 else *lcString=0;

 return (agString);
}

//----------------------------------------------------------------------------------------ToCardinal
/*METHOD string */ /* Converts a string into a cardinal number. */
template <int prDummy>
tcString clString<prDummy>::toCardinal(tcString agString,tyCardinal & agNumber) {
 tyBoolean lcError = false;

 agNumber=0;
 if (agString==0) return (0);

 while (*agString!=0 && isspace(*agString)) ++agString;

 if (*agString==0) lcError=true;
 else if (*agString=='+') ++agString;

 while (!lcError && *agString!=0 && !isspace(*agString)) {
  if (isdigit(*agString)) agNumber=10*agNumber+(*(agString++)-'0');
  else lcError=true;
 }

 if (lcError) {
  agString=0;
  agNumber=0;
 }

 return (agString);
}
//-----------------------------------------------------------------------------------------ToInteger
/*METHOD string */ /* Converts a string into an integer number. */
template <int prDummy>
tcString clString<prDummy>::toInteger(tcString agString,tyInteger & agNumber) {
 tyBoolean lcError = false;
 tyInteger lcSign  = 1;

 agNumber=0;
 if (agString==0) return (0);

 while (*agString!=0 && isspace(*agString)) ++agString;

 if (*agString==0) lcError=true;
 else if (*agString=='+') ++agString;
 else if (*agString=='-') {
  lcSign=-1;
  ++agString;
 }

 while (!lcError && *agString!=0 && !isspace(*agString)) {
  if (isdigit(*agString)) agNumber=10*agNumber+(*(agString++)-'0');
  else lcError=true;
 }

 agNumber*=lcSign;

 if (lcError) {
  agString=0;
  agNumber=0;
 }

 return (agString);
}
//-----------------------------------------------------------------------------------------ToPointer
/*METHOD string */ /* Converts a string into a pointer. */
template <int prDummy>
tcString clString<prDummy>::toPointer(tcString agString,tyPointer & agNumber) {
 tyCardinal lcCounter = sizeof(tyPointer)*2;
 tyBoolean  lcError   = false;
 tyByte *   lcNumber  = (tyByte *)(&agNumber);

 tyByte lcDigit;

 agNumber=0;
 if (agString==0) return (0);

 while (*agString!=0 && isspace(*agString)) ++agString;

 if (*agString!='0') lcError=true;
 else {
  if (*(++agString)!='x') lcError=true;
  else ++agString;
 }

 while (!lcError && *agString!=0 && !isspace(*agString)) {
  if (isxdigit(*agString)) {
   if (islower(*agString))
    lcDigit=tyByte(*agString-tyCharacter('a')+tyCharacter(10));
   else if (isupper(*agString))
    lcDigit=tyByte(*agString-tyCharacter('A')+tyCharacter(10));
   else lcDigit=tyByte(tyCharacter(*agString)-tyCharacter('0'));

   if (lcCounter>0) {
    --lcCounter;
    ++agString;

    if (lcCounter%2==1) lcNumber[lcCounter/2]=tyByte(16*lcDigit);
    else lcNumber[lcCounter/2]+=lcDigit;
   }
   else lcError=true;
  }
  else lcError=true;
 }

 if (lcCounter!=0) lcError=true;

 if (lcError) {
  agString=0;
  agNumber=0;
 }

 return (agString);
}
//--------------------------------------------------------------------------------------------ToReal
/*METHOD string */ /* Converts a string into a real number. */
template <int prDummy> tcString clString<prDummy>::toReal(tcString agString,tyReal & agNumber) {
 tyBoolean  lcDot   = false;
 tyBoolean  lcError = false;
 tyInteger  lcExp   = 0;
 tyCardinal lcLog   = 0;
 tyInteger  lcSign  = 1;

 agNumber=0;
 if (agString==0) return (0);

 while (*agString!=0 && isspace(*agString)) ++agString;

 if (*agString==0) lcError=true;
 else if (*agString=='+') ++agString;
 else if (*agString=='-') {
  lcSign=-1;
  ++agString;
 }

 while (!lcError && *agString!=0 && !isspace(*agString)) {
  if (isdigit(*agString)) {
   agNumber=10.0*agNumber+(*(agString++)-'0');
   if (lcDot) ++lcLog;
  }
  else if (*agString=='.') {
   lcDot=true;
   ++agString;
  }
  else if (*agString=='e' || *agString=='E') {
   agString=toInteger(++agString,lcExp);
   if (agString==0) lcError=true;
  }
  else lcError=true;
 }

 if (lcError) {
  agString=0;
  agNumber=0.0;
 }
 else {
  if (lcLog>0) agNumber/=std::pow(tyReal(10.0),tyReal(lcLog));

  if (lcExp>0) agNumber*=std::pow(tyReal(10.0),tyReal(lcExp));
  else if (lcExp<0) agNumber/=std::pow(tyReal(10.0),tyReal(-lcExp));

  agNumber*=lcSign;
 }

 return (agString);
}

// End //-------------------------------------------------------------------------------------------
}}
#undef BUFFER_SIZE
#endif