//
// Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//

#ifndef BOOST_URL_GRAMMAR_STRING_VIEW_BASE_HPP
#define BOOST_URL_GRAMMAR_STRING_VIEW_BASE_HPP

#include <boost/url/detail/config.hpp>
#include <boost/url/string_view.hpp>
#include <cstddef>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>

namespace boost {
namespace urls {
namespace grammar {

/** Common functionality for string views

    This base class is used to provide common
    member functions for reference types that
    behave like string views. This cannot be
    instantiated directly; Instead, derive
    from the type and provide constructors
    which offer any desired preconditions
    and invariants.
*/
class string_view_base
{
protected:
    /** The referenced character buffer
    */
    string_view s_;

    /** Constructor
    */
    constexpr
    string_view_base(
        string_view s) noexcept
        : s_(s)
    {
    }

    /** Constructor
    */
    constexpr
    string_view_base(
        char const* data,
        std::size_t size) noexcept
        : s_(data, size)
    {
    }

    /** Swap
    */
    // VFALCO No idea why this fails in msvc
    /*BOOST_CXX14_CONSTEXPR*/ void swap(
        string_view_base& s ) noexcept
    {
        std::swap(s_, s.s_);
    }

    /** Constructor
    */
    string_view_base() = default;

    /** Constructor
    */
    string_view_base(
        string_view_base const&) = default;

    /** Assignment
    */
    string_view_base& operator=(
        string_view_base const&) = default;

public:
    /// The character traits
    typedef std::char_traits<char> traits_type;
    /// The value type
    typedef char value_type;
    /// The pointer type
    typedef char* pointer;
    /// The const pointer type
    typedef char const* const_pointer;
    /// The reference type
    typedef char& reference;
    /// The const reference type
    typedef char const& const_reference;
    /// The const iterator type
    typedef char const* const_iterator;
    /// The iterator type
    typedef const_iterator iterator;
    /// The const reverse iterator type
    typedef std::reverse_iterator<
        const_iterator> const_reverse_iterator;
    /// The reverse iterator type
    typedef const_reverse_iterator reverse_iterator;
    /// The size type
    typedef std::size_t size_type;
    /// The difference type
    typedef std::ptrdiff_t difference_type;

    /// A constant used to represent "no position"
    static constexpr std::size_t npos = string_view::npos;

    //--------------------------------------------

    /** Conversion
    */
    operator
    string_view() const noexcept
    {
        return s_;
    }

    /** Conversion
    */
#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
    operator
    std::string_view() const noexcept
    {
        return std::string_view(s_);
    }
#endif

    /** Conversion

        Conversion to std::string is explicit
        because assigning to string using an
        implicit constructor does not preserve
        capacity.
    */
    explicit
    operator
    std::string() const noexcept
    {
        return std::string(s_);
    }

    //--------------------------------------------

    // iterator support

    /** Return an iterator to the beginning

        See `string_view::begin`
    */
    BOOST_CONSTEXPR const_iterator begin() const noexcept
    {
        return s_.begin();
    }

    /** Return an iterator to the end

        See `string_view::end`
    */
    BOOST_CONSTEXPR const_iterator end() const noexcept
    {
        return s_.end();
    }

    /** Return an iterator to the beginning

        See `string_view::cbegin`
    */
    BOOST_CONSTEXPR const_iterator cbegin() const noexcept
    {
        return s_.cbegin();
    }

    /** Return an iterator to the end

        See `string_view::cend`
    */
    BOOST_CONSTEXPR const_iterator cend() const noexcept
    {
        return s_.cend();
    }

    /** Return a reverse iterator to the end

        See `string_view::rbegin`
    */
#ifdef __cpp_lib_array_constexpr
    constexpr
#endif
    const_reverse_iterator rbegin() const noexcept
    {
        return s_.rbegin();
    }

    /** Return a reverse iterator to the beginning

        See `string_view::rend`
    */
#ifdef __cpp_lib_array_constexpr
    constexpr
#endif
    const_reverse_iterator rend() const noexcept
    {
        return s_.rend();
    }

    /** Return a reverse iterator to the end

        See `string_view::crbegin`
    */
#ifdef __cpp_lib_array_constexpr
    constexpr
#endif
    const_reverse_iterator crbegin() const noexcept
    {
        return s_.crbegin();
    }

    /** Return a reverse iterator to the beginning

        See `string_view::crend`
    */
#ifdef __cpp_lib_array_constexpr
    constexpr
#endif
    const_reverse_iterator crend() const noexcept
    {
        return s_.crend();
    }

    // capacity

    /** Return the size

        See `string_view::size`
    */
    BOOST_CONSTEXPR size_type size() const noexcept
    {
        return s_.size();
    }

