#pragma once

#include "chunks.h"

#include <util/generic/strbuf.h>

namespace NSrvKernel {
    namespace {
        struct TTreatsL {
            static char Get(const TChunk& c) noexcept {
                return c.AsStringBuf().front();
            }

            static TChunkPtr Extract(TChunkList& lst) noexcept {
                return TChunkPtr{lst.PopFront()};
            }

            static void Skip(TChunk& c) noexcept {
                c.Skip(1);
            }

            static void Insert(TChunkList& lst, TChunkPtr c) noexcept {
                lst.PushFront(std::move(c));
            }
        };

        struct TTreatsR {
            static char Get(const TChunk& c) noexcept {
                return c.AsStringBuf().back();
            }

            static TChunkPtr Extract(TChunkList& lst) noexcept {
                return TChunkPtr{lst.PopBack()};
            }

            static void Skip(TChunk& c) noexcept {
                c.Chop(1);
            }

            static void Insert(TChunkList& lst, TChunkPtr c) noexcept {
                lst.PushBack(std::move(c));
            }
        };

        template <class TTreats, class TMatch>
        bool DoStrip(TChunk& chunk, TMatch&& m) noexcept {
            const size_t len = chunk.Length();
            while (chunk.Length() && m(TTreats::Get(chunk))) {
                TTreats::Skip(chunk);
            }
            return chunk.Length() < len;
        }

        template <class TTreats, class TMatch>
        bool DoStrip(TChunkList& lst, TMatch&& m) noexcept {
            bool anyChanged = false;
            while (!lst.Empty()) {
                auto chunkPtr = TTreats::Extract(lst);
                const bool changed = DoStrip<TTreats>(*chunkPtr, m);

                if (chunkPtr->Length() > 0) {
                    TTreats::Insert(lst, std::move(chunkPtr));
                    return changed | anyChanged;
                }

                anyChanged = true;
            }
            return anyChanged;
        }

        template <class T, class TMatch>
        bool DoStripLR(T& ch, TMatch&& m) noexcept {
            bool lstr = DoStrip<TTreatsL>(ch, m);
            bool rstr = DoStrip<TTreatsR>(ch, m);

            return lstr || rstr;
        }
    }

    inline bool LStrip(TChunk& ch, char ws = ' ') noexcept {
        return DoStrip<TTreatsL>(ch, [=](char c) { return c == ws; });
    }

    inline bool RStrip(TChunk& ch, char ws = ' ') noexcept {
        return DoStrip<TTreatsR>(ch, [=](char c) { return c == ws; });
    }

    inline bool Strip(TChunk& ch, char ws = ' ') noexcept {
        return DoStripLR(ch, [=](char c) { return c == ws; });
    }

    inline bool LStrip(TChunkList& lst, char ws = ' ') noexcept {
        return DoStrip<TTreatsL>(lst, [=](char c) { return c == ws; });
    }

    inline bool RStrip(TChunkList& lst, char ws = ' ') noexcept {
        return DoStrip<TTreatsR>(lst, [=](char c) { return c == ws; });
    }

    inline bool Strip(TChunkList& lst, char ws = ' ') noexcept {
        return DoStripLR(lst, [=](char c) { return c == ws; });
    }

    inline bool LStrip(TChunk& ch, TStringBuf chSet) noexcept {
        return DoStrip<TTreatsL>(ch, [=](char c) { return chSet.find(c) != TStringBuf::npos; });
    }

    inline bool RStrip(TChunk& ch, TStringBuf chSet) noexcept {
        return DoStrip<TTreatsR>(ch, [=](char c) { return chSet.find(c) != TStringBuf::npos; });
    }

    inline bool Strip(TChunk& ch, TStringBuf chSet) noexcept {
        return DoStripLR(ch, [=](char c) { return chSet.find(c) != TStringBuf::npos; });
    }

    inline bool LStrip(TChunkList& lst, TStringBuf chSet) noexcept {
        return DoStrip<TTreatsL>(lst, [=](char c) { return chSet.find(c) != TStringBuf::npos; });
    }

    inline bool RStrip(TChunkList& lst, TStringBuf chSet) noexcept {
        return DoStrip<TTreatsR>(lst, [=](char c) { return chSet.find(c) != TStringBuf::npos; });
    }

    inline bool Strip(TChunkList& lst, TStringBuf chSet) noexcept {
        return DoStripLR(lst, [=](char c) { return chSet.find(c) != TStringBuf::npos; });
    }
}
