#pragma once

#include <nghttp2/nghttp2.h>
#include <balancer/kernel/custom_io/iterator.h>
#include <library/cpp/coroutine/engine/events.h>
#include <balancer/kernel/http/parser/http.h>
#include <balancer/kernel/coro/coroutine.h>

namespace NBalancerClient {
class THttp2Connection;

class THttp2Stream: protected TIntrusiveListItem<THttp2Stream> {
    friend class THttp2Connection;
    friend class TIntrusiveListItem<THttp2Stream>;
  public:
    struct TCallbacks {
        typedef void (OnChunkSentHandler)(size_t size);
        typedef void (OnChunkReceivedHandler)(size_t size);
        typedef void (OnEndOfInputStreamHandler)();

        typedef void (OnFrameSent)(const nghttp2_frame* frame);
        typedef void (OnFrameRecv)(const nghttp2_frame* frame);

        std::function<OnChunkSentHandler> OnChunkSent;
        std::function<OnChunkReceivedHandler> OnChunkReceived;
        std::function<OnEndOfInputStreamHandler> OnEndOfInputStream;

        std::function<OnFrameSent> OnFrameSentHandler;
        std::function<OnFrameRecv> OnFrameRecvHandler;
    };

    ~THttp2Stream();

    i32 GetStreamId() const;

    NSrvKernel::TError Join(TInstant deadline);

  private:
    THttp2Stream(THttp2Connection& connection,
                 i32 streamId,
                 TCont* cont,
                 NSrvKernel::IIoInput& input,
                 NSrvKernel::IHttpOutput& output,
                 TCallbacks callbacks);
    void Read();

    ssize_t CheckNewData(ui8* buf, size_t length, ui32* data_flags);
    NSrvKernel::TErrorOr<NSrvKernel::TChunkList> GetNewData(size_t length);
    int OnFrameRecv(const nghttp2_frame *frame);
    int OnFrameSent(const nghttp2_frame *frame);
    int OnData(ui8 flags, const ui8 *data, size_t len);
    void OnStreamClose(NSrvKernel::TError error);
    int OnHeader(const nghttp2_frame *frame, nghttp2_rcbuf *nameRcBuf, nghttp2_rcbuf *valueRcBuf, ui8 flags);

    THttp2Connection& Connection_;
    i32 StreamId_;
    NSrvKernel::TStreamIterator ClientInput_;
    NSrvKernel::IHttpOutput& ClientOutput_;
    TContSimpleEvent ReadEvent_;
    TContSimpleEvent DoneEvent_;
    TMaybe<NSrvKernel::TError> Result_;
    NSrvKernel::TResponse Response_;
    bool HeadersReceived_ = false;
    NSrvKernel::THeaders Trailers_;
    bool HaveNewChunk_ = false;
    size_t ChunkSize_ = 0;
    bool Deferred_ = false;
    bool StreamClosed_ = false;
    NSrvKernel::TCoroutine ReadCoroutine_;
    TCallbacks Callbacks_;
};
} // namespace NBalancerClient
