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

#include "SceneSerializer.h"

#include <fstream>

#include "Components.h"
#include "Entity.h"
#include "yaml-cpp/yaml.h"

namespace YAML
{
    template<>
    struct convert<glm::vec3>
    {
        static Node encode(const glm::vec3& out)
        {
            Node node;
            node.push_back(out.x);
            node.push_back(out.y);
            node.push_back(out.z);
            node.SetStyle(EmitterStyle::Flow);
            return node;
        }

        static bool decode(const Node& node, glm::vec3& out)
        {
            if (node.size() != 3 && !node.IsSequence())
                return false;
            out.x = node[0].as<float>();
            out.y = node[1].as<float>();
            out.z = node[2].as<float>();
            return true;
        }
    };

    template<>
    struct convert<glm::vec4>
    {
        static Node encode(const glm::vec4& out)
        {
            Node node;
            node.push_back(out.x);
            node.push_back(out.y);
            node.push_back(out.z);
            node.push_back(out.w);
            node.SetStyle(EmitterStyle::Flow);
            return node;
        }

        static bool decode(const Node& node, glm::vec4& out)
        {
            if (node.size() != 4 && !node.IsSequence())
                return false;
            out.x = node[0].as<float>();
            out.y = node[1].as<float>();
            out.z = node[2].as<float>();
            out.w = node[3].as<float>();
            return true;
        }
    };

    Emitter& operator<<(Emitter& out, const glm::vec3& value)
    {
        out << Flow;
        out << BeginSeq << value.x << value.y << value.z << EndSeq;
        return out;
    }

    Emitter& operator<<(Emitter& out, const glm::vec4& value)
    {
        out << Flow;
        out << BeginSeq << value.x << value.y << value.z << value.w << EndSeq;
        return out;
    }
}

namespace Hazel
{



    SceneSerializer::SceneSerializer(const Ref<Scene> scene)
        : m_Scene(scene)
    {
    }

    static void SerializeEntity(YAML::Emitter& out, Entity entity)
    {
        out << YAML::BeginMap; // entity
        out << YAML::Key << "Entity" << YAML::Value << "123479283";  // TODO: entity ID goes here

        if (entity.HasComponent<TagComponent>())
        {
            out << YAML::Key << "TagComponent";

            out << YAML::BeginMap;      // TagComponent

            const auto& tag = entity.GetComponent<TagComponent>().Tag;
            out << YAML::Key << "Tag" << YAML::Value << tag;

            out << YAML::EndMap;        // TagComponent
        }

        if (entity.HasComponent<TransformComponent>())
        {
            out << YAML::Key << "TransformComponent";
            out << YAML::BeginMap;      // TransformComponent

            auto& transform = entity.GetComponent<TransformComponent>();
            out << YAML::Key << "Translation" << YAML::Value << transform.Translation;
            out << YAML::Key << "Rotation" << YAML::Value << transform.Rotation;
            out << YAML::Key << "Scale" << YAML::Value << transform.Scale;

            out << YAML::EndMap;        // TransformComponent
        }

        if (entity.HasComponent<CameraComponent>())
        {
            out << YAML::Key << "CameraComponent";

            out << YAML::BeginMap;      // CameraComponent

            auto& cameraComponent = entity.GetComponent<CameraComponent>();
            auto& camera = cameraComponent.Camera;

            out << YAML::Key << "Camera" << YAML::Value;      // CameraComponent
            out << YAML::BeginMap;      // Camera

            out << YAML::Key << "ProjectionType" << YAML::Value << (int)camera.GetProjectionType();
            out << YAML::Key << "PerspectiveVerticalFOV" << YAML::Value << camera.GetPerspectiveVerticalFOV();
            out << YAML::Key << "PerspectiveNear" << YAML::Value << camera.GetPerspectiveNearCLip();
            out << YAML::Key << "PerspectiveFar" << YAML::Value << camera.GetPerspectiveFarCLip();
            out << YAML::Key << "OrthographicSize" << YAML::Value << camera.GetOrthographicSize();
            out << YAML::Key << "OrthographicNear" << YAML::Value << camera.GetOrthographicNearCLip();
            out << YAML::Key << "OrthographicFar" << YAML::Value << camera.GetOrthographicFarCLip();

            out << YAML::Key << "Primary" << YAML::Value << cameraComponent.Primary;
            out << YAML::Key << "FixedAspectRatio" << YAML::Value << cameraComponent.FixedAspectRatio;

            out << YAML::EndMap;      // Camera
            out << YAML::EndMap;        // CameraComponent
        }

        if (entity.HasComponent<SpriteRendererComponent>())
        {
            out << YAML::Key << "SpriteRendererComponent";

            out << YAML::BeginMap;      // SpriteRendererComponent
            auto& spriteRendererComponent = entity.GetComponent<SpriteRendererComponent>();
            out << YAML::Key << "Color" << YAML::Value << spriteRendererComponent.Color;
            out << YAML::EndMap;        // SpriteRendererComponent
        }

        out << YAML::EndMap; // entity
    }