    /** Return the size

        See `string_view::length`
    */
    BOOST_CONSTEXPR size_type length() const noexcept
    {
        return s_.length();
    }

    /** Return the maximum allowed size

        See `string_view::max_size`
    */
    BOOST_CONSTEXPR size_type max_size() const noexcept
    {
        return s_.max_size();
    }

    /** Return true if the string is empty

        See `string_view::size`
    */
    BOOST_CONSTEXPR bool empty() const noexcept
    {
        return s_.empty();
    }
   
    // element access

    /** Access a character

        See `string_view::operator[]`
    */
    BOOST_CXX14_CONSTEXPR const_reference
        operator[]( size_type pos ) const noexcept
    {
        return s_[pos];
    }

    /** Access a character

        See `string_view::at`
    */
    BOOST_CXX14_CONSTEXPR const_reference
        at( size_type pos ) const
    {
        return s_.at(pos);
    }

    /** Return the first character

        See `string_view::front`
    */
    BOOST_CXX14_CONSTEXPR const_reference
        front() const noexcept
    {
        return s_.front();
    }

    /** Return the last character

        See `string_view::back`
    */
    BOOST_CXX14_CONSTEXPR const_reference
        back() const noexcept
    {
        return s_.back();
    }

    /** Return a pointer to the character buffer

        See `string_view::data`
    */
    BOOST_CONSTEXPR const_pointer
        data() const noexcept
    {
        return s_.data();
    }

    // string operations

    /** Copy the characters to another buffer

        See `string_view::copy`
    */
    BOOST_CXX14_CONSTEXPR size_type copy(
        char* s, size_type n, size_type pos = 0 ) const
    {
        return s_.copy(s, n, pos);
    }

    /** Return a view to part of the string

        See `string_view::substr`
    */
    BOOST_CXX14_CONSTEXPR string_view substr(
        size_type pos = 0, size_type n = string_view::npos ) const
    {
        return s_.substr(pos, n);
    }

    // comparison

    /** Return the result of comparing to another string

        See `string_view::compare`
    */
    BOOST_CXX14_CONSTEXPR int
        compare( string_view str ) const noexcept
    {
        return s_.compare(str);
    }

    /** Return the result of comparing to another string

        See `string_view::compare`
    */
    BOOST_CONSTEXPR int compare(
        size_type pos1, size_type n1, string_view str ) const
    {
        return s_.compare(pos1, n1, str);
    }

    /** Return the result of comparing to another string

        See `string_view::compare`
    */
    BOOST_CONSTEXPR int compare(
        size_type pos1, size_type n1, string_view str,
        size_type pos2, size_type n2 ) const
    {
        return s_.compare(pos1, n1, str, pos2, n2);
    }

    /** Return the result of comparing to another string

        See `string_view::compare`
    */
    BOOST_CONSTEXPR int compare(
        char const* s ) const noexcept
    {
        return s_.compare(s);
    }

    /** Return the result of comparing to another string

        See `string_view::compare`
    */
    BOOST_CONSTEXPR int compare(
        size_type pos1, size_type n1, char const* s ) const
    {
        return s_.compare(pos1, n1, s);
    }

    /** Return the result of comparing to another string

        See `string_view::compare`
    */
    BOOST_CONSTEXPR int compare(
        size_type pos1, size_type n1,
        char const* s, size_type n2 ) const
    {
        return s_.compare(pos1, n1, s, n2);
    }

    // starts_with

    /** Return true if a matching prefix exists

        See `string_view::starts_with`
    */
    BOOST_CONSTEXPR bool starts_with(
        string_view x ) const noexcept
    {
        return s_.starts_with(x);
    }

    /** Return true if a matching prefix exists

        See `string_view::starts_with`
    */
    BOOST_CONSTEXPR bool starts_with(
        char x ) const noexcept
    {
        return s_.starts_with(x);
    }

    /** Return true if a matching prefix exists

        See `string_view::starts_with`
    */
    BOOST_CONSTEXPR bool starts_with(
        char const* x ) const noexcept
    {
        return s_.starts_with(x);
    }

    // ends_with

    /** Return true if a matching suffix exists

        See `string_view::ends_with`
    */
    BOOST_CONSTEXPR bool ends_with(
        string_view x ) const noexcept
    {
        return s_.ends_with(x);
    }

    /** Return true if a matching suffix exists

        See `string_view::ends_with`
    */
    BOOST_CONSTEXPR bool ends_with(
        char x ) const noexcept
    {
        return s_.ends_with(x);
    }

    /** Return true if a matching suffix exists

        See `string_view::ends_with`
    */
    BOOST_CONSTEXPR bool ends_with(
        char const* x ) const noexcept
    {
        return s_.ends_with(x);
    }

    // find

