/*
 * 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 "TexturedCubeExampleApp.hpp"
#include "Scene/Scene.hpp"
#include "Scene/Shader/Shader.hpp"
#include "Scene/Geometry.hpp"
#include "Scene/GeometryFactory.hpp"
#include "Scene/Material.hpp"
#include "Scene/Vertex.hpp"
#include "Scene/SimpleDrawable.hpp"
#include "Scene/UI/PerformanceInfo.hpp"
#include "Input/InputManager.hpp"
#include "Host/GraphicsAppManager.hpp"
#include "Math/Math.hpp"
#include "Base/EngineConfiguration.hpp"
#include "Controller/FreeCamCameraController.hpp"
#include "Base/FrameMetadata.hpp"

namespace OpenVulkano
{
	using namespace Scene;
	using namespace Input;
	using namespace Math;

	class TexturedCubeExampleAppImpl final : public TexturedCubeExampleApp
	{
		OpenVulkano::Scene::Scene scene;
		PerspectiveCamera cam;
		OpenVulkano::FreeCamCameraController camController;
		Material mat;
		Shader shader;
		SimpleDrawable drawable;
		Node node;

		OpenVulkano::Scene::UI::SimpleUi m_ui;
		std::shared_ptr<OpenVulkano::Scene::UI::PerformanceInfo> m_perfInfo;

	public:
		void Init() override
		{
			scene.Init();
			cam.Init(70, 16, 9, 0.1f, 100);
			scene.SetCamera(&cam);
			shader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/basic");
			shader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/basicTexture");
			shader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription());
			shader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING);
			static Geometry geo = GeometryFactory::MakeCube();
			mat.texture = &Texture::PLACEHOLDER;
			drawable.Init(&shader, &geo, &mat);
			node.Init();
			scene.GetRoot()->AddChild(&node);
			node.SetUpdateFrequency(UpdateFrequency::Always);
			node.AddDrawable(&drawable);

			GetGraphicsAppManager()->GetRenderer()->SetScene(&scene);

			camController.Init(&cam);
			//camController.SetDefaultKeybindings();
			camController.SetPosition({0, 0, 2});

			m_perfInfo = std::make_shared<OpenVulkano::Scene::UI::PerformanceInfo>();
			m_ui.AddElement(m_perfInfo);
			GetGraphicsAppManager()->GetRenderer()->SetActiveUi(&m_ui);
		}

		float t = 0;
		void Tick() override
		{
			t += CURRENT_FRAME.frameTime * 0.25;

			Math::Matrix4f rotation = Math::Utils::rotate(t, Math::Vector3f_SIMD{1.0f, 0.0f, 0.0f});
			rotation *= Math::Utils::rotate(t, Math::Vector3f_SIMD{0.0f, 1.0f, 0.0f});
			rotation *= Math::Utils::rotate(t, Math::Vector3f_SIMD{0.0f, 0.0f, 1.0f});
			node.SetMatrix(rotation);

			camController.Tick();
		}

		void Close() override
		{}
	};

	IGraphicsApp* TexturedCubeExampleApp::Create()
	{
		return new TexturedCubeExampleAppImpl();
	}

	std::unique_ptr<IGraphicsApp> TexturedCubeExampleApp::CreateUnique()
	{
		return std::make_unique<TexturedCubeExampleAppImpl>();
	}

}