/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef INCLUDE_ECXX_CTL_STRING_TPP
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_CTL_STRING_TPP


#ifndef ECXX_CTL_STRING_TPP
#define ECXX_CTL_STRING_TPP


//
// A helper struct for string manipulations
//
template <size_t OtherMaxCapacity>
struct StringHelper {
    typedef String<OtherMaxCapacity>      StringType;
    typedef typename StringType::SizeType SizeType;

    static inline void setStringLength(StringType& string, SizeType length) noexcept
    { string._length = length; }
};


//
// A simple string
//
template <size_t MaxCapacity>
inline String<MaxCapacity>::String() noexcept
: _length(0)
{}


template <size_t MaxCapacity>
inline String<MaxCapacity>::String(const String& string) noexcept
: _length(0)
{ this->operator=(string); }


template <size_t MaxCapacity>
inline String<MaxCapacity>::String(String&& string) noexcept
: _length(0)
{ this->operator=( eCxx::move(string) ); }


template <size_t MaxCapacity> template <size_t OtherMaxCapacity>
inline String<MaxCapacity>::String(const String<OtherMaxCapacity>& string) noexcept
: _length(0)
{ this->operator=(string); }


template <size_t MaxCapacity> template <size_t OtherMaxCapacity>
inline String<MaxCapacity>::String(String<OtherMaxCapacity>&& string) noexcept
: _length(0)
{ this->operator=( eCxx::move(string) ); }


template <size_t MaxCapacity> template <typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>::String(PGMCharPointer ptr) noexcept
: _length(0)
{ concatWith<PGMStrNCpyFunctorT>(ptr); }


template <size_t MaxCapacity> template <typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>::String(PGMCharPointerFar ptr) noexcept
: _length(0)
{ concatWith<PGMStrNCpyFunctorT>(ptr); }


template <size_t MaxCapacity> template <typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>::String(PGMCharPointer ptr, SizeType lenMax) noexcept
: _length(0)
{ concatWith<PGMStrNCpyFunctorT>(ptr, lenMax); }


template <size_t MaxCapacity> template <typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>::String(PGMCharPointerFar ptr, SizeType lenMax) noexcept
: _length(0)
{ concatWith<PGMStrNCpyFunctorT>(ptr, lenMax); }


template <size_t MaxCapacity>
inline String<MaxCapacity>::~String() noexcept
{ clear(); }


template <size_t MaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator=(const String& string) noexcept
{ return this->operator=<MaxCapacity>(string); }


template <size_t MaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator=(String&& string) noexcept
{ return this->operator=<MaxCapacity>( eCxx::move(string) ); }


template <size_t MaxCapacity> template <size_t OtherMaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator=(const String<OtherMaxCapacity>& string) noexcept
{
    _length = (string.length() > Capacity) ? Capacity : string.length();
    memcpy(_buffer(0), string.data(), _length);

    return *this;
}


template <size_t MaxCapacity> template <size_t OtherMaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator=(String<OtherMaxCapacity>&& string) noexcept
{
    // Copy the string
    _length = (string.length() > Capacity) ? Capacity : string.length();
    memcpy(_buffer(0), string.data(), _length);

    // Done if all characters from the source string are copied
    if(string.length() <= _length) {
        StringHelper<OtherMaxCapacity>::setStringLength(string, 0);
        return *this;
    }

    // Otherwise, shift the characters within the source string
    Iterator      dst = string.begin();
    ConstIterator src = dst + _length;
    SizeType      cnt = string.length() - _length;

    StringHelper<OtherMaxCapacity>::setStringLength(string, cnt);
    while(cnt--) *dst++ = *src++;

    // Done
    return *this;
}


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::SizeType String<MaxCapacity>::capacity() const noexcept
{ return Capacity; }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::SizeType String<MaxCapacity>::size() const noexcept
{ return _length; }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::SizeType String<MaxCapacity>::length() const noexcept
{ return _length; }


template <size_t MaxCapacity>
inline bool String<MaxCapacity>::empty() const noexcept
{ return !_length; }


template <size_t MaxCapacity>
inline void String<MaxCapacity>::clear() noexcept
{ _length = 0; }


