#pragma once

#include "common.hpp"

namespace render {

/// Abstract raw data buffer
class Buffer {
public:
    virtual ~Buffer() { }

    // NOTE: matches VkBufferUsageFlags
    enum class Usage {
        TRANSFER_SRC = 1,
        TRANSFER_DST = 2,
        UNIFORM = 16,
        STORAGE = 32,
        INDEX = 64,
        VERTEX = 128
    };
    friend inline Usage operator|(Usage a, Usage b) {
        return static_cast<Usage>(static_cast<int>(a) | static_cast<int>(b));
    }

    struct requirement {
        requirement(size_t size, Usage usage, const data_view view = data_view()):
            size(size), usage(usage), view(view) { }

        size_t size;
        Usage usage;
        data_view view;
    };
};

/// Writable raw data buffer
class WritableBuffer: public Buffer {
public:
    static _FORCE_INLINE_ std::unique_ptr<WritableBuffer> Create(size_t size, Usage usage = Usage::TRANSFER_SRC, const data_view write = data_view()) {
        assert(createFunc != nullptr && "Uninitialized renderer");
        return createFunc(size, usage, write);
    }

    virtual void write(const data_view, size_t offset = 0) = 0;
    virtual void read(data_ref, size_t offset = 0) = 0;

protected:
    static std::unique_ptr<WritableBuffer> (*createFunc)(size_t, Usage, const data_view);
};

/// Fast raw data buffer
class FastBuffer: public Buffer {
public:
    // NOTE: implementation may implicitly add Usage::TRANSFER_DST
    static _FORCE_INLINE_ std::unique_ptr<FastBuffer> Create(size_t size, Usage usage, const data_view write = data_view()) {
        assert(createFunc != nullptr && "Uninitialized renderer");
        return createFunc(size, usage, write);
    }

protected:
    static std::unique_ptr<FastBuffer> (*createFunc)(size_t, Usage, const data_view);
};

/// Grouped vertex and u16 index buffers
/// NOTE: vertex structure is undefined
class ShortIndexedVertexBuffer: public Buffer {
public:
    static _FORCE_INLINE_ std::unique_ptr<ShortIndexedVertexBuffer> Create(const data_view vertices, const data_view indices) {
        assert(createFunc != nullptr && "Uninitialized renderer");
        return createFunc(vertices, indices);
    }

protected:
    static std::unique_ptr<ShortIndexedVertexBuffer> (*createFunc)(const data_view, const data_view);
};
}