// textUtility.cpp

#include "textUtility.h"    // includes <iostream> and <string>


/*
 * int digitCount(int n)
 *
 * Counts base-10 digits in the specifed integer.
 *
 * Parameter:
 *    n - the integer whose digits are to be counted.
 *
 * Returns:
 *    The number of digits in a base-10
 *    representation of n.
 */
int digitCount(int n)
{
   if ( n < 0 )
      n = 0 - n;

   int numberOfDigits = 1;

   while ( n >= 10 )  {
      n = n / 10;
      numberOfDigits++;
   }  // while

   return numberOfDigits;
}  // function digitCount


/*
 * void printRightJustified(int numberToPrint,
 *                          int columnWidth,
 *                          ostream& output)
 *
 * Prints an integer with a suitable number
 * of leading spaces so that it can be lined
 * up in a column with a specified width,
 * provided that width is at least equal to
 * the number of digits in the printed number.
 *
 * Parameters:
 *    numberToPrint - the integer to print.
 *    columnWidth - total number of characters
 *        on each line of the column, including
 *        leading spaces.  Must be at least
 *        equal to the number of digits in
 *        numberToPrint.
 *    output - an output stream
 *        Precondition:  The state of output
 *           is true, and numberToPrint and
 *           its leading spaces have not yet
 *           been printed.
 *        Postcondition:  The state of output
 *           is still true, and numberToPrint
 *           has been printed.
 */
void printRightJustified(int numberToPrint,
                         int columnWidth,
                         ostream& output)
{
   int nonSpaceCharacters
            = digitCount(numberToPrint)
               + ( ( numberToPrint < 0 ) ? 1 : 0 );
   int leadingSpaces
            = columnWidth - nonSpaceCharacters;

   for ( int i = 0; i < leadingSpaces; i++ )
      output << " ";

   output << numberToPrint;
}  // function printRightJustified(int, int, ostream&)


/*
 * void printRightJustified(float numberToPrint,
 *                          int decimalPlaces,
 *                          int columnWidth,
 *                          ostream& output)
 *
 * Prints a floating-point number in fixed-point
 * format with a suitable number of leading
 * spaces so that it can be lined up in a column
 * with a specified width, provided that width
 * is at least one greater than the total the
 * number of digits in the printed number,
 * including digits in whole number portion and
 * the requested number of decimal places. and
 * provided the total number of digits does not
 * exceed the precision of the machine's
 * floating-point format.
 *
 * Parameters:
 *    numberToPrint - a floating-point number
 *        to be printed in fixed-point format.
 *        The total of the number of digits in
 *        the whole number portion plus
 *        decimalPlaces should not exceed the
 *        precision of the machine's
 *        floating-point format.  (If it does,
 *        the least significant digits will be
 *        meaningless.
 *    decimalPlaces - number of digits printed
 *        to the right of the decimal point.
 *    columnWidth - total number of characters
 *        on each line of the column, including
 *        leading spaces.  Must be at least
 *        one greater than the total number of
 *        printed digits.
 *    output - an output stream
 *        Precondition:  The state of output
 *           is true, and numberToPrint and
 *           its leading spaces have not yet
 *           been printed.
 *        Postcondition:  The state of output
 *           is still true, and numberToPrint
 *           has been printed.
 */
void printRightJustified(float numberToPrint,
                         int decimalPlaces,
                         int columnWidth,
                         ostream& output)
{
   bool negative = ( numberToPrint < 0 );

   if ( negative )
      numberToPrint = 0 - numberToPrint;

   // Assertion:  numberToPrint is now non-negative.
   // Its original sign is remembered by boolean
   // variable negative.

   // Print leading spaces, whole number part,
   // and decimal point:
   int wholeNumberPart = (int) numberToPrint;
   int nonSpaceCharacters
            = digitCount(wholeNumberPart)
               + ( negative ? 1 : 0 )    // minus sign
               + 1    // decimal point
               + decimalPlaces;
   int leadingSpaces
            = columnWidth - nonSpaceCharacters;
   for ( int i = 0; i < leadingSpaces; i++ )
      output << " ";
   output << (negative ? "-" : "" )
                << wholeNumberPart << ".";

   // Generate fractional part:
   float fractionalPart
             = numberToPrint - wholeNumberPart;
   for ( int i = 0; i < decimalPlaces; i++ )
      fractionalPart = fractionalPart * 10;
   int fractionalPartToPrint = (int) fractionalPart;

   // Print fractional part with leading zeroes, if any:
   int nonZeroDigits = digitCount(fractionalPartToPrint);
   int leadingZeroes = decimalPlaces - nonZeroDigits;
   for ( int i = 0; i < leadingZeroes; i++ )
      output << "0";
   output << fractionalPartToPrint;
}  // function printRightJustified(float, int, int, ostream&)


/*
 * void pause()
 *
 * Pauses the display. 
 * Prompts the user to press ENTER to continue.
 * Lets the program continue after the user presses ENTER.
 *
 * Global variables:
 *    cin -- an istream object for interactive input.
 *           cin is an external variable, declared
 *           in library header file <iostream>.
 *       Precondition:  The state of cin is true.  (All
 *           previous interactive inputs were successful.)
 *       Postcondition:  The state of cin is still true,
 *    cout -- an ostream object for output to the terminal.
 *           cout is an external variable, declared
 *           in library header file <iostream>.
 *       Precondition:  The state of cout is true.
 *       Postcondition:  The state of cout is still true.
 */
