/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#include "ArBackgroundDrawable.hpp"
#include "AR/ArSession.hpp"
#include "AR/ArFrame.hpp"
#include "Base/Logger.hpp"

namespace OpenVulkano::Scene
{
	namespace
	{
		const Math::CameraIntrinsicWithResolution FALLBACK_INTRINSICS;
	}

	ArBackgroundDrawable::ArBackgroundDrawable(const Ptr<AR::ArSession>& arSession)
			: Drawable(DrawEncoder::GetDrawEncoder<ArBackgroundDrawable>(), DrawPhase::BACKGROUND)
			, m_arSession(arSession)
	{
		m_shader.topology = Topology::TRIANGLE_STRIP;
		m_shader.AddShaderProgram(ShaderProgramType::VERTEX, "Shader/background");
		m_shader.AddShaderProgram(ShaderProgramType::FRAGMENT, "Shader/background");
		m_shader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING);
		m_shader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING);
		m_shader.depthCompareOp = CompareOp::LESS_OR_EQUAL;
		SetShader(&m_shader);
		m_intrinsicsBuffer.Init(sizeof(Math::CameraIntrinsicWithResolution), &FALLBACK_INTRINSICS);
		m_intrinsicsBuffer.updateFrequency = UpdateFrequency::Always;
		if (m_arSession)
			m_arSession->OnNewFrame += EventHandler(this, &ArBackgroundDrawable::OnNewArFrame);
	}

	void ArBackgroundDrawable::Init(const Ptr<AR::ArSession>& arSession)
	{
		if (m_arSession)
		{
			if (m_arSession == arSession) return;
			Close();
		}
		if (!arSession) return;
		m_arSession = arSession;
		m_arSession->OnNewFrame += EventHandler(this, &ArBackgroundDrawable::OnNewArFrame);
	}

	void ArBackgroundDrawable::Close()
	{
		if (m_arSession) m_arSession->OnNewFrame -= EventHandler(this, &ArBackgroundDrawable::OnNewArFrame);
		m_arSession = nullptr;
		m_nextFrame = nullptr;
		Drawable::Close();
	}

	void ArBackgroundDrawable::OnNewArFrame(const Ptr<AR::ArFrame>& frame)
	{
		m_nextFrame = frame;
	}

	void ArBackgroundDrawable::Tick()
	{
		m_lastFrame = nullptr;
		if (m_nextFrame)
		{
			m_lastFrame = std::move(m_currentFrame);
			m_currentFrame = std::move(m_nextFrame);
			m_nextFrame = nullptr;
			m_texture = m_currentFrame->GetImageTexture();
			m_intrinsicsBuffer.data = &m_currentFrame->GetFrameMetadata().intrinsic;
			m_intrinsicsBuffer.updated = true;
		}
		if (!m_texture) m_texture = &Texture::PLACEHOLDER;
	}
}
