#include "request_head_ending_scanner.h"

#include <iterator>

namespace NSrvKernel {

    TMessageHeadEndingScanner::TMessageHeadEndingScanner(TString* buffer, bool scanHeaders) noexcept
        : Buffer_(buffer)
        , ScanHeaders_(scanHeaders)
    {}

    TErrorOr<bool> TMessageHeadEndingScanner::ProcessNewDataChunkAndSeparateBody(TChunkList lst, TChunkList& body) noexcept {
        while (!lst.Empty()) {
            TChunkPtr currentChunk = lst.PopFront();
            const char* chunkEnd = currentChunk->Data() + currentChunk->Length();
            for (const char* it = currentChunk->Data(); it != chunkEnd;) {
                char ch = *it;
                if (ExpectedNewLine_) {
                    Y_REQUIRE(ch == '\n', THttpParseError(HTTP_BAD_REQUEST));
                    ExpectedNewLine_ = false;
                    ++CrlfOrLfCount_;
                } else if (ch == '\r') {
                    ExpectedNewLine_ = true;
                } else if (ch == '\n') {
                    ++CrlfOrLfCount_;
                } else {
                    EmptyMessage_ = false;
                    CrlfOrLfCount_ = 0;
                    ExpectedNewLine_ = false;
                    it = static_cast<const char*>(memchr(it + 1, '\n', std::distance(it + 1, chunkEnd)));
                    if (!it) {
                        break;
                    }
                    if (*(it - 1) == '\r') {
                        --it;
                    }
                    continue;
                }

                if (Y_UNLIKELY(CrlfOrLfCount_ == 2 || EmptyMessage_ && CrlfOrLfCount_ == 1 && ScanHeaders_)) {
                    ++it;
                    if (it != chunkEnd) {
                        body.Push(currentChunk->SubChunk(it, chunkEnd));
                    }
                    currentChunk->Shrink(it - currentChunk->Data());
                    *Buffer_ += currentChunk->AsStringBuf();
                    body.Append(std::move(lst));
                    return true;
                }

                ++it;
            }
            *Buffer_ += currentChunk->AsStringBuf();
        }

        // We reached the chunk's end and haven't found a request head ending.
        return false;
    }

}