    /** Return the position of matching characters

        See `string_view::find`
    */
    BOOST_CONSTEXPR size_type find(
        string_view str, size_type pos = 0 ) const noexcept
    {
        return s_.find(str, pos);
    }

    /** Return the position of matching characters

        See `string_view::find`
    */
    BOOST_CXX14_CONSTEXPR size_type find(
        char c, size_type pos = 0 ) const noexcept
    {
        return s_.find(c, pos);
    }

    /** Return the position of matching characters

        See `string_view::find`
    */
    BOOST_CXX14_CONSTEXPR size_type find(
        char const* s, size_type pos, size_type n ) const noexcept
    {
        return s_.find(s, pos, n);
    }

    /** Return the position of matching characters

        See `string_view::find`
    */
    BOOST_CONSTEXPR size_type find(
        char const* s, size_type pos = 0 ) const noexcept
    {
        return s_.find(s, pos);
    }

    // rfind

    /** Return the position of matching characters

        See `string_view::rfind`
    */
    BOOST_CONSTEXPR size_type rfind(
        string_view str, size_type pos = string_view::npos ) const noexcept
    {
        return s_.rfind(str, pos);
    }

    /** Return the position of matching characters

        See `string_view::rfind`
    */
    BOOST_CXX14_CONSTEXPR size_type rfind(
        char c, size_type pos = string_view::npos ) const noexcept
    {
        return s_.rfind(c, pos);
    }

    /** Return the position of matching characters

        See `string_view::rfind`
    */
    BOOST_CXX14_CONSTEXPR size_type rfind(
        char const* s, size_type pos, size_type n ) const noexcept
    {
        return s_.rfind(s, pos, n);
    }

    /** Return the position of matching characters

        See `string_view::rfind`
    */
    BOOST_CONSTEXPR size_type rfind(
        char const* s, size_type pos = string_view::npos ) const noexcept
    {
        return s_.rfind(s, pos);
    }

    // find_first_of

    /** Return the position of the first match

        See `string_view::find_first_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_first_of(
        string_view str, size_type pos = 0 ) const noexcept
    {
        return s_.find_first_of(str, pos);
    }

    /** Return the position of the first match

        See `string_view::find_first_of`
    */
    BOOST_CONSTEXPR size_type find_first_of(
        char c, size_type pos = 0 ) const noexcept
    {
        return s_.find_first_of(c, pos);
    }

    /** Return the position of the first match

        See `string_view::find_first_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_first_of(
        char const* s, size_type pos, size_type n ) const noexcept
    {
        return s_.find_first_of(s, pos, n);
    }

    /** Return the position of the first match

        See `string_view::find_first_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_first_of(
        char const* s, size_type pos = 0 ) const noexcept
    {
        return s_.find_first_of(s, pos);
    }

    // find_last_of

    /** Return the position of the last match

        See `string_view::find_last_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_last_of(
        string_view str, size_type pos = string_view::npos ) const noexcept
    {
        return s_.find_last_of(str, pos);
    }

    /** Return the position of the last match

        See `string_view::find_last_of`
    */
    BOOST_CONSTEXPR size_type find_last_of(
        char c, size_type pos = string_view::npos ) const noexcept
    {
        return s_.find_last_of(c, pos);
    }

    /** Return the position of the last match

        See `string_view::find_last_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_last_of(
        char const* s, size_type pos, size_type n ) const noexcept
    {
        return s_.find_last_of(s, pos, n);
    }

    /** Return the position of the last match

        See `string_view::find_last_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_last_of(
        char const* s, size_type pos = string_view::npos ) const noexcept
    {
        return s_.find_last_of(s, pos);
    }

    // find_first_not_of

    /** Return the position of the first non-match

        See `string_view::find_first_not_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_first_not_of(
        string_view str, size_type pos = 0 ) const noexcept
    {
        return s_.find_first_not_of(str, pos);
    }

    /** Return the position of the first non-match

        See `string_view::find_first_not_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_first_not_of(
        char c, size_type pos = 0 ) const noexcept
    {
        return s_.find_first_not_of(c, pos);
    }

    /** Return the position of the first non-match

        See `string_view::find_first_not_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_first_not_of(
        char const* s, size_type pos, size_type n ) const noexcept
    {
        return s_.find_first_not_of(s, pos, n);
    }

    /** Return the position of the first non-match

        See `string_view::find_first_not_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_first_not_of(
        char const* s, size_type pos = 0 ) const noexcept
    {
        return s_.find_first_not_of(s, pos);
    }

    // find_last_not_of

    /** Return the position of the last non-match

        See `string_view::find_last_not_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_last_not_of(
        string_view str, size_type pos = string_view::npos ) const noexcept
    {
        return s_.find_last_not_of(str, pos);
    }

    /** Return the position of the last non-match

        See `string_view::find_last_not_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_last_not_of(
        char c, size_type pos = string_view::npos ) const noexcept
    {
        return s_.find_last_not_of(c, pos);
    }

    /** Return the position of the last non-match

        See `string_view::find_last_not_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_last_not_of(
        char const* s, size_type pos, size_type n ) const noexcept
    {
        return s_.find_last_not_of(s, pos, n);
    }

    /** Return the position of the last non-match

        See `string_view::find_last_not_of`
    */
    BOOST_CXX14_CONSTEXPR size_type find_last_not_of(
        char const* s, size_type pos = string_view::npos ) const noexcept
    {
        return s_.find_last_not_of(s, pos);
    }

