// @topic W040506 substring class with overloaded operators
// @brief class substring implementation

// CIS-255 substring class

#include "substring.h"

// Constructor that takes a reference to a string:
substring::substring( std::string& str )
    :
m_str( str ),
    m_begin( 0 ), // this ctr puts starting pos at the beginning of the string
    m_end( str.length() ) // end of the string
{
    /*
    The substring class is entirely dependent on the std::string object
    which has to be passed to each constructor. It will be refferred to
    as "underlying string object" in our comments.

    DONE: Add initializer list to this constructor, or your code won't
    compile.

    DONE: Add two member variables to accommodate the substring
    positions,

    size_t m_begin;  // absolute offset of the substring beginning
    size_t m_end;    // absolute offset of the substring ending

    and set these positions to match the entire string.
    */
}

// Constructor that takes string reference, beginning, and ending positions
substring::substring( std::string& str, size_t pos_begin, size_t pos_end )
    :
m_str( str ),
    m_begin( pos_begin ),
    m_end( pos_end )
{
    /*
    In addition to std::string object, this constructor
    takes two absolute positions to initialize m_begin
    and m_end offsets of the substring object.

    DONE: Add initializer list to this constructor, or your code won't
    compile.
    */
}

// Return copy of the substring as std::string object
std::string substring::str() const
{
    /*
    This function returns a copy of the string object, initialized
    by the characters of the substring.

    DONE: modify the following statement to construct and return
    the appropriate string:
    */
    //std::string temp = m_str.substr( m_begin, size() );
    return m_str.substr( m_begin, size() );
}

// Return substring size
size_t substring::size() const
{
    /*
    DONE: Calculate and return the length of the substring,
    which is the distance between the beginning and the
    ending positions.
    */
    return m_end - m_begin;
}

// Return beginning position 
size_t substring::begin() const
{
    /*
    DONE: Return the beginning position.
    */
    return m_begin;
}

// Return ending position
size_t substring::end() const
{
    /*
    DONE: Return the ending position.
    */
    return m_end;
}

// Set beginning position 
void substring::begin( size_t pos )
{
    /*
    DONE: Add code to set the beginning position.
    */
    m_begin = pos;
}

// Set ending position
void substring::end( size_t pos )
{
    /*
    DONE: Add code to set the ending position.
    */
    m_end = pos;
}

// The substring content is set to an empty string
void substring::clear()
{
    /*
    Hint: call replace() member function of the underlying string
    object to replace the current substring with an empty string.

    DONE: because clear changes in the underlying substring,
    the function must update the beginning and ending
    positions accordingly:

    BEFORE clear:

    sub.clear();

    m_str: XXXX?????????????????YYYYYYYYY
    |                |
    '-m_begin        `-m_end

    AFTER clear:

    m_str: XXXXYYYYYYYYY
    |
    +--m_end: got adjusted
    |
    '-m_begin
    */
    m_str.replace( m_begin, size(), "" );
    m_end = m_begin;
}

// Replace substring with str
substring& substring::replace( std::string const& str )
{
    /*
    Note that this function follows the logic of std::string::replace().

    DONE: because replace causes changes in the underlying substring,
    the function must update the beginning and ending
    positions according to the length of the new content.
    For example,

    BEFORE replace:

    sub.replace( "abc" );

    m_str: XXXX?????????????????XXXXXXXXXX
    |                |
    '-m_begin        `-m_end

    AFTER replace:

    m_str: XXXXabcXXXXXXXXXX
    |  |
    |  `-m_end: got adjusted
    '-m_begin
    */
    m_str.replace( m_begin, size(), str );
    m_end = m_begin + str.length();
    return *this;
}

// Fill substring with a particular character
substring& substring::fill( char ch )
{
    /*
    DONE: write a loop that will replace every character that
    belongs to the substring with the character passed here
    as the input parameter.

    Hint: use subscript access to the underlying string object:

    m_str[ position ] = ch;

    where position is every offset that belongs to the open
    range
    m_begin <= position < m_end
    */
    size_t position = m_begin;
    for ( ; position < m_end; ++position ) {
        m_str[ position ] = ch;
    }
    return *this;
}

