#pragma once

#include <util/generic/ptr.h>
#include <util/generic/strbuf.h>
#include <util/generic/yexception.h>

namespace NSrvKernel {
    class TRE2Error : public yexception {};

    class TRegexp {
    public:
        class TOpts {
            friend class TRegexp;
        public:
            TOpts() noexcept;
            TOpts(TOpts&&) noexcept;
            TOpts& operator=(TOpts&&) noexcept;
            ~TOpts() noexcept;

        public:
            bool GetLiteral() const noexcept;
            TOpts& SetLiteral(bool value) noexcept;

            bool GetCaseInsensitive() const noexcept;
            TOpts& SetCaseInsensitive(bool value) noexcept;

            bool GetPosixSyntax() const noexcept;
            TOpts& SetPosixSyntax(bool value) noexcept;

        private:
            class TImpl;
            THolder<TImpl> Impl_;
        };

    public:
        TRegexp(TStringBuf pattern, const TOpts& options = Default<TOpts>());
        TRegexp(TRegexp&&) noexcept;
        TRegexp& operator=(TRegexp&&) noexcept;
        ~TRegexp() noexcept;

        bool Match(TStringBuf input) const noexcept;

        bool Extract(TStringBuf input, TVector<TStringBuf>* out, bool partial) const noexcept;

        bool ExtractAll(TStringBuf output, TVector<TStringBuf>* out, bool needZeroMatch) const;

        struct IRewriteSpec {
            IRewriteSpec() noexcept = default;

            virtual void Custom(TStringBuf key, TString& value) const noexcept;
        };

        size_t Rewrite(TStringBuf pattern, char escape, TStringBuf input,
                       TString* out, bool global, const IRewriteSpec& spec) const noexcept;
        size_t Rewrite(TStringBuf pattern, char escape, TStringBuf input,
                       TString* out, bool global) const noexcept
        {
            const TRegexp::IRewriteSpec spec{};
            return Rewrite(pattern, escape, input, out, global, spec);
        }

    private:
        class TImpl;
        THolder<TImpl> Impl_;
    };
}
