/// Guards

#ifdef  GLBACKEND_WXWIDGETS
#ifndef GLBACKEND_WXWIDGETS_HPP_
#define GLBACKEND_WXWIDGETS_HPP_


/// Includes

#include <array>
#include <string>

#include <glbackend.hpp>

// #include <wx/wx.h>
#include <wx/evtloop.h>
#include <wx/glcanvas.h>
#include <wx/app.h>
#include <wx/gdicmn.h>
#include <wx/gtk/app.h>
#include <wx/gtk/evtloop.h>
#include <wx/longlong.h>
#include <wx/time.h>
#include <wx/unix/glx11.h>
#include "wx/cursor.h"
class wxAppConsole;
class wxFrame;


/// Class definition

class GLBackendWxWidgets final : public GLBackend
{

public:

    //// Special member functions

    explicit GLBackendWxWidgets(
        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
    );
    ~GLBackendWxWidgets();
    GLBackendWxWidgets(GLBackendWxWidgets &&) noexcept;
    GLBackendWxWidgets(GLBackendWxWidgets const &) = delete;
    GLBackendWxWidgets & operator=(GLBackendWxWidgets &&) = delete;
    GLBackendWxWidgets & operator=(GLBackendWxWidgets 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

    wxAppConsole * app_;
    wxFrame      * frame_;
    wxGLCanvas   * canvas_;
    wxGLContext  * context_;

    //// Render loop

    wxGUIEventLoop event_loop_;
    float          time_;

    //// Input and output

    bool locked_;

};


/// Inline definitions

inline void GLBackendWxWidgets::current()
{
    context_->SetCurrent(*canvas_);
}

inline void GLBackendWxWidgets::events()
{
    // wxWidgets/include/wx/evtloop.h
    // wxWidgets/src/common/evtloopcmn.cpp
    wxEventLoopActivator event_loop_activator(&event_loop_);
    while (event_loop_.Pending())
        event_loop_.Dispatch();
    event_loop_.ProcessIdle();
    if (wxTheApp)
        wxTheApp->ProcessPendingEvents();
}

inline void GLBackendWxWidgets::swap()
{
    canvas_->SwapBuffers();
}

inline bool GLBackendWxWidgets::running() const
{
    return canvas_->IsShown();
}

inline bool GLBackendWxWidgets::running(bool running)
{
    canvas_->Show(running);
    return running;
}

inline float GLBackendWxWidgets::time() const
{
    return (float)(wxGetUTCTimeUSec().ToDouble() / 1000000.0) - time_;
}

inline float GLBackendWxWidgets::time(float time)
{
    time_ = (float)(wxGetUTCTimeUSec().ToDouble() / 1000000.0) - time;
    return time;
}

inline void GLBackendWxWidgets::lock(bool lock)
{
    canvas_->SetCursor(lock ? wxCursor(wxCURSOR_BLANK) : wxNullCursor);
    if (lock && !locked_)
        canvas_->CaptureMouse();
    else if (!lock && locked_)
        canvas_->ReleaseMouse();
    locked_ = lock;
    // TODO(rcrnstn): wxWidgets `WarpPointer` needed?
    // canvas_->WarpPointer(
    //     size_[0] / 2,
    //     size_[1] / 2
    // );
}

inline bool GLBackendWxWidgets::key(std::string const & key) const
{
    (void)key;
    return false;
}
inline bool GLBackendWxWidgets::button(int button) const
{
    (void)button;
    return false;
}


/// Guards

#endif
#endif