// Insert str at the specified position:
substring& substring::insert( std::string const& str, size_t pos )
{
    /*
    Hint: use insert() member function of std::string.

    TODO: because insert causes changes in the underlying substring,
    the function must compare its beginning and ending against the
    insertion position and the length of the string being inserted.
    The substring needs to adjust its beginning and ending positions
    as demonstrated by the following diagrams:

    Example 1.
    BEFORE insert:

    insert( "abc", pos )
    |
    .--'
    |
    m_str: XXXX?????????????????XXXXXXXXXX
    |                |
    '-m_begin        `-m_end

    AFTER insert:
    |
    m_str: XXabcXX?????????????????XXXXXXXXXX
    |                |
    '-m_begin:       `-m_end:
    got adjusted     got adjusted

    Example 2.
    BEFORE insert:

    insert( "abc", pos )
    |
    `-------.
    |
    m_str: XXXX?????????????????XXXXXXXXXX
    |                |
    '-m_begin        `-m_end

    AFTER insert:
    |
    m_str: XXXX?????????abc???????XXXXXXXXXX
    |                  |
    '-m_begin          `-m_end: got adjusted


    IMPORTANT: Make your own decision regarding two special cases
    when the insertion occurs exactly at the beginning, or exactly
    at the ending of the substring. Make sure to document your
    coding in the comments of your program.
    */
    return *this;
}

// Convert substring to UPPER CASE
substring& substring::toupper()
{
    /*
    TODO: write a loop that will replace every character that
    belongs to the substring with the corresponding UPPER CASE
    character.

    Hint: use toupper() library function and and subscript
    access to the underlying string object:

    toupper( m_str[ position ] );

    where position is every offset that belongs to the open
    range
    m_begin <= position < m_end
    */
    return *this;
}

// Convert substring to lower case
substring& substring::tolower()
{
    /*
    Similar to the above.
    */
    return *this;
}

// Search for a new substring starting at the specified position:
size_t substring::search( const std::string& str, size_t pos )
{
    /*
    TODO: use find() of the underlying string and update the beginning
    and the ending positions of the substring.

    TODO: when find() call fails, it returns back the member value
    std::string::npos. In such case the substring must not update
    its positions and also return std::string::npos back to the caller:
    */
    return std::string::npos;
}

// The string content is set to the size of entire string
void substring::expand()
{
    /*
    TODO: Update substring positions to match the entire
    underlying string.
    */
}

// Equality test between two substrings
bool substring::equals_to( substring const& other ) const
{
    /*
    TODO: Compare this substring with another character by character.
    The test should succeed if every character in both substrings
    matches exactly.

    Return true, if the underlying text fragments are the same.
    Return false otherwise.
    */
    return false;
}

// Equality test between substring and std::string
bool substring::equals_to( std::string const& str ) const
{
    /*
    TODO: Compare this substring with a string character by character.
    The test should succeed if every character matches exactly in both.

    Return true, if the underlying text fragments are the same.
    Return false otherwise.
    */
    return false;
}

// Merge with another substring
substring& substring::merge( substring const& other )
{
    /*
    TODO: Modify the substring positions so that
    new beginning = min( this->beginning, other.beginning )
    new ending = max( this->ending, other.ending )
    */
    return *this;
}

// Swap values with another substring
void substring::swap( substring& other )
{
    /*
    TODO: Replace content of this substring with the
    content of the other, and vice versa.

    TODO: Make sure that the substrings don't overlap.
    Save the content of both substrings in two std::string
    objects and then invoke replace.
    */
}

// Prepare substring for parsing
substring& substring::parse()
{
    /*
    This function initializes the substring preparing the object
    for character parsing.

    TODO: add two new member variables to the definition
    of the substring class:

    // Character parsing state variables:
    size_t m_parse_begin; // position where parsing starts
    bool m_parse_success; // flag to indicate the parsing succeeded

    and set them here as follows:

    m_parse_begin = m_begin;
    m_parse_success = true;

    Thus, parse() remembers the position at which the character
    matching starts:

    m_str: XXXX?????????????????XXXXXXXXXX
    |                |
    +-m_begin        `-m_end
    |
    `-m_parse_begin
    m_parse_success == true
    */
    return *this;
}

