/// Guards

#ifndef GLBACKEND_HPP_
#define GLBACKEND_HPP_


/// Includes

#include <array>
#include <functional>
#include <ostream>
#include <string>
#include <utility>
#include <vector>

#include <glbase.hpp>


/// Class definition

class GLBackend : public GLBase
{

public:

    //// Context

    void virtual current() = 0;

    //// Render loop

    void virtual swap() = 0;

    void virtual events() = 0;

    bool virtual running() const = 0;
    bool virtual running(bool running) = 0;

    float virtual time() const = 0;
    float virtual time(float time) = 0;

    void run(float dt_fixed = 0.0F);

    using CallbackUpdate   = std::function<void(float t, float dt, bool last)>;
    using CallbackRender   = std::function<void()>;

    GLBASE_ACCESS(CallbackUpdate,   callback_update)
    GLBASE_ACCESS(CallbackRender,   callback_render)

    //// Input and output

    using Point = std::array<GLfloat, 2>;
    using Size  = std::array<GLsizei, 2>;

    void virtual lock(bool lock) = 0;

    bool virtual key   (std::string const & key)    const = 0;
    bool virtual button(int                 button) const = 0;

    GLBASE_GET(Point, scroll)
    GLBASE_GET(Point, position)
    GLBASE_GET(Point, move)
    GLBASE_GET(Size,  size)

    using CallbackKey      = std::function<void(std::string const &)>;
    using CallbackButton   = std::function<void(int)>;
    using CallbackScroll   = std::function<void(Point)>;
    using CallbackPosition = std::function<void(Point)>;
    using CallbackMove     = std::function<void(Point)>;
    using CallbackSize     = std::function<void(Size)>;

    GLBASE_ACCESS(CallbackKey,      callback_key)
    GLBASE_ACCESS(CallbackButton,   callback_button)
    GLBASE_ACCESS(CallbackScroll,   callback_scroll)
    GLBASE_ACCESS(CallbackPosition, callback_position)
    GLBASE_ACCESS(CallbackMove,     callback_move)
    GLBASE_ACCESS(CallbackSize,     callback_size)

    //// Path

    GLBASE_ACCESS_GLOBAL(Path, prefix)

    //// TGA

    void tga_write(
        Path const & path
    ) const;

    bool tga_compare(
        Path const & path,
        bool         write_on_failed_read = false
    ) const;

    //// Debug

    std::string virtual debug_info() const;

protected:

    //// Special member functions

    GLBackend();
    GLBackend(GLBackend &&) = default;
    virtual ~GLBackend() = default;

    void static init_();

    //// TGA

    TGA_ tga_() const;

    //// Debug

    void static debug_info_(
        std::ostream                                           & ostream,
        std::string                                      const & category,
        std::vector<std::pair<std::string, std::string>> const & values,
        char                                                     fill = '.'
    );

    void static GLAPIENTRY debug_gl_message_callback_(
        GLenum          source,
        GLenum          type,
        GLuint          id,
        GLenum          severity,
        GLsizei         length,
        GLchar  const * message,
        void    const * user_param
    );

    std::string static debug_gl_message_(std::string message);

protected:

    //// Render loop

    CallbackUpdate   callback_update_;
    CallbackRender   callback_render_;

    //// Input and output

    Point scroll_;
    Point position_;
    Point move_;
    Size  size_;

    CallbackKey      callback_key_;
    CallbackButton   callback_button_;
    CallbackScroll   callback_scroll_;
    CallbackPosition callback_position_;
    CallbackMove     callback_move_;
    CallbackSize     callback_size_;

    //// Path

    Path static thread_local prefix_;

};


/// Guards

#endif
