//*****************************************************************************
//
//			STring.C
//
//*****************************************************************************
//
//      Copyright (C) 1996 
//
//                      Ronald A. MacCracken
//			Graduate Student
//                      Computer Science Department
//                      University of California
//                      Davis, CA  95616
//
//      Permission is granted to use at your own risk and 
//      distribute this software in source and binary forms 
//      provided the above copyright notice and this paragraph 
//      are preserved on all copies.  This software is provided 
//      "as is" with no express or implied warranty.
//
//*****************************************************************************

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stream.h>
#include <iomanip.h>
#include <strstream.h>
#include "STring.h"


const STring	NullString ;


//
// Constructors and Destructors
//

STring :: STring ( void )
{
    length = 0 ;
    str    = NULL ;
}


STring :: STring ( const int x )
{
    ostrstream os ;

    os << x << ends ;

    char* s = os.str( ) ;

    length = strlen ( s ) ;
    str    = new char[length+1] ;

    for ( int i = 0 ; i < length ; ++i )
	str[i] = s[i] ;

    str[length] = '\0' ;

    delete s ;
}


STring :: STring ( const double& x )
{
    ostrstream os ;

    os << x << ends ;

    char* s = os.str ( ) ;

    length = strlen ( s ) ;

    str = new char[length+1] ;

    for ( int i = 0 ; i < length ; ++i )
	str[i] = s[i] ;

    str[length] = '\0' ;

    delete s ;
}


STring :: STring ( const char c ) 
{
    if ( c == '\0' )
    {
	length = 0 ;

	str    = new char[1] ;
	str[0] = '\0' ;
    }
    else
    {
	length = 1 ;

	str    = new char[2] ;
	str[0] = c ;
	str[1] = '\0' ;
    }
}


STring :: STring ( const char* s ) 
{
    length = strlen ( s ) ;

    if ( length != 0 )
    {
	str = new char[length+1] ;

	for ( int i = 0 ; i <= length ; ++i )
	    str[i] = s[i] ;
    }
    else
	str = NULL ;
}


STring :: STring ( const STring& s )
{
    Copy ( s ) ;
}


STring :: ~STring ( void )
{
    Delete ( ) ;
}


//
// Private Member Functions
//

void	STring :: Copy ( const STring& s )
{
    length = s.length ;

    if ( length == 0 )
    {
	str = NULL ;
    }
    else
    {
	str = new char[length+1] ;

	for ( int i = 0 ; i <= length ; ++i )
	    str[i] = s.str[i] ;
    }
}


void	STring :: Delete ( void )
{
    if ( str != NULL )
    {
	delete [] str ;

	str = NULL ;
    }
}


//
// Public Member Functions
//

STring&	STring :: operator= ( const STring& s )
{
    if ( this == &s ) 
	return ( *this ) ;

    Delete ( ) ;

    Copy ( s ) ;

    return ( *this ) ;
}


STring&	STring :: operator+= ( const STring& s )
{
    if ( s.length == 0 )
	 return ( *this ) ;
	
    char* p = str ;


    // allocate enough memory for size of combined string

    str = new char [ length + s.length + 1 ] ;


    // copy first string into new string

    int	k = 0 ;

    for ( int i = 0 ; i < length ; ++i , ++k )
    {
	str[k] = p[i] ;
    }


    // copy second string into new string

    for ( int j = 0 ; j < s.length ; ++j , ++k )
    {
	str[k] = s.str[j] ;
    }


    length += s.length ;

    str[length] = '\0' ;

    delete p ;
    
    return ( *this ) ;
}


STring	STring :: operator() ( const int i , const int n ) const
{
    // make sure indices make sense

    assert ( ( 0 <= i ) && ( i <= n ) && ( n <= length ) ) ;


    // copy substring of length n starting at index i into new string

    STring s ;

    s.length = n - i;

    s.str    = new char [ s.length + 1 ] ;

    for ( int j = 0 , k = i ; j < s.length ; ++j , ++k ) 
    {
	s.str[j] = str[k] ;
    }

    s.str[s.length] = '\0' ;

    return ( s ) ;
}


char&	STring :: operator[] ( const int idx )
{
    assert ( ( idx >= 0 ) && ( idx < length ) ) ;

    return ( str[idx] ) ;
}


