#pragma once

#include <glm/glm.hpp>

#include "glm/geometric.hpp"
#include "object.h"

namespace kek {

class Plane {
	glm::vec3 normal;
	float distance;

  public:
	Plane(glm::vec3 normal, float distance)
		: normal(glm::normalize(normal)),
		  distance(distance){};

	Plane(glm::vec3 point, glm::vec3 normal)
		: normal(glm::normalize(normal)),
		  distance(glm::dot(this->normal, point)){};

	float getDistance(glm::vec3 point) const;
};

struct Frustum {
	const Plane near;
	const Plane far;
	const Plane top;
	const Plane bottom;
	const Plane left;
	const Plane right;

	Frustum(Plane near, Plane far, Plane top, Plane bottom, Plane left, Plane right)
		: near(near),
		  far(far),
		  top(top),
		  bottom(bottom),
		  left(left),
		  right(right){};

	bool contains(glm::vec3 point) const;
};

class BoundingVolume {
  public:
	virtual bool isContained(const Frustum &frustum, glm::vec3 position, glm::quat rotation) = 0;
};

class SphereBoundingVolume: public BoundingVolume {
  private:
	glm::vec3 offset;
	float radius;

  public:
	SphereBoundingVolume(glm::vec3 offset, float radius)
		: offset(offset),
		  radius(radius) {}

	virtual bool isContained(const Frustum &frustum, glm::vec3 position, glm::quat rotation);
};

class Camera: public DefaultObject {

  public:
	glm::vec3 direction;
	float roll;

	Camera();

	void lookAt(glm::vec3 direction);

	void lookAtPos(glm::vec3 pos);

	void setYaw(float degrees);

	void rotateYaw(float degrees);

	void setPitch(float degrees);

	void rotatePitch(float degrees);

	void setRoll(float degrees);

	void rotateRoll(float degrees);

	glm::mat4 transformationMatrix();

	void lookAtMatrix(glm::mat4 matrix);

	// x = yaw, y = pitch, z = roll
	glm::vec3 eulerAngles();

	void applyEuler(glm::vec3 euler);

	Frustum getFrustum(float aspect);
};

}
