#ifndef MAGNUM_RENDER_H
#define MAGNUM_RENDER_H

#include <Corrade/Containers/Optional.h>
#include <Magnum/GL/Framebuffer.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/Math/Vector2.h>
#include <Magnum/Platform/GLContext.h>
#include <Magnum/SceneGraph/Drawable.h>
#include <Magnum/SceneGraph/MatrixTransformation3D.h>
#include <Magnum/SceneGraph/Object.h>
#include <Magnum/SceneGraph/Scene.h>
#include <Magnum/Shaders/Flat.h>
#include <Magnum/Shaders/Phong.h>
#include "arc_ball_camera.h"

using namespace Magnum;

using Object3D = SceneGraph::Object<SceneGraph::MatrixTransformation3D>;
using Scene3D = SceneGraph::Scene<SceneGraph::MatrixTransformation3D>;

class GridDrawable : public SceneGraph::Drawable3D {
public:
  explicit GridDrawable(Object3D &object, Shaders::Flat3D &shader,
                        GL::Mesh &mesh, SceneGraph::DrawableGroup3D &drawables)
    : SceneGraph::Drawable3D{object, &drawables},
      m_shader(shader),
      m_mesh(mesh) {}

  void draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera);

private:
  Shaders::Flat3D &m_shader;
  GL::Mesh &m_mesh;
};

class SubjectDrawable : public SceneGraph::Drawable3D {
public:
  explicit SubjectDrawable(Object3D &object, Shaders::Phong &shader,
                           GL::Mesh &mesh, SceneGraph::DrawableGroup3D &drawables,
                           int count, float hue, float t)
      : SceneGraph::Drawable3D{object, &drawables},
        m_shader(shader),
        m_mesh(mesh),
        m_count(count),
        m_hue(hue),
        m_t(t) {}

  void draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera);

  void count(int count);
  void hue(float hue);
  void t(float t);

private:
  Shaders::Phong &m_shader;
  GL::Mesh &m_mesh;
  int m_count;
  float m_hue;
  float m_t;
};

class MagnumRenderer {
public:
  MagnumRenderer();
  ~MagnumRenderer();

  /**
   * @brief Called when Qt needs to resize/re-create FBO.
   */
  void reset(Platform::GLContext *ctx,
             GL::Framebuffer fbo,
             Vector2i windowSize,
             Vector2i viewportSize);

  void render();

  void t(float t);
  void hue(float hue);
  void count(int count);

  Containers::Optional<ArcBallCamera>& camera();
  void lagging(float lagging);
  float lagging();

private:
  /**
   * @brief We need to delay initialization until we have valid OpenGL context.
   */
  void lazyInitialize();
  /**
   * @brief Reset OpenGL state to our desired state.
   */
  void prepGLState();

  bool m_init;

  Platform::GLContext *m_ctx{nullptr};
  GL::Framebuffer m_FBO{NoCreate};

  Scene3D m_scene;
  SceneGraph::DrawableGroup3D m_drawables;
  Containers::Optional<ArcBallCamera> m_camera;

  GL::Mesh m_subject{NoCreate};
  Shaders::Phong m_subjectShader{NoCreate};
  Object3D* m_subjectObject{nullptr};
  SubjectDrawable* m_subjectDrawable{nullptr};

  GL::Mesh m_grid{NoCreate};
  Shaders::Flat3D m_gridShader{NoCreate};
  Object3D* m_gridObject{nullptr};
  GridDrawable* m_gridDrawable{nullptr};
};

#endif // MAGNUM_RENDER_H