const char& STring :: operator[] ( const int idx ) const
{
    assert ( ( idx >= 0 ) && ( idx < length ) ) ;

    return ( str[idx] ) ;
}


char*	STring :: Get_String ( void ) const
{
    char* s = new char[length+1] ;

    for ( int i = 0 ; i <= length ; ++i )
	s[i] = str[i] ;

    return ( s ) ;
}


int	STring :: Has_Substring ( const STring& s ) const
{
    if ( s.length == 0 ) 
	return ( 0 ) ;

    int n = length - s.length ;

    for ( int i = 0 ; i <= n ; ++i ) 
    {
	int found = TRUE ;

	for ( int j = i , k = 0 ; k < s.length ; ++j, ++k ) 
	{
	    if ( str[j] != s.str[k] ) 
	    {
		found = FALSE ;
		break ;
	    }
        }

	if ( found ) 
            return ( i ) ;
    }

    return ( NO_SUBSTRING ) ;
}

int	STring :: Has_Suffix ( const STring& s ) 
{
    // zero length, we call it a suffix
    if ( s.length == 0 ) 
	return ( 1 ) ;

    // if s is longer, it cannot be a suffix of our string
    if ( s.length > length )
        return ( 0 ) ;

    // now just check everything
    int s_i = s.length - 1;
    int m_i = length - 1;

    while ( s_i >= 0 ) {
        if( s.str[s_i] != str[m_i] ) return 0;
        s_i--;
        m_i--;
    }
    return 1;
}


int 	STring :: Is_Numeric ( void ) const
{
    if ( strspn ( str , "0123456789.+-e)" ) != length )
	return ( FALSE ) ;
    else
	return ( TRUE ) ;
}


int	STring :: Int_Value ( void ) const 
{
    assert ( length != 0 ) ;

    assert ( Is_Numeric ( ) ) ;

    return ( atoi ( str ) ) ;
}


double	STring :: Double_Value ( void ) const
{
    assert ( length != 0 ) ;

    assert ( Is_Numeric ( ) ) ;

    return ( atof ( str ) ) ;
}


void	STring :: To_Lower_Case ( void )
{
    for ( int i = 0 ; i <= length ; ++i )
	str[i] = tolower ( str[i] ) ;
}


void	STring :: To_Upper_Case ( void )
{
    for ( int i = 0 ; i <= length ; ++i )
	str[i] = toupper ( str[i] ) ;
}


//
// Friend Functions
//

STring	operator+ ( const STring& s1 , const STring& s2 )
{
    STring result ( s1 ) ;

    result += s2 ;

    return ( result ) ;
}


int	operator== ( const STring& s1 , const STring& s2 )
{
    return ( strcmp ( s1.str , s2.str ) == 0 ) ;
}


int	operator!= ( const STring& s1 , const STring& s2 )
{
    return ( strcmp ( s1.str , s2.str ) != 0 ) ;
}


int	operator< ( const STring& s1 , const STring& s2 )
{ 
    return ( strcmp ( s1.str , s2.str ) < 0 ) ;
}


int	operator<= ( const STring& s1 , const STring& s2 )
{ 
    return ( strcmp ( s1.str , s2.str ) <= 0 ) ;
}


int	operator> ( const STring& s1 , const STring& s2 )
{ 
    return ( strcmp ( s1.str , s2.str ) > 0 ) ;
}


int	operator>= ( const STring& s1 , const STring& s2 )
{ 
    return ( strcmp ( s1.str , s2.str ) >= 0 ) ;
}

istream&	operator>> ( istream& co , STring& s )
{
  int ch;
  int i = 0;
  register streambuf *sb = co.rdbuf();
  while ((ch = sb->sbumpc()) != EOF)
  {
    if (ch=='\n') break;
    s += (char)ch;
  }
  int new_state = co.rdstate();
  if (i == 0) new_state |= ios::failbit;
  if (ch == EOF) new_state |= ios::eofbit;
  co.clear(new_state);

  return ( co ) ;
}

ostream&	operator<< ( ostream& co , const STring& s )
{
    if ( s.str == NULL )
	co << "NullString" ;
    else
	co << s.str ;

    return ( co ) ;
}