template <size_t MaxCapacity>
inline void String<MaxCapacity>::resize(SizeType length, ValueType fill) noexcept
{
    // If the new length is zero, clear the string
    if(!length) {
        clear();
        return;
    }

    // If the new length is the same, do nothing
    if(length == _length) return;

    // Decrease the length
    if(length < _length) {
        _length = length;
        return;
    }

    // Increase the length
    if(length > Capacity) length = Capacity;

    for(SizeType i = _length; i < length; ++i) *_buffer(i) = fill;

    _length = length;
}


template <size_t MaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator--() noexcept
{
    if(!_length) return *this;

    --_length;
    return *this;
}


template <size_t MaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator+=(ValueType chr) noexcept
{
    if(_length == Capacity) return *this;

    *_buffer(_length++) = chr;
    return *this;
}


template <size_t MaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator+=(const String& string) noexcept
{ return this->operator+=<MaxCapacity>(string); }


template <size_t MaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator+=(String&& string) noexcept
{ return this->operator+=<MaxCapacity>( eCxx::move(string) ); }


template <size_t MaxCapacity> template <size_t OtherMaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator+=(const String<OtherMaxCapacity>& string) noexcept
{
    if(_length == Capacity) return *this;

    SizeType len = Capacity - _length;
    if(string.length() < len) len = string.length();

    memcpy(_buffer(_length), string.data(), len);
    _length += len;

    return *this;
}


template <size_t MaxCapacity> template <size_t OtherMaxCapacity>
inline String<MaxCapacity>& String<MaxCapacity>::operator+=(String<OtherMaxCapacity>&& string) noexcept
{
    // Copy the string
    if(_length == Capacity) return *this;

    SizeType len = Capacity - _length;
    if(string.length() < len) len = string.length();

    memcpy(_buffer(_length), string.data(), len);
    _length += len;

    // Done if all characters from the source string are copied
    if(string.length() <= len) {
        StringHelper<OtherMaxCapacity>::setStringLength(string, 0);
        return *this;
    }

    // Otherwise, shift the characters within the source string
    Iterator      dst = string.begin();
    ConstIterator src = dst + len;
    SizeType      cnt = string.length() - len;

    StringHelper<OtherMaxCapacity>::setStringLength(string, cnt);
    while(cnt--) *dst++ = *src++;

    // Done
    return *this;
}


template <size_t MaxCapacity> template <typename PGMPointerT, typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>& String<MaxCapacity>::_concatWith(PGMPointerT ptr, SizeType lenMax) noexcept
{
    // Copy the string
    if(_length == Capacity) return *this;

    SizeType len = Capacity - _length;
    if(lenMax < len) len = lenMax;

    PGMStrNCpyFunctorT functor;

    functor(_buffer(_length), ptr.p, len);
    if(len < Capacity - 1) _buffer(_length)[len] = 0;

    // Determine the new length
    _length = 0;

    for(len = 0; len < Capacity; ++len) {
        if(*_buffer(len) == 0) {
            _length = len;
            break;
        }
    }

    if(!_length) _length = Capacity;

    return *this;
}


template <size_t MaxCapacity> template <typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>& String<MaxCapacity>::concatWith(PGMCharPointer ptr, SizeType lenMax) noexcept
{ return _concatWith<PGMCharPointer, PGMStrNCpyFunctorT>(ptr, lenMax); }


template <size_t MaxCapacity> template <typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>& String<MaxCapacity>::concatWith(PGMCharPointerFar ptr, SizeType lenMax) noexcept
{ return _concatWith<PGMCharPointerFar, PGMStrNCpyFunctorT>(ptr, lenMax); }


template <size_t MaxCapacity> template <typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>& String<MaxCapacity>::concatWith(PGMCharPointer ptr) noexcept
{ return concatWith<PGMStrNCpyFunctorT>(ptr, Capacity - _length); }


template <size_t MaxCapacity> template <typename PGMStrNCpyFunctorT>
inline String<MaxCapacity>& String<MaxCapacity>::concatWith(PGMCharPointerFar ptr) noexcept
{ return concatWith<PGMStrNCpyFunctorT>(ptr, Capacity - _length); }


template <size_t MaxCapacity>
inline bool String<MaxCapacity>::operator==(const String& string) const noexcept
{ return operator==<MaxCapacity>(string); }