// Test if substring is alphabetic
substring& substring::isalpha()
{
    /*
    TODO: if m_parse_success is equal to false, the function
    should do nothing and return immediately:

    if ( m_parse_success == false ) {
    // Nothing to do: the test has already failed prior
    // to this call:
    return *this;
    }

    // Parser state is ok, proceed:
    // ...


    TODO: use character by character scanning to test
    the characters of the underlying substring as follows:

    if ( isalpha( m_str[ position ] ) )

    The code uses standard library character classification
    function isalpha(ch), which returns non-zero if character ch
    is alphabetic, zero if not.

    After successful test m_begin position should be rolled forward
    to point to the next unparsed character in the underlying substring
    as shown by the following diagram:

    parsed characters                   unparsed characters
    \____  ____________/
    \   \/           /
    m_str: XXXXabcd????????????XXXXXXXXXX
    |   |           |
    |   `-m_begin   `-m_end
    |
    `-m_parse_begin
    m_parse_success == true

    IMPORTANT: the test should stop immediately as soon as m_end position
    is reached.

    The success of the test is measured by the number of good matches.
    If one or more matches are found, the function rolls m_begin forward
    and returns.

    If no good matches are found (or if the substring is empty),
    the function should set m_parse_success flag to false and return:

    m_parse_success = false;
    return *this;

    */
    return *this;
}

// Test if substring is upper case
substring& substring::isupper()
{
    /*
    TODO: implement exactly the same logic as substring::isalpha(),
    but using standard library character classification
    function isupper(ch), which returns non-zero if character ch
    is UPPER CASE, zero if not.

    TODO: Remember to check the parser state at the very beginning:

    if ( m_parse_success == false ) {
    // Nothing to do: the test has already failed prior
    // to this call:
    return *this;
    }

    // Parser state is ok, proceed:
    // ...


    */
    return *this;
}

// Test if substring is lower case
substring& substring::islower()
{
    /*
    TODO: implement exactly the same logic as substring::isalpha(),
    but using standard library character classification
    function islower(ch), which returns non-zero if character ch
    is lower case, zero if not.
    */
    return *this;
}

// Test if substring is digit
substring& substring::isdigit()
{
    /*
    TODO: implement exactly the same logic as substring::isalpha(),
    but using standard library character classification
    function isdigit(ch), which returns non-zero if character ch
    is digit, zero if not.
    */
    return *this;
}

// Test if substring is alphabetic or digit
substring& substring::isalnum()
{
    /*
    TODO: implement exactly the same logic as substring::isalpha(),
    but using standard library character classification
    function isalnum(ch), which returns non-zero if character ch
    is alphabetic or digit, zero if not.
    */
    return *this;
}

// Test if substring is blank, tab, newline, return, formfeed, or vertical tab 
substring& substring::isspace()
{
    /*
    TODO: implement exactly the same logic as substring::isalpha(),
    but using standard library character classification
    function isspace(ch), which returns non-zero if character ch
    is blank, tab, newline, return, formfeed, vertical tab, zero if not.
    */
    return *this;
}

// Test if substring is matches text specified by str 
substring& substring::isstr( const std::string& str )
{
    /*
    TODO: implement exactly the same logic as substring::isalpha(),
    but compare the underlying substring character by character
    with the text provided in str parameter.
    */
    return *this;
}

// Test if substring is empty
substring& substring::isempty()
{
    /*
    TODO: This test succeeds if the underlying substring is empty,
    fails otherwise:

    if ( m_parse_success == false ) {
    // Nothing to do: the test has already failed prior
    // to this call:
    return *this;

    } else if ( m_begin == m_end ) {
    // Success: the substring is empty:
    return *this;

    } else {
    // Test failed: the substring is not empty:
    m_parse_success = false;
    return *this;
    }
    */
    return *this;
}

