#pragma once

#include <util/system/defaults.h>

#include <cstdint>
#include <iterator>
#include <limits>

namespace NYT {

////////////////////////////////////////////////////////////////////////////////

template <class T>
struct TCompactVectorOnHeapStorage;

//! A vector-like structure optimized for storing elements inline
//! and with little memory overhead.
/*!
 *  Stores up to #N (<= 254) elements inline.
 *
 *  When capacity starts exceeding #N, moves all elements to heap;
 *  \see #TCompactVectorOnHeapStorage.
 *
 *  When linked with YTAlloc, employs its API to adjust the on-heap capacity in accordance
 *  to the actual size of the allocated region.
 *
 *  Assuming the entropy and the alignment constraints, yields a seemingly optimal memory overhead.
 *  E.g. TCompactVector<uint8_t, 7> takes 8 bytes and TCompactVector<uint32_t, 3> takes 16 bytes.
 *  \see #ByteSize.
 *
 *  Assumes (and asserts) the following:
 *  1) the platform is 64 bit;
 *  2) the highest 8 bits of pointers returned by |malloc| are zeroes;
 *  3) the platform is little-endian.
 */
template <class T, size_t N>
class TCompactVector
{
public:
    static_assert(N < std::numeric_limits<uint8_t>::max());

    using size_type = size_t;
    using difference_type = ptrdiff_t;

    using value_type = T;

    using iterator = T*;
    using const_iterator = const T*;

    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
    using reverse_iterator = std::reverse_iterator<iterator>;

    using reference = T&;
    using const_reference = const T&;

    using pointer = T*;
    using const_pointer = const T*;

    TCompactVector() noexcept;
    TCompactVector(const TCompactVector& other);
    template <size_t OtherN>
    TCompactVector(const TCompactVector<T, OtherN>& other);
    TCompactVector(TCompactVector&& other) noexcept(std::is_nothrow_move_constructible_v<T>);
    template <size_t OtherN>
    TCompactVector(TCompactVector<T, OtherN>&& other);
    explicit TCompactVector(size_type count);
    TCompactVector(size_type count, const T& value);
    template <class TIterator>
    TCompactVector(TIterator first, TIterator last);
    TCompactVector(std::initializer_list<T> list);

     ~TCompactVector();

    [[nodiscard]] bool empty() const;

    iterator begin();
    const_iterator begin() const;
    iterator end();
    const_iterator end() const;

    reverse_iterator rbegin();
    const_reverse_iterator rbegin() const;
    reverse_iterator rend();
    const_reverse_iterator rend() const;

    size_type size() const;
    size_type capacity() const;
    size_type max_size() const;

    pointer data();
    const_pointer data() const;

    reference operator[](size_type index);
    const_reference operator[](size_type index) const;

    reference front();
    const_reference front() const;
    reference back();
    const_reference back() const;

    void push_back(const T& value);
    void push_back(T&& value);

    template <class... TArgs>
    iterator emplace(const_iterator pos, TArgs&&... args);
    template <class... TArgs>
    reference emplace_back(TArgs&&... args);

    void pop_back();

    iterator erase(const_iterator pos);
    iterator erase(const_iterator first, const_iterator last);

    void clear();

    void resize(size_type newSize);
    void resize(size_type newSize, const T& value);

    void reserve(size_type newCapacity);

    void swap(TCompactVector& other);

    void assign(size_type count, const T& value);
    template <class TIterator>
    void assign(TIterator first, TIterator last);
    void assign(std::initializer_list<T> list);
    template <size_t OtherN>
    void assign(const TCompactVector<T, OtherN>& other);
    template <size_t OtherN>
    void assign(TCompactVector<T, OtherN>&& other);

    TCompactVector& operator=(const TCompactVector& other);
    template <size_t OtherN>
    TCompactVector& operator=(const TCompactVector<T, OtherN>& other);
    TCompactVector& operator=(TCompactVector&& other);
    template <size_t OtherN>
    TCompactVector& operator=(TCompactVector<T, OtherN>&& other);
    TCompactVector& operator=(std::initializer_list<T> list);

    iterator insert(const_iterator pos, const T& value);
    iterator insert(const_iterator pos, T&& value);
    iterator insert(const_iterator pos, size_type count, const T& value);
    template <class TIterator>
    iterator insert(const_iterator pos, TIterator first, TIterator last);
    iterator insert(const_iterator pos, std::initializer_list<T> list);

    void shrink_to_small();

private:
    template <class OtherT, size_t OtherN>
    friend class TCompactVector;

    using TOnHeapStorage = TCompactVectorOnHeapStorage<T>;

    static constexpr size_t ByteSize =
        (sizeof(T) * N + alignof(T) + sizeof(uintptr_t) - 1) &
        ~(sizeof(uintptr_t) - 1);

    struct TInlineMeta
    {
        char Padding[ByteSize - sizeof(uint8_t)];
        //  > 0 indicates inline storage
        // == 0 indicates on-heap storage
        uint8_t SizePlusOne;
    } alias_hack;

    struct TOnHeapMeta
    {
        char Padding[ByteSize - sizeof(uintptr_t)];
        TOnHeapStorage* Storage;
    } alias_hack;

    union
    {
        T InlineElements_[N];
        TInlineMeta InlineMeta_;
        TOnHeapMeta OnHeapMeta_;
    };

    bool IsInline() const;
    void SetSize(size_t newSize);
    void EnsureOnHeapCapacity(size_t newCapacity, bool incremental);
    template <class TPtr, class F>
    reference PushBackImpl(TPtr valuePtr, F&& func);
    template <class F>
    void ResizeImpl(size_t newSize, F&& func);
    template <class TPtr, class UninitializedF, class InitializedF>
    iterator InsertOneImpl(const_iterator pos, TPtr valuePtr, UninitializedF&& uninitializedFunc, InitializedF&& initializedFunc);
    template <class UninitializedF, class InitializedF>
    iterator InsertManyImpl(const_iterator pos, size_t insertCount, UninitializedF&& uninitializedFunc, InitializedF&& initializedFunc);

    static void Destroy(T* first, T* last);
    template <class T1, class T2>
    static void Copy(const T1* srcFirst, const T1* srcLast, T2* dst);
    template <class T1, class T2>
    static void UninitializedCopy(const T1* srcFirst, const T1* srcLast, T2* dst);
    static void Move(T* srcFirst, T* srcLast, T* dst);
    static void MoveBackward(T* srcFirst, T* srcLast, T* dst);
    static void UninitializedMove(T* srcFirst, T* srcLast, T* dst);
};

////////////////////////////////////////////////////////////////////////////////

template <class T, size_t LhsN, size_t RhsN>
bool operator==(const TCompactVector<T, LhsN>& lhs, const TCompactVector<T, RhsN>& rhs);

template <class T, size_t LhsN, size_t RhsN>
bool operator!=(const TCompactVector<T, LhsN>& lhs, const TCompactVector<T, RhsN>& rhs);

template <class T, size_t LhsN, size_t RhsN>
bool operator<(const TCompactVector<T, LhsN>& lhs, const TCompactVector<T, RhsN>& rhs);

////////////////////////////////////////////////////////////////////////////////

} // namespace NYT

#define COMPACT_VECTOR_INL_H_
#include "compact_vector-inl.h"
#undef COMPACT_VECTOR_INL_H_