    void SceneSerializer::Serialize(const std::string& filepath) const
    {
        YAML::Emitter out;
        out << YAML::BeginMap;
        out << YAML::Key << "Scene" << YAML::Value << "Untitled";
        out << YAML::Key << "Entities" << YAML::Value << YAML::BeginSeq;
        m_Scene->m_Registry.view<entt::entity>().each([&](auto entityID)
        {
            const Entity entity = {entityID, m_Scene.get()};
            if (!entity)
                return;

            SerializeEntity(out, entity);
        });

        std::ofstream fout(filepath);
        fout << out.c_str();
    }

    void SceneSerializer::SerializeRuntime(const std::string& filepath)
    {
    }

    bool SceneSerializer::Deserialize(const std::string& filepath)
    {
        const std::ifstream stream(filepath);
        std::stringstream strStream;
        strStream << stream.rdbuf();

        YAML::Node data = YAML::Load(strStream.str());
        if (!data["Scene"])
        {
            HZ_CORE_WARN("cannot find key: Scene");
            return false;
        }


        auto sceneName = data["Scene"].as<std::string>();

        HZ_CORE_TRACE("Deserializing Scene {}", strStream.str());

        auto entities = data["Entities"];
        if (entities)
        {
            for (auto entity : entities)
            {
                uint64_t uuid = entity["Entity"].as<uint64_t>();        // TODO

                std::string name;
                auto TagComponent = entity["TagComponent"];
                if (TagComponent)
                    name = TagComponent["Tag"].as<std::string>();

                HZ_CORE_TRACE("Deserializing Entity: {0}:({1})", name, uuid);

                Entity deserializedEntity = m_Scene->CreateEntity(name);

                auto transformComponent = entity["TransformComponent"];
                if (transformComponent)
                {
                    auto& transform = deserializedEntity.GetComponent<TransformComponent>();
                    transform.Translation = transformComponent["Translation"].as<glm::vec3>();
                    transform.Rotation = transformComponent["Rotation"].as<glm::vec3>();
                    transform.Scale = transformComponent["Scale"].as<glm::vec3>();
                }

                auto cameraComponent = entity["CameraComponent"];
                if (cameraComponent)
                {
                    auto& camera = deserializedEntity.AddComponent<CameraComponent>();

                    auto cameraProps = cameraComponent["Camera"];
                    camera.Camera.SetProjectionType((ScenceCamera::ProjectionType)cameraProps["ProjectionType"].as<int>());

                    camera.Camera.SetPerspectiveVerticalFOV(cameraProps["PerspectiveVerticalFOV"].as<float>());
                    camera.Camera.SetPerspectiveNearClip(cameraProps["PerspectiveNear"].as<float>());
                    camera.Camera.SetPerspectiveFarClip(cameraProps["PerspectiveFar"].as<float>());

                    camera.Camera.SetOrthographicSize(cameraProps["OrthographicSize"].as<float>());
                    camera.Camera.SetOrthographicNearClip(cameraProps["OrthographicNear"].as<float>());
                    camera.Camera.SetOrthographicFarClip(cameraProps["OrthographicFar"].as<float>());

                    camera.Primary = cameraProps["Primary"].as<bool>();
                    camera.FixedAspectRatio = cameraProps["FixedAspectRatio"].as<bool>();
                }

                auto spriteRendererComponent = entity["SpriteRendererComponent"];
                if (spriteRendererComponent)
                {
                    auto& sprite = deserializedEntity.AddComponent<SpriteRendererComponent>();
                    sprite.Color = spriteRendererComponent["Color"].as<glm::vec4>();
                }

            }
        }

        return true;
    }

    bool SceneSerializer::DeserializeRuntime(const std::string& filepath)
    {
        return false;
    }

}