    // contains

    /** Return true if matching characters are found

        See `string_view::contains`
    */
    BOOST_CONSTEXPR bool contains( string_view sv ) const noexcept
    {
        return s_.contains(sv);
    }

    /** Return true if matching characters are found

        See `string_view::contains`
    */
    BOOST_CXX14_CONSTEXPR bool contains( char c ) const noexcept
    {
        return s_.contains(c);
    }

    /** Return true if matching characters are found

        See `string_view::contains`
    */
    BOOST_CONSTEXPR bool contains( char const* s ) const noexcept
    {
        return s_.contains(s);
    }

    // relational operators
#ifndef BOOST_URL_DOCS
private:
    template<class S0, class S1>
    using is_match = std::integral_constant<bool,
        std::is_convertible<S0, string_view>::value &&
        std::is_convertible<S1, string_view>::value && (
            (std::is_base_of<string_view_base,
                typename std::decay<S0>::type>::value &&
            std::is_convertible<S0 const volatile*,
                string_view_base const volatile*>::value) ||
            (std::is_base_of<string_view_base,
                typename std::decay<S1>::type>::value &&
            std::is_convertible<S1 const volatile*,
                string_view_base const volatile*>::value))>;
public:

    template<class S0, class S1>
    BOOST_CXX14_CONSTEXPR friend auto operator==(
        S0 const& s0, S1 const& s1) noexcept ->
        typename std::enable_if<
            is_match<S0, S1>::value, bool>::type
    {
        return urls::detail::to_sv(s0) == urls::detail::to_sv(s1);
    }

    template<class S0, class S1>
    BOOST_CXX14_CONSTEXPR friend auto operator!=(
        S0 const& s0, S1 const& s1) noexcept ->
        typename std::enable_if<
            is_match<S0, S1>::value, bool>::type
    {
        return urls::detail::to_sv(s0) != urls::detail::to_sv(s1);
    }

    template<class S0, class S1>
    BOOST_CXX14_CONSTEXPR friend auto operator<(
        S0 const& s0, S1 const& s1) noexcept ->
        typename std::enable_if<
            is_match<S0, S1>::value, bool>::type
    {
        return urls::detail::to_sv(s0) < urls::detail::to_sv(s1);
    }

    template<class S0, class S1>
    BOOST_CXX14_CONSTEXPR friend auto operator<=(
        S0 const& s0, S1 const& s1) noexcept ->
        typename std::enable_if<
            is_match<S0, S1>::value, bool>::type
    {
        return urls::detail::to_sv(s0) <= urls::detail::to_sv(s1);
    }

    template<class S0, class S1>
    BOOST_CXX14_CONSTEXPR friend auto operator>(
        S0 const& s0, S1 const& s1) noexcept ->
        typename std::enable_if<
            is_match<S0, S1>::value, bool>::type
    {
        return urls::detail::to_sv(s0) > urls::detail::to_sv(s1);
    }

    template<class S0, class S1>
    BOOST_CXX14_CONSTEXPR friend auto operator>=(
        S0 const& s0, S1 const& s1) noexcept ->
        typename std::enable_if<
            is_match<S0, S1>::value, bool>::type
    {
        return urls::detail::to_sv(s0) >= urls::detail::to_sv(s1);
    }
#endif

    //--------------------------------------------

    /** Return the hash of this value
    */
    friend
    std::size_t
    hash_value(
        string_view_base const& s) noexcept
    {
        return hash_value(s.s_);
    }

    BOOST_URL_DECL
    friend
    std::ostream&
    operator<<(
        std::ostream& os,
        string_view_base const& s);
};

//------------------------------------------------

/** Format a string to an output stream
*/
BOOST_URL_DECL
std::ostream&
operator<<(
    std::ostream& os,
    string_view_base const& s);

} // grammar

#ifndef BOOST_URL_DOCS
namespace detail {
template <>
inline
string_view
to_sv(grammar::string_view_base const& s) noexcept
{
    return s.operator string_view();
}
} // detail
#endif

} // urls
} // boost

#endif