template <size_t MaxCapacity> template <size_t OtherMaxCapacity>
inline bool String<MaxCapacity>::operator==(const String<OtherMaxCapacity>& string) const noexcept
{
    if( _length != string.length() ) return false;

    ConstPointer a = _buffer(0);
    ConstPointer b = string.data();
    SizeType     n = _length;

    while(n--) if(*a++ != *b++) return false;

    return true;
}


template <size_t MaxCapacity>
inline bool String<MaxCapacity>::operator!=(const String& string) const noexcept
{ return !operator==(string); }


template <size_t MaxCapacity> template <size_t OtherMaxCapacity>
inline bool String<MaxCapacity>::operator!=(const String<OtherMaxCapacity>& string) const noexcept
{ return !operator==<OtherMaxCapacity>(string); }


template <size_t MaxCapacity> template <typename PGMPointerT, typename PGMStrNCmpFunctorT, typename PGMStrNLenFunctorT>
inline bool String<MaxCapacity>::_equalsTo(PGMPointerT ptr, SizeType lenMax) const noexcept
{
    // Check the length first
    if( !isSame<PGMStrNLenFunctorT, void>::Value ) {
        PGMStrNLenFunctorT functor;

        SizeType la = _length;
        SizeType lb = functor(ptr.p, lenMax);

        if(lenMax < la) la = lenMax;
        if(lenMax < lb) lb = lenMax;

        if(la != lb) return false;
    }

    // Compare the string
    PGMStrNCmpFunctorT functor;

    if( functor( _buffer(0), ptr.p, (lenMax < _length) ? lenMax : _length ) ) return false;

    return true;
}


template <size_t MaxCapacity> template <typename PGMStrNCmpFunctorT, typename PGMStrNLenFunctorT>
inline bool String<MaxCapacity>::equalsTo(PGMCharPointer ptr, SizeType lenMax) const noexcept
{ return _equalsTo<PGMCharPointer, PGMStrNCmpFunctorT, PGMStrNLenFunctorT>(ptr, lenMax); }


template <size_t MaxCapacity> template <typename PGMStrNCmpFunctorT, typename PGMStrNLenFunctorT>
inline bool String<MaxCapacity>::equalsTo(PGMCharPointerFar ptr, SizeType lenMax) const noexcept
{ return _equalsTo<PGMCharPointerFar, PGMStrNCmpFunctorT, PGMStrNLenFunctorT>(ptr, lenMax); }


template <size_t MaxCapacity> template <typename PGMStrNCmpFunctorT, typename PGMStrNLenFunctorT>
inline bool String<MaxCapacity>::equalsTo(PGMCharPointer ptr) const noexcept
{ return equalsTo<PGMStrNCmpFunctorT, PGMStrNLenFunctorT>(ptr, Capacity - _length); }


template <size_t MaxCapacity> template <typename PGMStrNCmpFunctorT, typename PGMStrNLenFunctorT>
inline bool String<MaxCapacity>::equalsTo(PGMCharPointerFar ptr) const noexcept
{ return equalsTo<PGMStrNCmpFunctorT, PGMStrNLenFunctorT>(ptr, Capacity - _length); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::ConstPointer String<MaxCapacity>::cStr() noexcept
{
    *_buffer(_length) = 0; // Add null terminator

    return _buffer(0);
}


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::ConstPointer String<MaxCapacity>::data() const noexcept
{ return _buffer(0); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::Reference String<MaxCapacity>::front() noexcept
{ return *_buffer(0); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::ConstReference String<MaxCapacity>::front() const noexcept
{ return *_buffer(0); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::Reference String<MaxCapacity>::back() noexcept
{ return *_buffer(_length - 1); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::ConstReference String<MaxCapacity>::back() const noexcept
{ return *_buffer(_length - 1); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::Reference String<MaxCapacity>::operator[](SizeType index) noexcept
{ return *_buffer(index); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::ConstReference String<MaxCapacity>::operator[](SizeType index) const noexcept
{ return *_buffer(index); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::Iterator String<MaxCapacity>::begin() noexcept
{ return _buffer(0); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::Iterator String<MaxCapacity>::end() noexcept
{ return _buffer(_length); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::ConstIterator String<MaxCapacity>::cbegin() const noexcept
{ return _buffer(0); }


template <size_t MaxCapacity>
inline typename String<MaxCapacity>::ConstIterator String<MaxCapacity>::cend() const noexcept
{ return _buffer(_length); }


#endif
