// Copyright (c) 2022 Dominic Masters
// 
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

#pragma once
#include "scene/SceneItemComponent.hpp"
#include "display/RenderTarget.hpp"
#include "scene/Scene.hpp"
#include "physics/3d/Ray3D.hpp"

namespace Dawn {
  enum CameraType {
    CAMERA_TYPE_ORTHONOGRAPHIC,
    CAMERA_TYPE_PERSPECTIVE
  };

  class Camera : public SceneItemComponent {
    protected:
      bool_t projectionNeedsUpdating = true;
      glm::mat4 projection;
      std::function<void()> evtResized;

    public:
      static Camera * create(Scene *scene) {
        auto item = scene->createSceneItem();
        auto cam = item->addComponent<Camera>();
        return cam;
      }

      StateProperty<RenderTarget*> renderTarget;
      StateProperty<float_t> fov;
      StateProperty<enum CameraType> type;

      StateProperty<float_t> orthoLeft;
      StateProperty<float_t> orthoRight;
      StateProperty<float_t> orthoBottom;
      StateProperty<float_t> orthoTop;

      StateProperty<float_t> clipNear;
      StateProperty<float_t> clipFar;

      StateEvent<float_t, float_t> eventRenderTargetResized;

      /**
       * Create a new Camera Component.
       * 
       * @param item SceneItem that this component belongs to.
       */
      Camera(SceneItem *item);

      /**
       * Returns the current projection matrix.
       * 
       * @return Projection matrix for this camera.
       */
      glm::mat4 getProjection();

      /**
       * Returns the intended render target for this camera to render to, will
       * automatically revert to the back buffer if no frame buffer is provided.
       * 
       * @return The target render target framebuffer.
       */
      RenderTarget * getRenderTarget();

      /**
       * Returs the aspect ratio of the camera.
       * 
       * @return The aspect ratio of the camera.
       */
      float_t getAspect();

      /**
       * Creates the directional vector for a given point on the screen space
       * coordinates provided. This is useful if you want to, say, cast a ray
       * based on a position on the screen. The directional vector is normalized
       * between -1 and 1 where -1 is the near clipping plane, and 1 is the
       * far clipping plane.
       * 
       * @param screenSpace Screen space vector (-1,-1 to 1,1) on the screen.
       * @return The vector for the direction to cast from.
       */
      glm::vec3 getRayDirectionFromScreenSpace(glm::vec2 screenSpace);

      /**
       * Event triggered by the scene item when the item is added to the scene.
       */
      void onStart() override;
  };
}