#ifndef ARRAY_H
#define ARRAY_H


#include "Utilities.h"


// std::vector replacement that is cut down but familiar.
// It uses the custom allocators, and is restricted to POD elements only
template<typename T>
struct PODArrayPOD   // A dynamic array of POD type elements which is itself a POD type (so can have multi-dimensional arrays of these).
{
    static_assert(std::is_pod<T>::value, "T must be a POD type.");

    void init()                              { m_magic = 0x01020304; m_capacity = m_size = 0; m_data = nullptr; }
    void deinit()                            { if (m_data != nullptr) YQ_FREE(m_data); }

    const T& at(size_t a_pos) const          { checkInited(); checkIndex(a_pos); return m_data[a_pos]; }
    T& at(size_t a_pos)                      { checkInited(); checkIndex(a_pos); return m_data[a_pos]; }
    const T& operator[](size_t a_pos) const  { checkInited(); checkIndex(a_pos); return m_data[a_pos]; }
    void pop_back()                          { checkInited(); checkIndex(1); m_size--; }
    const T& back() const					 { checkInited(); return m_data[m_size - 1]; }
    T& back()								 { checkInited(); return m_data[m_size - 1]; }
    void push_back(const T& a_val)			 { checkInited(); if (m_size == m_capacity) ensureSpaceFor(1); m_data[m_size++] = a_val; }
    void push(const T& a_val)				 { checkInited(); push_back(a_val); }
    T pop()									 { checkInited(); pop_back(); return m_data[m_size]; }
    void reserve(size_t a_count)             { checkInited(); m_capacity = a_count; m_data = YQ_REALLOC_ARRAY(m_data, T, a_count); }
    size_t capacity() const                  { checkInited(); return m_capacity; }
    size_t size() const                      { checkInited(); return m_size; }
    T* data() const                          { checkInited(); return m_data; }
    void clear()                             { checkInited(); m_size = 0; }
//private: // Actually making private would make this no longer a POD type
    yqResult checkInited() const             { if (BT_IsDebug) YQ_API_PRECONDITION(m_magic == 0x01020304, R_Uninitialized); return R_Okay; }
    yqResult checkIndex(size_t a_pos) const  { YQ_API_PRECONDITION(a_pos < m_size, R_IndexOutOfRange); return R_Okay; }
    void ensureSpaceFor(size_t a_count)      { reserve(std::max<size_t>(m_size + a_count, m_capacity + m_capacity / 2)); } // try to grow by 50%
    T* m_data;  // Because of this pointer it is not safe to copy DynamicArray objects around. Making it a shared_ptr though stops this being a POD type
    size_t m_capacity;
    size_t m_size;
    uint32_t m_magic;
};


static_assert(std::is_pod<PODArrayPOD<char> >::value, "PODArrayPOD must be a POD type.");


// A dynamic array of POD type elements.
// It isn't POD itself, but is instead reference counted so safer to deal with.
template<typename T>
struct ArrayPOD
{
    static_assert(std::is_pod<T>::value, "T must be a POD type.");

    // Actually not reference counted yet, but safe as objects of this type can't be copied
    ArrayPOD& operator=(const ArrayPOD&) = delete;
    ArrayPOD(const ArrayPOD&) = delete;
    ArrayPOD()                               { m_capacity = m_size = 0; m_data = nullptr; }
    ~ArrayPOD()                              { if (m_data != nullptr) YQ_FREE(m_data); }

    const T& at(size_t a_pos) const          { checkIndex(a_pos); return m_data[a_pos]; }
    T& at(size_t a_pos)                      { checkIndex(a_pos); return m_data[a_pos]; }
    const T& operator[](size_t a_pos) const  { checkIndex(a_pos); return m_data[a_pos]; }
    void pop_back()                          { checkIndex(1); m_size--; }
    const T& back() const					 { return m_data[m_size - 1]; }
    T& back()								 { return m_data[m_size - 1]; }
    void push_back(const T& a_val)			 { if (m_size == m_capacity) ensureSpaceFor(1); m_data[m_size++] = a_val; }
    void push(const T& a_val)				 { push_back(a_val); }
    T pop()									 { pop_back(); return m_data[m_size]; }
    void reserve(size_t a_count)             { m_capacity = a_count; m_data = YQ_REALLOC_ARRAY(m_data, T, a_count); }
    size_t capacity() const                  { return m_capacity; }
    size_t size() const                      { return m_size; }
    T* data() const                          { return m_data; }
    void clear()                             { m_size = 0; }
private:
    yqResult checkIndex(size_t a_pos) const  { YQ_API_PRECONDITION(a_pos < m_size, R_IndexOutOfRange); return R_Okay; }
    void ensureSpaceFor(size_t a_count)      { reserve(std::max<size_t>(m_size + a_count, m_capacity + m_capacity / 2)); } // try to grow by 50%
    T* m_data;  // Because of this pointer it is not safe to copy DynamicArray objects around. Making it a shared_ptr though stops this being a POD type
    size_t m_capacity;
    size_t m_size;
};


struct PODString
{
public:
    void init()                              { m_data.init(); }
    void deinit()                            { m_data.deinit(); }
    void operator=(const char* a_str)        { m_data.clear(); operator+=(a_str); }
    void operator+=(const char* a_str)       { while (*a_str) m_data.push_back(*a_str++); m_data.push(0); m_data.pop(); }
    char* c_str()                            { return m_data.data(); }
//private: // Actually making private would make this no longer a POD type
    PODArrayPOD<char> m_data;
};


static_assert(std::is_pod<PODString>::value, "PODString must be a POD type.");


#endif // ARRAY_H
