#include <pch.hpp>

#include <renderer/renderer2d.hpp>
#include <scene/components.hpp>
#include <scene/entity.hpp>
#include <scene/scene.hpp>

namespace OpenEngine {
    Entity Scene::CreateEntity(const std::string& name)
    {
        Entity entity = { registry.create(), this };

        auto& tag = entity.AddComponents<TagComponent>();
        tag.tag = name.empty() ? "Entity" : name;

        return entity;
    }
    
    void Scene::DeleteEntity(Entity entity)
    {
        registry.destroy(entity);
    }

    void Scene::OnUpdateRuntime()
    {
        {
            OE_PROFILE_SCOPE("Updating Sripts");

            registry.view<NativeScriptComponent>().each([=, this](auto entity, auto& nsc)
                    {
                        if (!nsc.instance) {
                            nsc.instance = nsc.InstanciateScript();
                            nsc.instance->entity = Entity{ entity, this };
                            nsc.instance->OnCreate();
                        }

                            nsc.instance->OnUpdate();
                    });
        }

        SceneCamera* main_camera = nullptr;
        glm::mat4 main_transform{ 1.0f };
        {
            auto camera_view = registry.view<CameraComponent, TransformComponent>();
            for (auto entity : camera_view) {
                auto [camera_component, transform] = camera_view.get<CameraComponent, TransformComponent>(entity);

                if (camera_component.primary) {
                    main_camera = &camera_component.camera;
                    main_transform = GetTransformFromComp(transform);
                    break;
                }
            }

            if (main_camera) {
                Renderer2D::BeginScene(main_camera->GetProjection(), main_transform);

                auto view = registry.view<TransformComponent, SpriteRendererComponent>();

                for (const auto& entity : view) {
                    auto [transform, sprite] = view.get<TransformComponent, SpriteRendererComponent>(entity);

                    if (sprite.texture)
                        Renderer2D::DrawQuad(GetTransformFromComp(transform),
                                sprite.texture, sprite.color, (int)entity, sprite.tiling_factor);
                    else
                        Renderer2D::DrawQuad(GetTransformFromComp(transform),
                                sprite.color, (int)entity);
                }

                Renderer2D::EndScene();
            }
        }
    }

    void Scene::OnUpdateEditor(EditorCamera& camera)
	{
		Renderer2D::BeginScene(camera);

		auto group = registry.group<TransformComponent>(entt::get<SpriteRendererComponent>);
		for (auto entity : group)
		{
			auto [transform, sprite] = group.get<TransformComponent, SpriteRendererComponent>(entity);

            if (sprite.texture)
                Renderer2D::DrawQuad(GetTransformFromComp(transform),
                        sprite.texture, sprite.color, (int)entity, sprite.tiling_factor);
            else
                Renderer2D::DrawQuad(GetTransformFromComp(transform),
                        sprite.color, (int)entity);
		}

		Renderer2D::EndScene();
	}

    void Scene::OnViewportResize(uint32_t width, uint32_t height)
    {
        viewport_width = width;
        viewport_height = height;

        auto view = registry.view<CameraComponent>();
        for (auto entity : view) {
            auto& camera_comp = view.get<CameraComponent>(entity);
            if (!camera_comp.fixed_aspect_ratio)
                camera_comp.camera.SetViewportSize(width, height);
        }
    }

    Entity Scene::GetPrimaryCamera()
    {
        auto view = registry.view<CameraComponent>();

        for (auto entity : view) {
            const auto& camera = view.get<CameraComponent>(entity);
            if (camera.primary)
                return Entity { entity, this };
        }

        return {};
    }

    template<typename T>
        void Scene::OnComponentAdded(Entity entity, T& component)
        {
            static_assert(false);
        }

    template<>
	void Scene::OnComponentAdded<TransformComponent>(Entity entity, TransformComponent& component)
	{
	}

    template<>
	void Scene::OnComponentAdded<CameraComponent>(Entity entity, CameraComponent& component)
	{
		if (viewport_width > 0 && viewport_height > 0)
			component.camera.SetViewportSize(viewport_width, viewport_height);
	}

    template<>
	void Scene::OnComponentAdded<SpriteRendererComponent>(Entity entity, SpriteRendererComponent& component)
	{
	}

	template<>
	void Scene::OnComponentAdded<TagComponent>(Entity entity, TagComponent& component)
	{
	}

	template<>
	void Scene::OnComponentAdded<NativeScriptComponent>(Entity entity, NativeScriptComponent& component)
	{
	}
}
