#pragma once

#include <vector>

#include <glm/glm.hpp>

namespace gb
{
template <typename vector_type>
class uv;

template <typename vector_type>
class uvm;

// Unified Vector Manager
template <typename vector_type>
class uvm
{
private:
    static std::vector<vector_type> m_arr;

public:
    using uv_type = uv<vector_type>;
    using index_type = typename uv_type::index_type;

    static vector_type& query(index_type i)
    {
        return m_arr[i];
    }

    static index_type make(const vector_type& val)
    {
        m_arr.push_back(val);
        return m_arr.size()-1;
    }

    static index_type make(vector_type&& val)
    {
        m_arr.push_back(std::move(val));
        return m_arr.size()-1;
    }

    static void free(index_type i)
    {
        // TODO
    }
};

template <typename vector_type>
::std::vector<vector_type> uvm<vector_type>::m_arr {};

template <typename vector_type>
class uv
{
public:
    using index_type = size_t;
    using uvm_type = uvm<vector_type>;

public:
    index_type i;

public:
    // copies the index only
    explicit uv(const uv& v)
    {
        i = v.i;
    }
    explicit uv(uv&& v)
    {
        i = v.i;
    }

    // make new vector
    static uv make(const vector_type& val)
    {
        return uv(uvm_type::make(val));
    }

    static uv make(vector_type&& val)
    {
        return uv(uvm_type::make(std::move(val)));
    }

    // copies the index only
    uv& operator=(const uv& v)
    {
        i = v.i;
    }

    uv& operator=(uv&& v)
    {
        i = v.i;
    }

    vector_type& operator*(void)
    {
        return uvm_type::query(i);
    }

    const vector_type& operator*(void) const
    {
        return uvm_type::query(i);
    }

    vector_type* operator->(void)
    {
        return &**this;
    }

    const vector_type* operator->(void) const
    {
        return &**this;
    }

private:
    explicit uv(index_type _i)
        : i(_i)
    {
    }
};

using uv4 = uv<glm::vec4>;
using uv2 = uv<glm::vec2>;
} // namespace gb
