#pragma once

#include <fmt/format.h>

namespace cruft::format {
    namespace detail {
        /// A utility string container that is rendered enclosed in quotes when formatted.
        template <typename string_type = std::string_view>
        struct escaped_string {
            /// The string that needs to be escaped
            string_type source;
            /// The character marking the beginning and end of the string that needs to be escaped
            typename string_type::value_type delim;
            /// The character that escapes other characters
            typename string_type::value_type escape;

            constexpr bool
            escaped_character (char c) const
            {
                return delim == c or escape == c;
            }


            /// Copies the string characters to an output iterator, inserting
            /// delim and escape characters as required.
            template <typename OutputT>
            constexpr
            OutputT
            copy (OutputT dst) const
            {
                *dst++ = delim;

                for (const auto& c : source) {
                    if (escaped_character (c))
                        *dst++ = escape;

                    *dst++ = c;
                }

                *dst++ = delim;
                return dst;
            }
        };
    }

    /// Returns an object that will be enclosed between delimiters. All
    /// occurrences of this delimiter will be escaped with the supplied
    /// character (as will occurences of this escape character).
    constexpr
    auto
    quoted (
        std::string_view source,
        char delim = '"',
        char escape = '\\'
    ) {
        return cruft::format::detail::escaped_string<
            std::string_view
        > {
            .source = source,
            .delim = delim,
            .escape = escape,
        };
    }
}


///////////////////////////////////////////////////////////////////////////////
template <typename string_type>
struct fmt::formatter<cruft::format::detail::escaped_string<string_type>> {
    constexpr
    format_parse_context::iterator
    parse (fmt::format_parse_context ctx) const
    {
        return ctx.begin();
    }

    constexpr
    format_context::iterator
    format(
        cruft::format::detail::escaped_string<string_type> const& val,
        format_context & ctx
    ) const {
        return val.copy (ctx.out());
    }
};