// Return true if parsing succeeded, false otherwise
bool substring::succeeded() const
{
    /*
    TODO: simply return the parser result to the caller:

    return m_parse_success;
    */
    return false;
}

// Return matched substring
substring substring::match() const
{
    /*
    TODO: construct and return the substring object that
    matches the substring of successfully parsed characters:

    parsed characters                   unparsed characters
    \____  ____________/
    \   \/           /
    m_str: XXXXabcd????????????XXXXXXXXXX
    |   |           |
    |   `-m_begin   `-m_end
    |
    `-m_parse_begin
    m_parse_success == true

    This is easy, all you have to write is:

    return substring( m_str, m_parse_begin, m_begin );

    */
    return *this;
}

// Undo changes caused by parsing
substring& substring::unparse()
{
    /*
    TODO: restore the original m_begin position of the substring
    and set the parsing flag to true:

    m_begin = m_parse_begin;
    m_parse_success = true;

    Thus, unparse() prepares the substring for another parsing
    attempt:

    m_str: XXXX?????????????????XXXXXXXXXX
    |                |
    +-m_begin        `-m_end
    |
    `-m_parse_begin
    m_parse_success == true
    */
    return *this;
}

//----------------------------------------------
// Operators overloaded as members functions:
//----------------------------------------------

// Assignments:
substring& substring::operator=( substring const& other )
{
    if ( this == &other ) {
        // avoid self-assignment
        return *this;
    }
    replace( other.str() );
    return *this;
}

substring& substring::operator=( std::string const& str )
{
    replace( str );
    return *this;
}

// Merge with another substring:
substring& substring::operator+=( substring const& other )
{
    merge( other );
    return *this;
}

// Compare with another substring:
// ( us > other )
bool substring::operator>( substring const& other ) const
{
    // ( m_str > other.m_str ) +
    return
        (
            0 < m_str.compare(
                m_begin,
                size(),
                other.m_str,
                other.m_begin,
                other.size()
                )
        );
}

bool substring::operator<( substring const& other ) const
{
    return
        (
            0 > m_str.compare(
                m_begin,
                size(),
                other.m_str,
                other.m_begin,
                other.size()
                )
        );
}

// Subscript access to individual characters within substring:
char& substring::operator[]( size_t idx )
{
    return m_str[ m_begin + idx ];
}

char substring::operator[]( size_t idx ) const
{
    return m_str[ m_begin + idx ];
}

// Operators overloaded as non-member functions:
std::ostream& operator<<( std::ostream& os, substring const& sub )
{
    os << sub.str();
    return os;
}


// Equality/Inequality tests between two substrings:
bool operator==( substring const& one, substring const& another )
{
    //return one.str() == another.str();
    return
        (
            0 == one.m_str.compare(
                one.m_begin,
                one.size(),
                another.m_str,
                another.m_begin,
                another.size()
                )
        );
}

bool operator!=( substring const& one, substring const& another )
{
    return !operator==( one, another );
    //return !( one == another );
}

// Equality/Inequality tests between substring and std::string:
bool operator==( std::string const& str, substring const& sub )
{
    //return str == sub.str();
    return
        (
            0 == str.compare(
                0,
                str.length(),
                sub.m_str,
                sub.m_begin,
                sub.size()
                )
        );
}

bool operator==( substring const& sub, std::string const& str )
{
    return operator==( str, sub );
}

bool operator!=( std::string const& str, substring const& sub )
{
    return !operator==( str, sub );
}

bool operator!=( substring const& sub, std::string const& str )
{
    return !operator==( str, sub );
}

// Equality/Inequality tests between substring and zero-terminated string:
bool operator==( substring const& sub, char const* cstr )
{
    //return cstr == sub.str();
    return
        (
            0 == sub.m_str.compare(
                sub.m_begin,
                sub.size(),
                cstr
                )
        );
}

bool operator!=( substring const& sub, char const* cstr )
{
    return !operator==( sub, cstr );
}

bool operator==( char const* cstr, substring const& sub )
{
    return operator==( sub, cstr );
}

bool operator!=( char const* cstr, substring const& sub )
{
    return operator==( sub, cstr );
}