#ifndef ARCBALLCAMERA_H
#define ARCBALLCAMERA_H

#include <Magnum/SceneGraph/AbstractTranslationRotation3D.h>
#include <Magnum/SceneGraph/Camera.h>
#include <Magnum/SceneGraph/Object.h>
#include <Magnum/SceneGraph/Scene.h>
#include "arc_ball.h"

using namespace Magnum;

/* Arcball camera implementation integrated into the SceneGraph */
class ArcBallCamera : public ArcBall {
public:
  template <class Transformation>
  ArcBallCamera(SceneGraph::Scene<Transformation> &scene,
                const Vector3 &cameraPosition, const Vector3 &viewCenter,
                const Vector3 &upDir, Deg fov, const Vector2i &windowSize,
                const Vector2i &viewportSize)
      : ArcBall{cameraPosition, viewCenter, upDir, fov, windowSize} {
    /* Create a camera object of a concrete type */
    auto *cameraObject = new SceneGraph::Object<Transformation>{&scene};
    (*(m_camera = new SceneGraph::Camera3D{*cameraObject}))
        .setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend)
        .setProjectionMatrix(Matrix4::perspectiveProjection(
            fov, Vector2{windowSize}.aspectRatio(), 0.01f, 100.0f))
        .setViewport(viewportSize);

    /* Save the abstract transformation interface and initialize the
       camera position through that */
    (*(m_cameraObject = cameraObject))
        .rotate(transformation().rotation())
        .translate(transformation().translation());
  }

  /* Update screen and viewport size after the window has been resized */
  void reshape(const Vector2i &windowSize, const Vector2i &viewportSize) {
    m_windowSize = windowSize;
    m_camera->setViewport(viewportSize);
  }

  /* Update the SceneGraph camera if arcball has been changed */
  bool update() {
    /* call the internal update */
    if (!updateTransformation())
      return false;

    (*m_cameraObject)
        .resetTransformation()
        .rotate(transformation().rotation())
        .translate(transformation().translation());
    return true;
  }

  /* Draw objects using the internal scenegraph camera */
  void draw(SceneGraph::DrawableGroup3D &drawables) {
    m_camera->draw(drawables);
  }

private:
  SceneGraph::AbstractTranslationRotation3D *m_cameraObject{};
  SceneGraph::Camera3D *m_camera{};
};

#endif
