#pragma once
#include "hpack_definitions.h"

#include <balancer/kernel/helpers/errors.h>
#include <balancer/kernel/memory/alloc.h>
#include <balancer/kernel/memory/chunks.h>

namespace NSrvKernel::NHTTP2 {
    namespace NImpl {
        class THeaderFieldBlob : public TBigObj<THeaderFieldBlob>, public TSimpleRefCount<THeaderFieldBlob> {
        public:
            explicit THeaderFieldBlob(THeaderFieldView headerField) noexcept;

            THeaderFieldView GetHeaderField() const noexcept;

        private:
            const ui32 NameSize_ : 31;
            const ui32 HasValue_ : 1;
            const ui32 ValueSize_;
        };
        static_assert(alignof(THeaderFieldBlob) == alignof(size_t), "TBigObj's template parameter must be size_t-aligned type");

        class THeaderFieldBlobPtr : public TIntrusivePtr<THeaderFieldBlob> {
        public:
            using TBase = TIntrusivePtr<THeaderFieldBlob>;
            using TBase::TBase;

            operator THeaderFieldView() const noexcept {
                Y_VERIFY(Get());
                return Get()->GetHeaderField();
            }
        };

        using THeaderFieldsTable = TDeque<THeaderFieldBlobPtr>;

        class TIndexRef {
        public:
            ui64 Epoch_ = 0;
            ui32 RefCount_ = 0;

            TIndexRef() = default;

            explicit TIndexRef(ui64 epoch) noexcept
                : Epoch_(epoch)
                , RefCount_(1)
            {}
        };

        using THeaderFieldsIndex = THashMap<THeaderFieldBlobPtr, TIndexRef, THashOf<THeaderFieldView>, TEqualTo<THeaderFieldView>>;
    }


    class THPackTableBase : TMoveOnly {
    public:
        explicit THPackTableBase(const THPackBaseSettings& settings) noexcept;
        virtual ~THPackTableBase() = default;

        void SetMaxSize(size_t maxSize) noexcept;

        void AddHeaderField(THeaderFieldView headerField) noexcept;

        void DumpDynamicTable(size_t& sz, THeadersList& res) const noexcept;

    protected:
        void DoUpdateSize(ui32 newSize) noexcept;

        void DoShrinkTable() noexcept;

        virtual void DoRemoveHeaderField(THeaderFieldView) noexcept {}

        [[nodiscard]] virtual NImpl::THeaderFieldBlobPtr DoGetHeaderFieldBlob(
            THeaderFieldView headerField) noexcept;

    protected:
        NImpl::THeaderFieldsTable HeaderFieldsTable_;

        size_t MaxSize_ = RFC_HEADER_TABLE_SIZE_DEFAULT;
        size_t TableSize_ = MaxSize_;
        size_t CurrentSize_ = 0;
    };


    class THPackDecoderTable : public THPackTableBase {
    public:
        using THPackTableBase::THPackTableBase;

        TError UpdateSize(ui32 newSize) noexcept;

        TErrorOr<THeaderField> GetHeaderField(ui32 index, THPackLimits& limits) const noexcept;

        TErrorOr<TChunkPtr> GetHeaderName(ui32 index, THPackLimits& limits) const noexcept;

    private:
        TErrorOr<THeaderFieldView> DoGetHeaderField(ui32 index) const noexcept;
    };


    class THPackEncoderTable : public THPackTableBase {
    public:
        using THPackTableBase::THPackTableBase;

        [[nodiscard]] ui32 GetHeaderFieldIndex(THeaderFieldView headerField) const noexcept;

        [[nodiscard]] ui32 GetHeaderNameIndex(TStringBuf headerName) const noexcept;

    private:
        void DoRemoveHeaderField(THeaderFieldView) noexcept override;

        [[nodiscard]] NImpl::THeaderFieldBlobPtr DoGetHeaderFieldBlob(
            THeaderFieldView headerField) noexcept override;

        [[nodiscard]] NImpl::THeaderFieldBlobPtr DoInsertIndex(THeaderFieldView headerField) noexcept;

        void DoRemoveIndex(THeaderFieldView headerField) noexcept;

    private:
        NImpl::THeaderFieldsIndex HeaderFieldsIndex_;
        ui64 Epoch_ = 0;
    };
}
