#pragma once

#include <xrpl/basics/CountedObject.h>
#include <xrpl/protocol/STObject.h>

namespace xrpl {

class STArray final : public STBase, public CountedObject<STArray>
{
private:
    using list_type = std::vector<STObject>;

    list_type v_;

public:
    using value_type = STObject;
    using size_type = list_type::size_type;
    using iterator = list_type::iterator;
    using const_iterator = list_type::const_iterator;

    STArray() = default;
    STArray(STArray const&) = default;

    template <
        class Iter,
        class = std::enable_if_t<
            std::is_convertible_v<typename std::iterator_traits<Iter>::reference, STObject>>>
    explicit STArray(Iter first, Iter last);

    template <
        class Iter,
        class = std::enable_if_t<
            std::is_convertible_v<typename std::iterator_traits<Iter>::reference, STObject>>>
    STArray(SField const& f, Iter first, Iter last);

    STArray&
    operator=(STArray const&) = default;
    STArray(STArray&&);
    STArray&
    operator=(STArray&&);

    STArray(SField const& f, std::size_t n);
    STArray(SerialIter& sit, SField const& f, int depth = 0);
    explicit STArray(int n);
    explicit STArray(SField const& f);

    STObject&
    operator[](std::size_t j);

    STObject const&
    operator[](std::size_t j) const;

    STObject&
    back();

    STObject const&
    back() const;

    template <class... Args>
    void
    emplace_back(Args&&... args);

    void
    push_back(STObject const& object);

    void
    push_back(STObject&& object);

    iterator
    begin();

    iterator
    end();

    const_iterator
    begin() const;

    const_iterator
    end() const;

    size_type
    size() const;

    bool
    empty() const;

    void
    clear();

    void
    reserve(std::size_t n);

    void
    swap(STArray& a) noexcept;

    std::string
    getFullText() const override;

    std::string
    getText() const override;

    Json::Value
    getJson(JsonOptions index) const override;

    void
    add(Serializer& s) const override;

    void
    sort(bool (*compare)(STObject const& o1, STObject const& o2));

    bool
    operator==(STArray const& s) const;

    bool
    operator!=(STArray const& s) const;

    iterator
    erase(iterator pos);

    iterator
    erase(const_iterator pos);

    iterator
    erase(iterator first, iterator last);

    iterator
    erase(const_iterator first, const_iterator last);

    SerializedTypeID
    getSType() const override;

    bool
    isEquivalent(STBase const& t) const override;

    bool
    isDefault() const override;

private:
    STBase*
    copy(std::size_t n, void* buf) const override;
    STBase*
    move(std::size_t n, void* buf) override;

    friend class detail::STVar;
};

template <class Iter, class>
STArray::STArray(Iter first, Iter last) : v_(first, last)
{
}

template <class Iter, class>
STArray::STArray(SField const& f, Iter first, Iter last) : STBase(f), v_(first, last)
{
}

inline STObject&
STArray::operator[](std::size_t j)
{
    return v_[j];
}

inline STObject const&
STArray::operator[](std::size_t j) const
{
    return v_[j];
}

inline STObject&
STArray::back()
{
    return v_.back();
}

inline STObject const&
STArray::back() const
{
    return v_.back();
}

template <class... Args>
inline void
STArray::emplace_back(Args&&... args)
{
    v_.emplace_back(std::forward<Args>(args)...);
}

inline void
STArray::push_back(STObject const& object)
{
    v_.push_back(object);
}

inline void
STArray::push_back(STObject&& object)
{
    v_.push_back(std::move(object));
}

inline STArray::iterator
STArray::begin()
{
    return v_.begin();
}

inline STArray::iterator
STArray::end()
{
    return v_.end();
}

inline STArray::const_iterator
STArray::begin() const
{
    return v_.begin();
}

inline STArray::const_iterator
STArray::end() const
{
    return v_.end();
}

inline STArray::size_type
STArray::size() const
{
    return v_.size();
}

inline bool
STArray::empty() const
{
    return v_.empty();
}

inline void
STArray::clear()
{
    v_.clear();
}

inline void
STArray::reserve(std::size_t n)
{
    v_.reserve(n);
}

inline void
STArray::swap(STArray& a) noexcept
{
    v_.swap(a.v_);
}

inline bool
STArray::operator==(STArray const& s) const
{
    return v_ == s.v_;
}

inline bool
STArray::operator!=(STArray const& s) const
{
    return v_ != s.v_;
}

inline STArray::iterator
STArray::erase(iterator pos)
{
    return v_.erase(pos);
}

inline STArray::iterator
STArray::erase(const_iterator pos)
{
    return v_.erase(pos);
}

inline STArray::iterator
STArray::erase(iterator first, iterator last)
{
    return v_.erase(first, last);
}

inline STArray::iterator
STArray::erase(const_iterator first, const_iterator last)
{
    return v_.erase(first, last);
}

}  // namespace xrpl
