text: #include "hdrpch.h"
  #include "EditorCamera.h"
  
  #include "hedron/core/Input.h"
  #include "hedron/core/KeyCodes.h"
  #include "hedron/core/MouseButtonCodes.h"
  
  #include <glfw/glfw3.h>
  
  #define GLM_ENABLE_EXPERIMENTAL
  #include <glm/gtx/quaternion.hpp>
  
  namespace hedron
  {
  
  	EditorCamera::EditorCamera(float fov, float aspect_ratio, float near_clip, float far_clip)
  		: m_fov(fov), m_aspect_ratio(aspect_ratio), m_near_clip(near_clip), m_far_clip(far_clip)
  	{
  		update_view();
  	}
  
  	void EditorCamera::on_update(Timestep ts)
  	{
  		HDR_PROFILE_FUNCTION();
  
  		if (Input::GetKey(KeyCode::LEFT_ALT))
  		{
  			const vec2f32& mouse{ Input::get_mouse_x(), Input::get_mouse_y() };
  			vec2f32 delta = (mouse - m_initial_mouse_position) * 0.003f;
  			m_initial_mouse_position = mouse;
  
  			if (Input::GetMouseButton(MouseButton::MOUSE_BUTTON_MIDDLE))
  				mouse_pan(delta);
  			else if (Input::GetMouseButton(MouseButton::MOUSE_BUTTON_LEFT))
  				mouse_rotate(delta);
  			else if (Input::GetMouseButton(MouseButton::MOUSE_BUTTON_RIGHT))
  				mouse_zoom(delta.y);
  		}
  
  		update_view();
  	}
  
  	void EditorCamera::on_event(Event& event)
  	{
  		HDR_PROFILE_FUNCTION();
  
  		EventDispatcher dispatcher(event);
  		dispatcher.dispatch<MouseScrolledEvent>(HDR_BIND_EVENT_FN(EditorCamera::on_mouse_scroll));
  	}
  
  	vec3f32 EditorCamera::get_up_direction() const
  	{
  		return glm::rotate(get_orientation(), vec3f32(0.0f, 1.0f, 0.0f));
  	}
  
  	vec3f32 EditorCamera::get_right_direction() const
  	{
  		return glm::rotate(get_orientation(), vec3f32(1.0f, 0.0f, 0.0f));
  	}
  
  	vec3f32 EditorCamera::get_forward_direction() const
  	{
  		return glm::rotate(get_orientation(), vec3f32(0.0f, 0.0f, -1.0f));
  	}
  
  	glm::quat EditorCamera::get_orientation() const
  	{
  		return glm::quat(vec3f32(-m_pitch, -m_yaw, 0.0f));
  	}
  
  	void EditorCamera::update_projection()
  	{
  		HDR_PROFILE_FUNCTION();
  
  		m_aspect_ratio = m_viewport_width / m_viewport_height;
  		m_projection = glm::perspective(glm::radians(m_fov), m_aspect_ratio, m_near_clip, m_far_clip);
  	}
  
  	void EditorCamera::update_view()
  	{
  		HDR_PROFILE_FUNCTION();
  
  		// m_yaw = m_pitch = 0.0f; // Lock the camera rotation
  		m_position = calculate_position();
  
  		glm::quat orientation = get_orientation();
  		m_view_matrix = glm::translate(mat4f32(1.0f), m_position) * glm::toMat4(orientation);
  		m_view_matrix = glm::inverse(m_view_matrix);
  	}
  
  	bool EditorCamera::on_mouse_scroll(MouseScrolledEvent& e)
  	{
  		HDR_PROFILE_FUNCTION();
  
  		float delta = e.get_y_offset() * 0.1f;
  		mouse_zoom(delta);
  		update_view();
  		return false;
  	}
  
  	void EditorCamera::mouse_pan(const vec2f32& delta)
  	{
  		HDR_PROFILE_FUNCTION();
  
  		auto [x_speed, y_speed] = pan_speed();
  		m_focal_point += -get_right_direction() * delta.x * x_speed * m_distance;
  		m_focal_point += get_up_direction() * delta.y * y_speed * m_distance;
  	}
  
  	void EditorCamera::mouse_rotate(const vec2f32& delta)
  	{
  		HDR_PROFILE_FUNCTION();
  
  		float yaw_sign = get_up_direction().y < 0 ? -1.0f : 1.0f;
  		m_yaw += yaw_sign * delta.x * rotation_speed(); // Y axis rotation
  		m_pitch += delta.y * rotation_speed(); // X axis rotation
  	}
  
  	void EditorCamera::mouse_zoom(float delta)
  	{
  		HDR_PROFILE_FUNCTION();
  
  		m_distance -= delta * zoom_speed();
  		if (m_distance < 1.0f)
  		{
  			m_focal_point += get_forward_direction();
  			m_distance = 1.0f;
  		}
  	}
  
  	vec3f32 EditorCamera::calculate_position() const
  	{
  		HDR_PROFILE_FUNCTION();
  
  		return m_focal_point - get_forward_direction() * m_distance;
  	}
  
  	std::pair<float, float> EditorCamera::pan_speed() const
  	{
  		HDR_PROFILE_FUNCTION();
  
  		float x = std::min(m_viewport_width / 1000.0f, 2.4f); // max = 2.4f
  		float x_factor = 0.0366f * (x * x) - 0.1778f * x + 0.3021f;
  
  		float y = std::min(m_viewport_height / 1000.0f, 2.4f); // max = 2.4f
  		float y_factor = 0.0366f * (y * y) - 0.1778f * y + 0.3021f;
  
  		return { x_factor, y_factor };
  	}
  
  	float EditorCamera::rotation_speed() const
  	{
  		return 0.8f;
  	}
  
  	float EditorCamera::zoom_speed() const
  	{
  		HDR_PROFILE_FUNCTION();
  
  		float distance = m_distance * 0.2f;
  		distance = std::max(distance, 0.0f);
  		float speed = distance * distance;
  		speed = std::min(speed, 100.0f);
  		return speed;
  	}
  
  }