/**
 * @file      lib.AbstractBaseString.hpp
 * @author    Sergey Baigudin, sergey@baigudin.software
 * @copyright 2017-2022, Sergey Baigudin, Baigudin Software
 */
#ifndef LIB_ABSTRACTBASESTRING_HPP_
#define LIB_ABSTRACTBASESTRING_HPP_

#include "lib.Object.hpp"
#include "api.String.hpp"
#include "lib.CharTrait.hpp"

namespace eoos
{
namespace lib
{

/**
 * @class AbstractBaseString<T,R,A>
 * @brief Abstract base string class.
 *
 * @tparam T A data type of string characters.
 * @tparam R A character traits.
 * @tparam A A heap memory allocator class.
 */
template <typename T, class R, class A = Allocator>
class AbstractBaseString : public Object<A>, public api::String<T>
{
    typedef Object<A> Parent;

public:

    using api::String<T>::getLength;

    /**
     * @brief Destructor.
     */
    virtual ~AbstractBaseString()
    {
    }

    /**
     * @copydoc eoos::api::Object::isConstructed()
     */
    virtual bool_t isConstructed() const ///< SCA MISRA-C++:2008 Defected Rule 9-3-3
    {
        return Parent::isConstructed();
    }

    /**
     * @copydoc eoos::api::Collection::isEmpty()
     */
    virtual bool_t isEmpty() const ///< SCA MISRA-C++:2008 Defected Rule 9-3-3
    {
        bool_t res;
        size_t const length( getLength() );
        if(length == 0U)
        {
            res = true;
        }
        else
        {
            res = false;
        }
        return res;
    }

    /**
     * @copydoc eoos::api::String::copy(const api::String<T>&)
     */
    virtual bool_t copy(api::String<T> const& string)
    {
        bool_t res;
        if( isConstructed() && string.isConstructed() )
        {
            T const* const str( string.getChar() );
            res = copyRaw(str);
        }
        else
        {
            res = false;
        }
        return res;
    }

    /**
     * @copydoc eoos::api::String::concatenate(const api::String<T>&)
     */
    virtual bool_t concatenate(api::String<T> const& string)
    {
        bool_t res;
        if( isConstructed() && string.isConstructed() )
        {
            T const* const str( string.getChar() );
            res = concatenateRaw(str);            
        }
        else
        {
            res = false;            
        }
        return res;
    }

    /**
     * @copydoc eoos::api::String::isEqualTo(const api::String<T>&)
     */
    virtual bool_t isEqualTo(api::String<T> const& string) const
    {
        bool_t res;
        if( isConstructed() && string.isConstructed() )
        {
            T const* const str( string.getChar() );
            res = isEqualToRaw(str);   
        }
        else
        {
            res = false;            
        }
        return res;
    }
    
    /**
     * @brief Converts an integer number to this string.
     *
     * The function converts an integer value into a character string using the base parameter,
     * which has to be 2, 8, 10, or 16 based numerals for converting to an appropriate numeral system.
     *
     * Mark that only if the base is decimal, a passed number is available to be negative values,
     * and the resulting string of these values is preceded with a minus sign. In addition,
     * a hexadecimal number includes lower case characters, and any resulting strings do not contain
     * any suffixes or prefixes for identifying a numeral system.
     *
     * @note You need to use `AbstractBaseString.template convert<I>(value, base);` syntax,
     * if you have to specify the template argument type explicitly.
     *
     * @param value A value that would be converted to this string.
     * @param base  A numerical base used to represent a value as this string.
     * @return True if the conversion has been completed successfully.
     */
    template <typename I>
    bool_t convert(I const value, Number::Base const base = Number::BASE_10) ///< SCA MISRA-C++:2008 Defected Rule 9-3-3
    {
        return convertToString(value, base);
    } 

    /**
     * @brief Assignment by sum operator.
     *
     * @param source A source object interface.
     * @return Reference to this object.
     */
    AbstractBaseString& operator+=(api::String<T> const& source)
    {
        static_cast<void>( concatenate(source) );
        return *this;
    }

    /**
     * @brief Assignment by sum operator.
     *
     * @param source A source character string.
     * @return Reference to this object.
     */
    AbstractBaseString& operator+=(T const* const source)
    {
        static_cast<void>( concatenateRaw(source) );
        return *this;
    }    

protected:

    /**
     * @brief Constructor.
     */
    AbstractBaseString()
        : Object<A>()
        , api::String<T>() {
    }

    /**
     * @copydoc eoos::Object::Object(Object const&)
     */
    AbstractBaseString(AbstractBaseString const& obj)
        : Object<A>(obj)
        , api::String<T>() {
    }

    /**
     * @copydoc eoos::Object::operator=(Object const&)
     */       
    AbstractBaseString& operator=(AbstractBaseString const& obj)
    {
        if( isConstructed() && (this != &obj) )
        {
            Parent::operator=(obj);
        }
        return *this;
    }    

