#pragma once
#include "image.h"

struct PBO
{
    // copy
    PBO(const PBO&) = delete;
    PBO& operator=(const PBO&) = delete;
    // move
    PBO(PBO&& other) noexcept;
    PBO& operator=(PBO&&) noexcept;
    // default
    PBO() noexcept = default;
    ~PBO() noexcept;

    GLuint buffer_id = 0;
    GLenum bound_slot = 0;
    GLubyte* mapped_ptr = nullptr;

    int width = 0;
    int height = 0;

    bool create() noexcept;
    bool create(class RTT& rtt) noexcept;
    void destroy() noexcept;
/*
    void bind_read() noexcept;
    void unbind() noexcept;
*/
    uint8_t* map() noexcept;
    void unmap() noexcept;
};

class RTT
{
    int w = 0;
    int h = 0;
    GLuint fboID = 0;
    GLuint texID = 0;
    GLuint rboID = 0;
    GLint int_fmt = 0;
    bool bound = false;
    GLint oldRFboID = 0;
    GLint oldDFboID = 0;

public:
    // copy
    RTT(const RTT&) = delete;
    RTT& operator=(const RTT&) = delete;
    // move
    RTT(RTT&& other);
    RTT& operator=(RTT&&);
    // default
    RTT();
    ~RTT();

    void destroy();
    // copy with interpolation
    void copy(const RTT& source);
    // copy a region
    void copy(const RTT& source, const glm::vec4& rect);
    RTT clone() const noexcept;
    bool resize(int width, int height);
    bool create(int width, int height);
    bool create(int width, int height, int tex);
    bool create(int width, int height, int tex, GLint internal_format, bool depth_buffer = false);
    bool recreate() { return create(w, h); }
    void clear(glm::vec4 color = glm::vec4(0));
    void clear_mask(glm::bool4 mask, glm::vec4 color = glm::vec4(0));
    glm::ivec4 calc_bounds() const noexcept;
    bool updateRgba8(int x, int y, int width, int height, const void* data) noexcept;
    bool readPixelsRgba8(int x, int y, int width, int height, void* buffer) const noexcept;
    uint8_t* readTextureData(uint8_t* buffer = nullptr) const noexcept;
    float* readTextureDataFloat(float* buffer = nullptr) const noexcept;
    uint8_t* createBuffer() const noexcept;
    float* createBufferFloat() const noexcept;
    void bindFramebuffer();
    void unbindFramebuffer();
    void bindTexture();
    void unbindTexture();
    GLuint getTextureID() const noexcept { return texID; }
    int getWidth() const noexcept { return w; }
    int getHeight() const noexcept { return h; }
    glm::ivec2 getSize() const noexcept { return { w, h }; }
    int bytes() const noexcept { return w * h * 4; }
    int stride() const noexcept { return w * 4; }
    GLuint getFBO() const noexcept { return fboID; }
    Image get_image() const noexcept;
    bool valid() const noexcept;
};
