#pragma once

#include "http2_flat_heap.h"

#include <util/generic/intrlist.h>

namespace NSrvKernel::NHTTP2 {

    // TODO (velavokr): devirtualize as a final optimization

    template <class TItem>
    class TQueueItem {
    public:
        struct TItemHolderBase {
            TQueueItem& Item;
            explicit TItemHolderBase(TQueueItem& item) noexcept
                : Item(item)
            {}
        };

        struct TListItem : TIntrusiveListItem<TListItem>, TItemHolderBase {
            using TItemHolderBase::TItemHolderBase;
        };

        struct THeapItem : TIntrusiveFlatHeapItem<THeapItem>, TItemHolderBase {
            using TItemHolderBase::TItemHolderBase;
        };

    public:
        explicit TQueueItem(TItem& item) noexcept
            : ListItem_(*this)
            , HeapItem_(*this)
            , Item_(item)
        {}

        void OnUnlink() {
            Item_.OnUnlink();
        }

        [[nodiscard]] TItem& Get() noexcept {
            return Item_;
        }

        [[nodiscard]] const TItem& Get() const noexcept {
            return Item_;
        }

        [[nodiscard]] auto& GetListItem() noexcept {
            return ListItem_;
        }

        [[nodiscard]] const auto& GetListItem() const noexcept {
            return ListItem_;
        }

        [[nodiscard]] bool IsListLinked() const noexcept {
            return !ListItem_.Empty();
        }

        void UnlinkList() noexcept {
            ListItem_.Unlink();
        }

        [[nodiscard]] auto& GetHeapItem() noexcept {
            return HeapItem_;
        }

        [[nodiscard]] const auto& GetHeapItem() const noexcept {
            return HeapItem_;
        }

        [[nodiscard]] bool IsHeapLinked() const noexcept {
            return HeapItem_.IsLinked();
        }

        void UnlinkHeap() noexcept {
            HeapItem_.Unlink();
        }

        [[nodiscard]] bool IsLinked() const noexcept {
            return IsListLinked() || IsHeapLinked();
        }

        void Unlink() noexcept {
            UnlinkList();
            UnlinkHeap();
        }

    private:
        TListItem ListItem_;
        THeapItem HeapItem_;
        TItem& Item_;
    };


    template <class TItem>
    class IQueue {
    public:
        virtual ~IQueue() = default;

        void MoveFrom(IQueue<TItem>& other) noexcept {
            while (TQueueItem<TItem>* itemPtr = other.GetNext()) {
                itemPtr->Unlink();
                Insert(*itemPtr);
            }
        }

        [[nodiscard]] TQueueItem<TItem>* GetNext() noexcept {
            return DoGetNext();
        }

        [[nodiscard]] const TQueueItem<TItem>* GetNext() const noexcept {
            return DoGetNext();
        }

        void Update(TQueueItem<TItem>& item) noexcept {
            DoUpdate(item);
        }

        void Insert(TQueueItem<TItem>& item) noexcept {
            DoInsert(item);
        }

        void Remove(TQueueItem<TItem>& item) noexcept {
            DoRemove(item);
        }

        void Rebuild() noexcept {
            DoRebuild();
        }

        [[nodiscard]] bool Empty() const noexcept {
            return DoEmpty();
        }

        [[nodiscard]] size_t Size() const noexcept {
            return DoSize();
        }

        explicit operator bool() const noexcept {
            return !Empty();
        }

    private:
        [[nodiscard]] virtual TQueueItem<TItem>* DoGetNext() noexcept = 0;

        [[nodiscard]] virtual const TQueueItem<TItem>* DoGetNext() const noexcept {
            return const_cast<IQueue<TItem>*>(this)->GetNext();
        }

        virtual void DoUpdate(TQueueItem<TItem>&) noexcept = 0;

        virtual void DoInsert(TQueueItem<TItem>&) noexcept = 0;

        virtual void DoRemove(TQueueItem<TItem>&) noexcept = 0;

        virtual void DoRebuild() noexcept {}

        virtual bool DoEmpty() const noexcept = 0;

        virtual size_t DoSize() const noexcept = 0;
    };


    template <class TItem>
    class TFIFOQueueBase : public IQueue<TItem> {
    protected:
        TQueueItem<TItem>* DoGetNext() noexcept override {
            return List_.Empty() ? nullptr : &List_.Front()->Item;
        }

        void DoInsert(TQueueItem<TItem>& item) noexcept override {
            List_.PushBack(&item.GetListItem());
        }

        void DoRemove(TQueueItem<TItem>& item) noexcept override {
            item.UnlinkList();
        }

        bool DoEmpty() const noexcept override {
            return List_.Empty();
        }

        size_t DoSize() const noexcept override {
            return List_.Size();
        }

    protected:
        TIntrusiveList<typename TQueueItem<TItem>::TListItem> List_;
    };


    template <class TItem>
    class TStaticFIFOQueue : public TFIFOQueueBase<TItem> {
    public:
        using TFIFOQueueBase<TItem>::TFIFOQueueBase;

    private:
        void DoUpdate(TQueueItem<TItem>&) noexcept override {}
    };


    template <class TItem>
    class TRoundRobinQueue : public TFIFOQueueBase<TItem> {
    public:
        using TFIFOQueueBase<TItem>::TFIFOQueueBase;

    private:
        void DoUpdate(TQueueItem<TItem>& item) noexcept override {
            this->Insert(item);
        }
    };


    template <class TItem, class TItemLess>
    class THeapQueueBase : public IQueue<TItem> {
        struct THeapLess {
            // We assume that comparator can't throw an exception.
            bool operator()(const typename TQueueItem<TItem>::THeapItem& a,
                            const typename TQueueItem<TItem>::THeapItem& b) const noexcept {
                return TItemLess{}(b.Item.Get(), a.Item.Get());
            }
        };

    public:
        explicit THeapQueueBase() noexcept {}

    protected:
        TQueueItem<TItem>* DoGetNext() noexcept override {
            return Heap_.Empty() ? nullptr : &Heap_.GetMin()->Get().Item;
        }

        void DoUpdate(TQueueItem<TItem>& item) noexcept override {
            Heap_.Update(item.GetHeapItem());
        }

        void DoInsert(TQueueItem<TItem>& item) noexcept override {
            Heap_.Insert(item.GetHeapItem());
        }

        void DoRemove(TQueueItem<TItem>& item) noexcept override {
            item.UnlinkHeap();
        }

        bool DoEmpty() const noexcept override {
            return Heap_.Empty();
        }

        size_t DoSize() const noexcept override {
            return Heap_.Size();
        }

    private:
        TIntrusiveFlatHeap<typename TQueueItem<TItem>::THeapItem, THeapLess> Heap_;
    };
}