    #if EOOS_CPP_STANDARD >= 2011

    /**
     * @copydoc eoos::Object::Object(Object&&)
     */       
    AbstractBaseString(AbstractBaseString&& obj) noexcept 
        : Object<A>( move(obj) )
        , api::String<T>(){
    }   

    /**
     * @copydoc eoos::Object::operator=(Object&&)
     */
    AbstractBaseString& operator=(AbstractBaseString&& obj) & noexcept
    {
        if( isConstructed() && (this != &obj) )
        {
            Parent::operator=( move(obj) );            
        }        
        return *this;
    }        

    #endif // EOOS_CPP_STANDARD >= 2011

    /**
     * @brief Returns a calculated string length.
     *
     * @param str A character string would be measured.
     * @return A length of the passed string.
     */
    static size_t getLengthRaw(T const* str)
    {
        size_t len( 0U ); 
        T const null( R::getTerminator() );
        while( *str != null )
        {
            ++str; ///< SCA MISRA-C++:2008 Justified Rule 5-0-15
            ++len;
        }
        return len;
    }

    /**
     * @brief Copies a string to a string.
     *
     * @param dst A destination array where the content would be copied.
     * @param src A character string to be copied.
     * @param cnt Destination character string size.     
     */
    static void copyRaw3(T* dst, T const* src, size_t cnt)
    {
        if( (dst != NULLPTR) && (src != NULLPTR) )
        {
            T const null( R::getTerminator() );
            while(true)
            {
                if( cnt == 0U )
                {
                    *dst = null;
                    break;
                }     
                *dst = *src;
                --cnt;
                if( *dst == null )
                {
                    break;
                }
                ++dst;
                ++src;
            }
        }
    }

    /**
     * @brief Concatenates two strings.
     *
     * @param dst A destination character string where the content would be appended.
     * @param src An appended character string.
     * @param cnt Destination character string length that cannot be less than dst string length.     
     */
    static void concatenateRaw3(T* dst, T const* src, size_t cnt)
    {
        if( (dst != NULLPTR) && (src != NULLPTR) )
        {
            T const null( R::getTerminator() );
            while(true)
            {
                if(*dst == null)
                {
                    break;
                }
                ++dst;
                --cnt;
            }
            copyRaw3(dst, src, cnt);
        }
    }
    
    /**
     * @brief Compares two strings.
     *
     * @param string A string object interface to be compared with.
     * @return true if strings equal to each other.
     */
    static bool_t isEqualRaw2(T const* str1, T const* str2)
    {
        bool_t res( false );
        if( (str1 != NULLPTR) && (str2 != NULLPTR) )
        {        
            T const null( R::getTerminator() );
            while(true)
            {
                res = *str1 == *str2;
                if( (*str1 == null) || (res == false) )
                {
                    break;
                }
                ++str1; ///< SCA MISRA-C++:2008 Justified Rule 5-0-15
                ++str2; ///< SCA MISRA-C++:2008 Justified Rule 5-0-15
            }
        }
        return res;    
    }

private:

    /**
     * @brief Copies a passed string into this string.
     *
     * @param str A character string to be copied.
     * @return True if a passed string has been copied successfully.
     */
    virtual bool_t copyRaw(T const* str) = 0;

    /**
     * @brief Concatenates a passed string to this string.
     *
     * @param str A character string to be appended.
     * @return True if a passed string has been appended successfully.
     */
    virtual bool_t concatenateRaw(T const* str) = 0;

    /**
     * @brief Compares this string with a passed string lexicographically.
     *
     * @param string A string object interface to be compared with.
     * @return true if this string equals to a given string.
     */
    virtual bool_t isEqualToRaw(T const* str) const = 0;
    