void pause()
{
   cout << endl << "Press ENTER to continue....";
   string line;
   getline(cin, line);
   cout << endl;
}  // function pause


/*
 * bool isAscii(char x)
 *
 * Tests whether the specified character is
 * in the ASCII range.
 *
 * Parameter:
 *    x - the character (byte) to be tested.
 *
 * Returns:
 *    true if x is in the ASCII range (0 to 127),
 *    false otherwise.
 */
bool isAscii(char x)
{
   return( x >= 0 );
}  // function isAscii


/*
 * bool isAsciiDigit(char x)
 *
 * Tests whether the specified character is an ASCII digit.
 *
 * Parameter:
 *    x - the character (byte) to be tested.
 *
 * Returns:
 *    true if x is an ASCII digit ('0' to '9'),
 *    false otherwise.
 */
bool isAsciiDigit(char x)
{
   return( (x >= '0' && x <= '9'));
}  // function isAsciiDigit


/*
 * bool isAsciiLetter(char x)
 *
 * Tests whether the specified character is an ASCII letter.
 *
 * Parameter:
 *    x - the character (byte) to be tested.
 *
 * Returns:
 *     true if x is an ASCII letter
 *     ('A' to 'Z' or 'a' to 'z'),
 *     false otherwise.
 */
bool isAsciiLetter(char x)
{
   return( (x >= 'A' && x <= 'Z') ||
           (x >= 'a' && x <= 'z'));
}  // function isAsciiLetter


/*
 * bool isSpace(char x)
 *
 * Tests whether the specified character is a space.
 *
 * Parameter:
 *    x - the character (byte) to be tested.
 *
 * Returns:
 *    true if x is a space (' '),
 *    false otherwise.
 */
bool isSpace(char x)
{
   return ( x == ' ' );
}  // function isSpace


/*
 * bool isAsciiControl(char x)
 *
 * Tests whether the specified character is an
 * ASCII control character.
 *
 * Parameter:
 *    x - the character (byte) to be tested.
 *
 * Returns:
 *    true if x is an ASCII control
 *    character (0 to 31, or 127),
 *    false otherwise.
 */
bool isAsciiControl(char x)
{
   return( x < 32 || x == 127 );
}  // function isAsciiControl


/*
 * bool containsAsciiControl(const string& text)
 *
 * Tests whether the specified string contains
 * ASCII control characters.
 *
 * Parameter:
 *    text -  the string to be tested.
 *
 * Returns:
 *    true if text contains one or more
 *    ASCII control characters,
 *    false otherwise.
 */
bool containsAsciiControl(const string& text)
{
   // Search for an ASCII control character,
   // and return true when one is found:
   for ( int i = 0; i < text.length(); i++ )
      if ( isAsciiControl(text[i]) )
          return true;

   // Assertion:  If we have reached this point,
   // text contains no ASCII control characters.

   return false;
}  // function containsAsciiControl


/*
 * bool isNaturalNumber(const string& text)
 *
 * Tests whether the specified string represents
 * a natural number, i.e. a non-negative integer.
 * A string represents a natural number if, and
 * only if, it contains at least one character
 * and all its characters are digits.
 *
 * Parameter:
 *    text -  the string to be tested.
 *
 * Returns:
 *    true if text represents a natural number,
 *    false otherwise.
 */
bool isNaturalNumber(const string& text)
{
   // This function has not yet been written.
   // You will write it for homework.

   cout << "Function isNaturalNumber has not "
         << "yet been implemented." << endl;
   return true;
}  // function isNaturalNumber


/*
 * int toDigitValue(char x)
 *
 * Converts a digit character to its
 * intended numeric value.
 *
 * Parameter:
 *    x - the character to be converted
 *
 * Returns:
 *    the intended numeric value of the
 *    character x, if x is a digit.
 *    Returns -1 if x is not a digit.
 */
int toDigitValue(char x)
{
   if ( isAsciiDigit(x) )
      return ( x - '0' );
   else
      return  -1;
}  // function toDigitValue


/*
 * bool parseNaturalNumber(const string& text,
 *                         int& number)
 *
 * Converts a string to a non-negative integer, if the
 * string consists solely of digit characters.
 *
 * Parameters:
 *    text - the string to be converted/
 *    number -  the intended natural number value.
 *       Precondition: none
 *       Postcondition:  number is the intended
 *          integer value represented by the string,
 *          if the string represents a valid natural
 *          number value.  Otherwise, number is
 *          unchanged.
 *
 * Returns:
 *    true if a non-negative integer was successfully
 *    read from text, false otherwise.
 */
bool parseNaturalNumber(const string& text,
                        int& number)
{
   // Check validity of text:
   if ( !isNaturalNumber(text) )    // stub - doesn't work yet
      return false;

   // Assertion:  If we have reached this point,
   // text represents a valid natural number.

   // Read from text the natural number that
   // it represents:
   number = 0;
   for ( int i = 0; i < text.length(); i++ )
      number = (number * 10) + toDigitValue(text[i]);
   return true;
}  // method parseNaturalNumber