//
// Created by sfd on 25-5-26.
//

#include "Scene.h"

#include <Hazel/Renderer/Renderer2D.h>

#include "Components.h"

#include "Entity.h"

namespace Hazel
{
    Scene::Scene()
    {
    }

    Scene::~Scene()
    {
    }

    Entity Scene::CreateEntity(const std::string& name)
    {
        Entity entity = {m_Registry.create(), this};
        entity.AddComponent<TransformComponent>();
        entity.AddComponent<TagComponent>(name.empty() ? "Entity" : name);

        return entity;
    }

    void Scene::DestoryEntity(const Entity entity)
    {
        m_Registry.destroy(entity);
    }

    void Scene::OnUpdate(TimeStep& ts)
    {

        // Update scripts
        {
            m_Registry.view<NativeScriptComponent>().each([=](auto entity, auto& nsc)
            {
                // TODO: move to Scene::OnScenePlay
                if (!nsc.Instance)
                {
                    nsc.Instance = nsc.InstantiateScript();
                    nsc.Instance->m_Entity = Entity{entity, this};
                    nsc.Instance->OnCreate();
                }

                nsc.Instance->OnUpdate(ts);

            });
        }

        // Renderer 2D
        Camera* mainCamera = nullptr;
        glm::mat4 cameraTranform;
        {
            for (auto view = m_Registry.view<TransformComponent, CameraComponent>(); const auto entity : view)
            {
                auto&& [transform, camera] = view.get<TransformComponent, CameraComponent>(entity);

                if (camera.Primary)
                {
                    mainCamera = &camera.Camera;
                    cameraTranform = transform.GetTransform();
                    break;
                }
            }
        }


        if (mainCamera != nullptr)
        {

            Renderer2D::BeginScene(mainCamera->GetProjection(), cameraTranform);

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

                Renderer2D::DrawQuad(transform.GetTransform(), sprite.Color);
            }

            Renderer2D::EndScene();
        }
    }

    void Scene::OnViewportResize(uint32_t width, uint32_t height)
    {
        m_ViewportWidth = width;
        m_ViewportHeight = height;

        //Reisze our non-fixedAspectRatio Cameras

        auto view = m_Registry.view<CameraComponent>();
        for (auto entity : view)
        {
            auto& cameraComponent = view.get<CameraComponent>(entity);
            if (!cameraComponent.FixedAspectRatio)
            {
                cameraComponent.Camera.SetViewPortSize(width, height);
            }
        }

    }

    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)
    {
        component.Camera.SetViewPortSize(m_ViewportWidth, m_ViewportHeight);
    }

    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)
    {
    }


    template HAZEL_API void Scene::OnComponentAdded<TransformComponent>(Entity, TransformComponent&);
    template HAZEL_API void Scene::OnComponentAdded<CameraComponent>(Entity, CameraComponent&);
    template HAZEL_API void Scene::OnComponentAdded<TagComponent>(Entity, TagComponent&);
    template HAZEL_API void Scene::OnComponentAdded<SpriteRendererComponent>(Entity, SpriteRendererComponent&);
    template HAZEL_API void Scene::OnComponentAdded<NativeScriptComponent>(Entity, NativeScriptComponent&);
}
