#ifndef BUFFER_HPP
#define BUFFER_HPP

#include <cstdint>
#include <vector>
#include <string>

namespace OpenEngine {

    enum class ShaderDataType
	{
		Float = 0, Float2, Float3, Float4, Mat3, Mat4, Int, Int2, Int3, Int4, Bool
	};

	uint32_t shaderDataTypeSize(ShaderDataType type);

    struct BufferLayoutElement
    {
        std::string name;
        ShaderDataType type;
        uint32_t size;
        uint32_t offset;
        bool normalized;

        BufferLayoutElement() {};
        BufferLayoutElement(ShaderDataType type, const std::string& name, bool normalized = false)
            : name(name), type(type), size(shaderDataTypeSize(type)), offset(0), normalized(normalized)
        {
        };

        uint32_t GetComponentCount() const;
    };

    class BufferLayout
    {
        public:
            BufferLayout() {};
            BufferLayout(const std::initializer_list<BufferLayoutElement>& elements)
                : elements(elements)
            {
                CalculateOffsetsAndStride();
            };

            inline uint32_t GetStride() const { return stride; };
            inline const std::vector<BufferLayoutElement>& GetElements() const { return elements; };

            std::vector<BufferLayoutElement>::iterator begin() { return elements.begin(); };
            std::vector<BufferLayoutElement>::iterator end() { return elements.end(); };
            std::vector<BufferLayoutElement>::const_iterator begin() const { return elements.begin(); };
            std::vector<BufferLayoutElement>::const_iterator end() const { return elements.end(); };

        private:
            void CalculateOffsetsAndStride();

            std::vector<BufferLayoutElement> elements;
            uint32_t stride = 0;
    };

    class VertexBuffer
    {
        public:
            virtual ~VertexBuffer() = default;

            virtual void SetData(const void* data, uint32_t size) = 0;

            virtual void Bind() const = 0;
            virtual void UnBind() const = 0;

            virtual const BufferLayout& GetLayout() const = 0;
            virtual void SetLayout(const BufferLayout& layout) = 0;

            static Ref<VertexBuffer> Create(uint32_t size);
            static Ref<VertexBuffer> Create(float* vertices, uint32_t size);
    };

    class IndexBuffer
    {
        public:
            virtual ~IndexBuffer() = default;

            virtual void Bind() const = 0;
            virtual void UnBind() const = 0;

            virtual uint32_t GetCount() const = 0;

            static Ref<IndexBuffer> Create(uint32_t* indices, uint32_t count);
    };
}

#endif // BUFFER_HPP
