#pragma once

#include <util/generic/algorithm.h>
#include <util/generic/strbuf.h>
#include <util/generic/yexception.h>
#include <util/string/cast.h>
#include <util/system/yassert.h>

#include <iterator>

class TDelimStringIter {
public:
    using value_type = TStringBuf;
    using difference_type = ptrdiff_t;
    using pointer = const TStringBuf*;
    using reference = const TStringBuf&;
    using iterator_category = std::forward_iterator_tag;

    inline TDelimStringIter(const char* begin, const char* strEnd, TStringBuf delim)
        : TDelimStringIter(TStringBuf(begin, strEnd), delim)
    {
    }

    inline TDelimStringIter(TStringBuf str, TStringBuf delim)
        : IsValid(true)
        , Str(str)
        , Delim(delim)
    {
        UpdateCurrent();
    }

    inline TDelimStringIter()
        : IsValid(false)
    {
    }

    inline explicit operator bool() const {
        return IsValid;
    }

    // NOTE: this is a potentially unsafe operation (no overrun check)
    inline TDelimStringIter& operator++() {
        if (Current.end() != Str.end()) {
            Str.Skip(Current.length() + Delim.length());
            UpdateCurrent();
        } else {
            Str.Clear();
            Current.Clear();
            IsValid = false;
        }
        return *this;
    }

    inline void operator+=(size_t n) {
        for (; n > 0; --n) {
            ++(*this);
        }
    }

    inline bool operator==(const TDelimStringIter& rhs) const {
        return (IsValid == rhs.IsValid) && (!IsValid || (Current.begin() == rhs.Current.begin()));
    }

    inline bool operator!=(const TDelimStringIter& rhs) const {
        return !(*this == rhs);
    }

    inline TStringBuf operator*() const {
        return Current;
    }

    inline const TStringBuf* operator->() const {
        return &Current;
    }

    // Get & advance
    template <class T>
    inline bool TryNext(T& t) {
        if (IsValid) {
            t = FromString<T>(Current);
            operator++();
            return true;
        } else {
            return false;
        }
    }

    template <class T>
    inline TDelimStringIter& Next(T& t) // Get & advance
    {
        if (!TryNext(t))
            ythrow yexception() << "No valid field";
        return *this;
    }

    template <class T>
    inline T GetNext() {
        T res;
        Next(res);
        return res;
    }

    inline const char* GetBegin() const {
        return Current.begin();
    }

    inline const char* GetEnd() const {
        return Current.end();
    }

    inline bool Valid() const {
        return IsValid;
    }

    // contents from next token to the end of string
    inline TStringBuf Cdr() const {
        return Str.SubStr(Current.length() + Delim.length());
    }

    inline TDelimStringIter IterEnd() const {
        return TDelimStringIter();
    }

private:
    inline void UpdateCurrent() {
        // it is much faster than TStringBuf::find
        size_t pos = std::search(Str.begin(), Str.end(), Delim.begin(), Delim.end()) - Str.begin();
        Current = Str.Head(pos);
    }

private:
    bool IsValid;

    TStringBuf Str;
    TStringBuf Current;
    TStringBuf Delim;
};

//example: for (TStringBuf field: TDelimStroka(line, "@@")) { ... }
struct TDelimStroka {
    TStringBuf S;
    TStringBuf Delim;

    inline TDelimStroka(TStringBuf s, TStringBuf delim)
        : S(s)
        , Delim(delim)
    {
    }

    inline TDelimStringIter begin() const {
        return TDelimStringIter(S, Delim);
    }

    inline TDelimStringIter end() const {
        return TDelimStringIter();
    }
};

inline TDelimStringIter begin_delim(const TString& str, TStringBuf delim) {
    return TDelimStringIter(str, delim);
}

inline TDelimStringIter begin_delim(TStringBuf str, TStringBuf delim) {
    return TDelimStringIter(str.begin(), str.end(), delim);
}

inline TDelimStringIter end_delim(const TString& /*str*/, TStringBuf /*delim*/) {
    return TDelimStringIter();
}

class TKeyValueDelimStringIter {
public:
    TKeyValueDelimStringIter(const TStringBuf str, const TStringBuf delim);
    bool Valid() const;
    TKeyValueDelimStringIter& operator++();
    const TStringBuf& Key() const;
    const TStringBuf& Value() const;

private:
    TDelimStringIter DelimIter;
    TStringBuf ChunkKey, ChunkValue;

private:
    void ReadKeyAndValue();
};