#include "response_stream.h"

namespace NHttp::NRequesters {
TBalancerClient::TResponseStream::TResponseStream(TWorker& worker): Worker_{worker}, Event_{&Worker_.Executor()} {}

void TBalancerClient::TResponseStream::Read(size_t sizeLimit, bool peek, TInstant deadline, TReadHandler callback) noexcept {
    Worker_.Execute([&]() {
        Y_VERIFY(!ReadCoro_ || !ReadCoro_->IsRunning(), "parallel reads are prohibited");

        ReadCoro_ = Worker_.Create([=, this, callback = std::move(callback)]() mutable {
            auto result = Wait(sizeLimit, peek, deadline);
            CallbackCoro_ = Worker_.Create([callback = std::move(callback), result = std::move(result)]() mutable {
                callback(std::move(result));
            }, "TBalancerClient::TResponseStream::Callback");
        }, "TBalancerClient::TResponseStream::Wait");
    });
}

IResponseStream::TReadResult TBalancerClient::TResponseStream::Wait(size_t sizeLimit, bool peek, TInstant deadline) {
    int checked = 0;
    do {
        if (!Results_.empty()) {
            if (auto data = get_if<TDataFrame>(&Results_.front()); !data || sizeLimit == 0 ||
                                                                   sizeLimit > data->size()) {
                if (peek) {
                    return TReadResult{Results_.front()};
                } else {
                    auto frame = std::move(Results_.front());
                    Results_.pop();
                    return TReadResult{std::move(frame)};
                }
            } else {
                if (peek) {
                    return TReadResult{data->SubList(sizeLimit)};
                } else {
                    auto dataFrame = data->Cut(sizeLimit);
                    return TReadResult{std::move(dataFrame)};
                }
            }
        }

        if (Finished_) {
            return TReadResult{Nothing()};
        }

        if (Error_) {
            return TReadResult{*Error_};
        }

        int result = Event_.WaitD(deadline);
        if (result == ECANCELED) {
            return TError{"TBalancerClient::TResponseStream::Wait cancelled"};
        }
        if (result == ETIMEDOUT) {
            return TError{"TBalancerClient::TResponseStream::Wait read timeout", TTimeoutError{}};
        }
        Y_VERIFY(result == EWAKEDUP);
    } while(!checked++);

    Y_FAIL("something went wrong");
}

void TBalancerClient::TResponseStream::PushData(TResponseFrame frame) noexcept {
    Results_.emplace(std::move(frame));
    Event_.Signal();
}

void TBalancerClient::TResponseStream::Finish() {
    Finished_ = true;
    Event_.BroadCast();
}

void TBalancerClient::TResponseStream::Error(TError error) {
    Error_ = std::move(error);
    Event_.BroadCast();
}
}
