/// Guards

#ifdef  GLBACKEND_SDL
#ifndef GLBACKEND_SDL_HPP_
#define GLBACKEND_SDL_HPP_


/// Includes

#include <array>
#include <string>

#include <glbackend.hpp>

// #include <SDL2/SDL.h>
#include <SDL2/SDL_keyboard.h>
#include <SDL2/SDL_mouse.h>
#include <SDL2/SDL_scancode.h>
#include <SDL2/SDL_stdinc.h>
#include <SDL2/SDL_timer.h>
#include <SDL2/SDL_video.h>


/// Class definition

class GLBackendSDL final : public GLBackend
{

public:

    //// Special member functions

    explicit GLBackendSDL(
        std::string const & title,
        std::array<int, 2>  size        = {0, 0},
        std::array<int, 2>  version     = {0, 0},
        int                 samples     = 0,
        bool                fullscreen  = false,
        bool                transparent = false
    );
    ~GLBackendSDL();
    GLBackendSDL(GLBackendSDL &&) noexcept;
    GLBackendSDL(GLBackendSDL const &) = delete;
    GLBackendSDL & operator=(GLBackendSDL &&) = delete;
    GLBackendSDL & operator=(GLBackendSDL const &) = delete;

    //// Context

    void current() override;

    //// Render loop

    void swap() override;

    void events() override;

    bool running() const override;
    bool running(bool running) override;

    float time() const override;
    float time(float time) override;

    //// Input and output

    void lock(bool lock) override;

    bool key   (std::string const & key)    const override;
    bool button(int                 button) const override;

    //// Debug

    std::string debug_info() const override;

protected:

    //// Special member functions

    void destroy_();

protected:

    //// Context

    SDL_Window    * window_;
    SDL_GLContext   context_;

    //// Render loop

    bool  running_;
    float time_;

};


/// Inline definitions

inline void GLBackendSDL::current()
{
    SDL_GL_MakeCurrent(window_, context_);
}

inline void GLBackendSDL::swap()
{
    SDL_GL_SwapWindow(window_);
}

inline bool GLBackendSDL::running(bool running)
{
    running_ = running;
    return running;
}

inline bool GLBackendSDL::running() const
{
    return running_;
}

inline float GLBackendSDL::time() const
{
    // return (float)SDL_GetTicks() / 1000.0F - time_;
    return
        (float)SDL_GetPerformanceCounter() /
        (float)SDL_GetPerformanceFrequency()
        - time_;
}

inline float GLBackendSDL::time(float time)
{
    // time_ = (float)SDL_GetTicks() / 1000.0F - time;
    time_ =
        (float)SDL_GetPerformanceCounter() /
        (float)SDL_GetPerformanceFrequency()
        - time;
    return time;
}

inline void GLBackendSDL::lock(bool lock)
{
    SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE);
}

inline bool GLBackendSDL::key(std::string const & key) const
{
    auto * keys = SDL_GetKeyboardState(nullptr);
    if (key == "Enter")
        return keys[SDL_SCANCODE_RETURN];
    else if (key == "Control")
        return keys[SDL_SCANCODE_RCTRL] || keys[SDL_SCANCODE_LCTRL];
    else if (key == "Shift")
        return keys[SDL_SCANCODE_RSHIFT] || keys[SDL_SCANCODE_LSHIFT];
    else if (key == "Alt")
        return keys[SDL_SCANCODE_RALT] || keys[SDL_SCANCODE_LALT];
    else
        return keys[SDL_GetScancodeFromName(key.c_str())];
}

inline bool GLBackendSDL::button(int button) const
{
    return SDL_GetMouseState(nullptr, nullptr) & (Uint32)SDL_BUTTON(button);
}


/// Guards

#endif
#endif