    /**
     * @brief Converts an integer number to a string.
     *
     * The function converts an integer value into a character string using the base parameter,
     * which has to be 2, 8, 10, or 16 based numerals for converting to an appropriate numeral system.
     *
     * @note See exceptions below:
     *
     *       Exception 1: Minimum negative value can be `T_MIN + 1` or greater.
     *
     *       Exception 2: Only if the base is decimal, a passed number is available to be negative value,
     *       and the resulting string of these values is preceded with a minus sign. 
     *       
     *       Exception 3: A hexadecimal number includes lower case characters, and any resulting strings 
     *       do not contain any suffixes or prefixes for identifying a numeral system.
     *
     * @todo Rework the implementation to avoid the exceptions.
     *
     * @param val  A value that would be converted to a string.
     * @param base A numerical base used to represent a value as a string.
     * @return True if the conversion has been completed successfully.
     */
    template <typename I>
    bool_t convertToString(I const val, Number::Base const base)
    {
        const int32_t LENGTH( ( static_cast<int32_t>( sizeof(I) ) * 8) + 1 );
        bool_t res( true );
        bool_t isNegative( false );
        int32_t index( LENGTH - 1 );
        T temp[LENGTH];
        temp[index] = R::getTerminator();
		index -= 1;		
        do
        {
            // Test for available base
            switch(base)
            {
                case Number::BASE_2:
                case Number::BASE_8:
                case Number::BASE_16:
                {
                    isNegative = false;
                    break;
                }
                case Number::BASE_10:
                {
                    isNegative = ( !isPositive(val) ) ? true : false;
                    break;
                }
                default:
                {
                    res = false;
                    break;
                }
            }
            // If the base is not available
            if(res == false)
            {
                break;
            }
            // Prepare absolute value
            I module( isNegative ? (0 - val) : val );
            if( !isPositive(module) )
            {
                res = false;
                break;
            }
            // Do the conversion
            // @todo Revise possibility to declare index of size_t underlying type.
            //       But in the case index will always more than or equal zero.
            //       Thus, algorithm shall be re-worked.
            while(index >= 0)
            {
                I digit( module % static_cast<I>(base) );
                temp[index] = R::convertDigitToChar(digit);
                index -= 1;
                module = module / static_cast<I>(base);
                if(module == 0)
                {
                    break;
                }
            }
            // Add minus
            if( isNegative && (index >= 0) )
            {
                temp[index] = R::getMinusSign();
                index -= 1;
            }
            res = true;
        }
        while(false);
        if(res == true)
        {
			index += 1;
            res = copyRaw(&temp[index]);
        }
        return res;
    }
    
    /**
     * @brief Tests if a value is signed or unsigned.
     *
     * @param value A value that would be tested.
     * @return True if the value has been negative.
     *
     * @note The `volatile` keyword added, as the GCC 7.5.0 compiler doesn't call this function optimizing it for Release build.
     *       This behavior is cause a bug in `itoa` function with -9223372036854775808 value of int64_t type trying to check module of the value.
     *       Partial specialization of the template function for int64_t also doesn't help.
     */
    template <typename I>
    static bool_t isPositive(volatile I value)
    {
        return ( (value > 0) || (value == 0) ) ? true : false;
    }
        
    template <typename T0> friend bool_t operator==(api::String<T0> const&, T0 const* const);
    template <typename T0> friend bool_t operator==(T0 const* const, api::String<T0> const&);
    template <typename T0> friend bool_t operator!=(api::String<T0> const&, T0 const* const);
    template <typename T0> friend bool_t operator!=(T0 const* const, api::String<T0> const&);
};

/**
 * @brief Compares for equality of two strings.
 *
 * @param source1 A source object interface 1.
 * @param source2 A source object interface 2.
 * @return True if strings are equal.
 */
template <typename T>
inline bool_t operator==(api::String<T> const& source1, api::String<T> const& source2)
{
    return source1.isEqualTo(source2);
}

/**
 * @brief Compares for equality of two strings.
 *
 * @param source1 A source object interface 1.
 * @param source2 A source character string 2.
 * @return True if strings are equal.
 */
template <typename T>
inline bool_t operator==(api::String<T> const& source1, T const* const source2)
{
    return AbstractBaseString< T,CharTrait<T> >::isEqualRaw2(source1.getChar(), source2);
}

/**
 * @brief Compares for equality of two strings.
 *
 * @param source1 A source character string 1.
 * @param source2 A source object interface 2.
 * @return True if strings are equal.
 */
template <typename T>
inline bool_t operator==(T const* const source1, api::String<T> const& source2)
{
    return AbstractBaseString< T,CharTrait<T> >::isEqualRaw2(source1, source2.getChar());
}

/**
 * @brief Compares for inequality of two strings.
 *
 * @param source1 A source object interface 1.
 * @param source2 A source object interface 2.
 * @return True if strings are not equal.
 */
template <typename T>
inline bool_t operator!=(api::String<T> const& source1, api::String<T> const& source2)
{
    return !source1.isEqualTo(source2);
}

/**
 * @brief Compares for inequality of two strings.
 *
 * @param source1 A source object interface 1.
 * @param source2 A source character string 2.
 * @return True if strings are not  equal.
 */
template <typename T>
inline bool_t operator!=(api::String<T> const& source1, T const* const source2)
{
    return !AbstractBaseString< T,CharTrait<T> >::isEqualRaw2(source1.getChar(), source2);
}

/**
 * @brief Compares for inequality of two strings.
 *
 * @param source1 A source character string 1.
 * @param source2 A source object interface 2.
 * @return True if strings are not equal.
 */
template <typename T>
inline bool_t operator!=(T const* const source1, api::String<T> const& source2)
{
    return !AbstractBaseString< T,CharTrait<T> >::isEqualRaw2(source1, source2.getChar());
}

} // namespace lib
} // namespace eoos
#endif // LIB_ABSTRACTBASESTRING_HPP_

Generated by OpenCppCoverage (Version: 0.9.9.0)