// Copyright (c) Wojciech Figat. All rights reserved.

#include "Engine/Content/Content.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/ScopeExit.h"
#include "Engine/Level/Actor.h"
#include "Engine/Level/Actors/EmptyActor.h"
#include "Engine/Level/Actors/DirectionalLight.h"
#include "Engine/Level/Actors/ExponentialHeightFog.h"
#include "Engine/Level/Actors/AnimatedModel.h"
#include "Engine/Level/Prefabs/Prefab.h"
#include "Engine/Level/Prefabs/PrefabManager.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#include <ThirdParty/catch2/catch.hpp>

TEST_CASE("Prefabs")
{
    SECTION("Virtual Prefab")
    {
        auto* prefab = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefab);
        Content::DeleteAsset(prefab);
    }
    SECTION("Test Repareting in Nested Prefab")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/718

        // Create Prefab B with two children attached to the root
        AssetReference<Prefab> prefabB = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabB);
        SCOPE_EXIT{ Content::DeleteAsset(prefabB); };
        Guid id;
        Guid::Parse("665bb01c49a3370f14a023b5395de261", id);
        prefabB->ChangeID(id);
        auto prefabBInit = prefabB->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"eec6b9644492fbca1a6ab0a7904a557e\","
                                         "\"TypeName\": \"FlaxEngine.EmptyActor\","
                                         "\"Name\": \"Prefab B.Root\""
                                         "},"
                                         "{"
                                         "\"ID\": \"124540354d2d0a1decbb3ebfc279cfe6\","
                                         "\"TypeName\": \"FlaxEngine.EmptyActor\","
                                         "\"ParentID\": \"eec6b9644492fbca1a6ab0a7904a557e\","
                                         "\"Name\": \"Prefab B.Parent\""
                                         "},"
                                         "{"
                                         "\"ID\": \"f701472747356b7c26186db1c4252b53\","
                                         "\"TypeName\": \"FlaxEngine.EmptyActor\","
                                         "\"ParentID\": \"eec6b9644492fbca1a6ab0a7904a557e\","
                                         "\"Name\": \"Prefab B.Child\""
                                         "}"
                                         "]");
        REQUIRE(!prefabBInit);

        // Create Prefab A with nested Prefab B attached to the root
        AssetReference<Prefab> prefabA = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabA);
        SCOPE_EXIT{ Content::DeleteAsset(prefabA); };
        Guid::Parse("02524a044184af56b6c664a0f98bd761", id);
        prefabA->ChangeID(id);
        auto prefabAInit = prefabA->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"5aa124754dcd1cdefed80e828831d45b\","
                                         "\"TypeName\": \"FlaxEngine.EmptyActor\","
                                         "\"Name\": \"Prefab A.Root\""
                                         "},"
                                         "{"
                                         "\"ID\": \"8e51f1094f430733333f8280e78dfcc3\","
                                         "\"PrefabID\": \"665bb01c49a3370f14a023b5395de261\","
                                         "\"PrefabObjectID\": \"eec6b9644492fbca1a6ab0a7904a557e\","
                                         "\"ParentID\": \"5aa124754dcd1cdefed80e828831d45b\""
                                         "},"
                                         "{"
                                         "\"ID\": \"8e1f2bae4aaedeab8725908ce1aec325\","
                                         "\"PrefabID\": \"665bb01c49a3370f14a023b5395de261\","
                                         "\"PrefabObjectID\": \"124540354d2d0a1decbb3ebfc279cfe6\","
                                         "\"ParentID\": \"8e51f1094f430733333f8280e78dfcc3\""
                                         "},"
                                         "{"
                                         "\"ID\": \"4e4f3a1847cf96fe2e8919848b7eca79\","
                                         "\"PrefabID\": \"665bb01c49a3370f14a023b5395de261\","
                                         "\"PrefabObjectID\": \"f701472747356b7c26186db1c4252b53\","
                                         "\"ParentID\": \"8e51f1094f430733333f8280e78dfcc3\""
                                         "}"
                                         "]");
        REQUIRE(!prefabAInit);

        // Spawn test instances of both prefabs
        ScriptingObjectReference<Actor> instanceB = PrefabManager::SpawnPrefab(prefabB);
        ScriptingObjectReference<Actor> instanceA = PrefabManager::SpawnPrefab(prefabA);

        // Verify initial scenario
        REQUIRE(instanceA);
        REQUIRE(instanceB);
        REQUIRE(instanceA->GetName() == TEXT("Prefab A.Root"));
        REQUIRE(instanceA->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[0]->GetName() == TEXT("Prefab B.Root"));
        REQUIRE(instanceA->Children[0]->GetChildrenCount() == 2);
        REQUIRE(instanceA->Children[0]->Children[0]->GetName() == TEXT("Prefab B.Parent"));
        REQUIRE(instanceA->Children[0]->Children[0]->GetChildrenCount() == 0);
        REQUIRE(instanceA->Children[0]->Children[1]->GetName() == TEXT("Prefab B.Child"));
        REQUIRE(instanceA->Children[0]->Children[1]->GetChildrenCount() == 0);

        // Modify Prefab B instance to reparent the actor
        instanceB->FindActor(TEXT("Prefab B.Child"))->SetParent(instanceB->FindActor(TEXT("Prefab B.Parent")));

        // Apply nested prefab changes
        auto applyResult = PrefabManager::ApplyAll(instanceB);
        REQUIRE(!applyResult);

        // Verify if instance of Prefab B nested in instance of Prefab A was properly updated
        REQUIRE(instanceA);
        REQUIRE(instanceB);
        REQUIRE(instanceA->GetName() == TEXT("Prefab A.Root"));
        REQUIRE(instanceA->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[0]->GetName() == TEXT("Prefab B.Root"));
        REQUIRE(instanceA->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[0]->Children[0]->GetName() == TEXT("Prefab B.Parent"));
        REQUIRE(instanceA->Children[0]->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[0]->Children[0]->Children[0]->GetName() == TEXT("Prefab B.Child"));
        REQUIRE(instanceA->Children[0]->Children[0]->Children[0]->GetChildrenCount() == 0);

        // Cleanup
        instanceA->DeleteObject();
        instanceB->DeleteObject();
    }
    SECTION("Test Adding Object in Nested Prefab")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/690

        // Create Prefab B with just root object
        AssetReference<Prefab> prefabB = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabB);
        SCOPE_EXIT{ Content::DeleteAsset(prefabB); };
        Guid id;
        Guid::Parse("25dbe4b0416be0777a6ce59e8788b10f", id);
        prefabB->ChangeID(id);
        auto prefabBInit = prefabB->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
                                         "\"TypeName\": \"FlaxEngine.EmptyActor\","
                                         "\"Name\": \"Prefab B.Root\""
                                         "}"
                                         "]");
        REQUIRE(!prefabBInit);

        // Create Prefab A with two nested Prefab B attached to the root
        AssetReference<Prefab> prefabA = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabA);
        SCOPE_EXIT{ Content::DeleteAsset(prefabA); };
        Guid::Parse("4cb744714f746e31855f41815612d14b", id);
        prefabA->ChangeID(id);
        auto prefabAInit = prefabA->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"244274a04cc60d56a2f024bfeef5772d\","
                                         "\"TypeName\": \"FlaxEngine.EmptyActor\","
                                         "\"Name\": \"Prefab A.Root\""
                                         "},"
                                         "{"
                                         "\"ID\": \"1e51f1094f430733333f8280e78dfcc3\","
                                         "\"PrefabID\": \"25dbe4b0416be0777a6ce59e8788b10f\","
                                         "\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
                                         "\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\""
                                         "},"
                                         "{"
                                         "\"ID\": \"2e1f2bae4aaedeab8725908ce1aec325\","
                                         "\"PrefabID\": \"25dbe4b0416be0777a6ce59e8788b10f\","
                                         "\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
                                         "\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\""
                                         "}"
                                         "]");
        REQUIRE(!prefabAInit);

        // Spawn test instances of both prefabs
        ScriptingObjectReference<Actor> instanceB = PrefabManager::SpawnPrefab(prefabB);
        ScriptingObjectReference<Actor> instanceA = PrefabManager::SpawnPrefab(prefabA);

        // Verify initial scenario
        REQUIRE(instanceA);
        REQUIRE(instanceB);
        REQUIRE(instanceA->GetName() == TEXT("Prefab A.Root"));
        REQUIRE(instanceA->GetChildrenCount() == 2);
        REQUIRE(instanceA->Children[0]->GetName() == TEXT("Prefab B.Root"));
        REQUIRE(instanceA->Children[0]->GetChildrenCount() == 0);
        REQUIRE(instanceA->Children[1]->GetName() == TEXT("Prefab B.Root"));
        REQUIRE(instanceA->Children[1]->GetChildrenCount() == 0);

        // Modify Prefab B instance to add a new actor as a child so at appears in both nested instances in Prefab A instance
        Guid newChildId;
        Guid::Parse(TEXT("123456a04cc60d56a2f024bfeef57723"), newChildId);
        auto newChild = EmptyActor::Spawn(ScriptingObject::SpawnParams(newChildId, EmptyActor::TypeInitializer));
        newChild->SetName(String(TEXT("Prefab B.Child")));
        newChild->SetParent(instanceB);

        // Apply nested prefab changes
        auto applyResult = PrefabManager::ApplyAll(instanceB);
        REQUIRE(!applyResult);

        // Verify if instance of Prefab B nested instances in instance of Prefab A was properly updated
        REQUIRE(instanceA);
        REQUIRE(instanceB);
        REQUIRE(instanceA->GetName() == TEXT("Prefab A.Root"));
        REQUIRE(instanceA->GetChildrenCount() == 2);
        REQUIRE(instanceA->Children[0]->GetName() == TEXT("Prefab B.Root"));
        REQUIRE(instanceA->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[0]->Children[0]->GetName() == TEXT("Prefab B.Child"));
        REQUIRE(instanceA->Children[0]->Children[0]->GetChildrenCount() == 0);
        REQUIRE(instanceA->Children[1]->GetName() == TEXT("Prefab B.Root"));
        REQUIRE(instanceA->Children[1]->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[1]->Children[0]->GetName() == TEXT("Prefab B.Child"));
        REQUIRE(instanceA->Children[1]->Children[0]->GetChildrenCount() == 0);

        // Add another child 
        Guid::Parse(TEXT("678906a04cc60d56a2f024bfeef57723"), newChildId);
        newChild = EmptyActor::Spawn(ScriptingObject::SpawnParams(newChildId, EmptyActor::TypeInitializer));
        newChild->SetName(String(TEXT("Prefab B.Child 2")));
        newChild->SetParent(instanceB);

        // Apply nested prefab changes
        applyResult = PrefabManager::ApplyAll(instanceB);
        REQUIRE(!applyResult);

        // Reparent another child into the first child
        newChild->SetParent(instanceB->Children[0]);

        // Apply nested prefab changes
        applyResult = PrefabManager::ApplyAll(instanceB);
        REQUIRE(!applyResult);

        // Verify if instance of Prefab B nested instances in instance of Prefab A was properly updated
        REQUIRE(instanceA);
        REQUIRE(instanceB);
        REQUIRE(instanceA->GetChildrenCount() == 2);
        REQUIRE(instanceA->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[0]->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[0]->Children[0]->Children[0]->GetChildrenCount() == 0);
        REQUIRE(instanceA->Children[1]->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[1]->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceA->Children[1]->Children[0]->Children[0]->GetChildrenCount() == 0);

        // Cleanup
        instanceA->DeleteObject();
        instanceB->DeleteObject();
    }
    SECTION("Test Syncing Changes In Nested Prefab Instance")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/1015

        // Create TestActor prefab with just root object
        AssetReference<Prefab> testActorPrefab = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(testActorPrefab);
        SCOPE_EXIT{ Content::DeleteAsset(testActorPrefab); };
        Guid id;
        Guid::Parse("7691e981482f2a486e10cfae149e07d3", id);
        testActorPrefab->ChangeID(id);
        auto testActorPrefabInit = testActorPrefab->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"5d73990240497afc0c6d36814cc6ebbe\","
                                         "\"TypeName\": \"FlaxEngine.EmptyActor\","
                                         "\"Name\": \"TestActor\""
                                         "}"
                                         "]");
        REQUIRE(!testActorPrefabInit);

        // Create NestedActor prefab that inherits from TestActor prefab
        AssetReference<Prefab> nestedActorPrefab = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(nestedActorPrefab);
        SCOPE_EXIT{ Content::DeleteAsset(nestedActorPrefab); };
        Guid::Parse("1d521df4465ad849e274748c6d14b703", id);
        nestedActorPrefab->ChangeID(id);
        auto nestedActorPrefabInit = nestedActorPrefab->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"75c1587b4caeea27241ba7af00dafd45\","
                                         "\"PrefabID\": \"7691e981482f2a486e10cfae149e07d3\","
                                         "\"PrefabObjectID\": \"5d73990240497afc0c6d36814cc6ebbe\","
                                         "\"Name\": \"NestedActor\""
                                         "}"
                                         "]");
        REQUIRE(!nestedActorPrefabInit);

        // Spawn test instances of both prefabs
        ScriptingObjectReference<Actor> testActor = PrefabManager::SpawnPrefab(testActorPrefab);
        ScriptingObjectReference<Actor> nestedActor = PrefabManager::SpawnPrefab(nestedActorPrefab);

        // Verify initial scenario
        REQUIRE(testActor);
        CHECK(testActor->GetName() == TEXT("TestActor"));
        CHECK(testActor->GetStaticFlags() == StaticFlags::FullyStatic);
        REQUIRE(nestedActor);
        CHECK(nestedActor->GetName() == TEXT("NestedActor"));
        CHECK(nestedActor->GetStaticFlags() == StaticFlags::FullyStatic);

        // Modify TestActor instance to have Static Flags property changed
        testActor->SetStaticFlags(StaticFlags::None);

        // Apply prefab changes
        auto applyResult = PrefabManager::ApplyAll(testActor);
        REQUIRE(!applyResult);

        // Verify if instances were properly updated
        REQUIRE(testActor);
        CHECK(testActor->GetName() == TEXT("TestActor"));
        CHECK(testActor->GetStaticFlags() == StaticFlags::None);
        REQUIRE(nestedActor);
        CHECK(nestedActor->GetName() == TEXT("NestedActor"));
        CHECK(nestedActor->GetStaticFlags() == StaticFlags::None);

        // Cleanup
        nestedActor->DeleteObject();
        testActor->DeleteObject();

        // Spawn another test instances instances of both prefabs
        testActor = PrefabManager::SpawnPrefab(testActorPrefab);
        nestedActor = PrefabManager::SpawnPrefab(nestedActorPrefab);

        // Verify if instances were properly updated
        REQUIRE(testActor);
        CHECK(testActor->GetName() == TEXT("TestActor"));
        CHECK(testActor->GetStaticFlags() == StaticFlags::None);
        REQUIRE(nestedActor);
        CHECK(nestedActor->GetName() == TEXT("NestedActor"));
        CHECK(nestedActor->GetStaticFlags() == StaticFlags::None);

        // Cleanup
        nestedActor->DeleteObject();
        testActor->DeleteObject();
    }
    SECTION("Test Loading Nested Prefab After Changing Root")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/1138

        // Create base prefab with 3 objects
        AssetReference<Prefab> prefabBase = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabBase);
        SCOPE_EXIT{ Content::DeleteAsset(prefabBase); };
        Guid id;
        Guid::Parse("2b3334524c696dcfa93cabacd2a4f404", id);
        prefabBase->ChangeID(id);
        auto prefabBaseInit = prefabBase->Init(Prefab::TypeName,
                                               "["
                                               "{"
                                               "\"ID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\","
                                               "\"TypeName\": \"FlaxEngine.DirectionalLight\","
                                               "\"Name\": \"1\""
                                               "},"
                                               "{"
                                               "\"ID\": \"589bcfaa4bd1a53435129480e5bbdb3b\","
                                               "\"TypeName\": \"FlaxEngine.Camera\","
                                               "\"ParentID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\","
                                               "\"Name\": \"2\""
                                               "},"
                                               "{"
                                               "\"ID\": \"9e81c24342e61af456411ea34593841d\","
                                               "\"TypeName\": \"FlaxEngine.UICanvas\","
                                               "\"ParentID\": \"589bcfaa4bd1a53435129480e5bbdb3b\","
                                               "\"Name\": \"3\""
                                               "}"
                                               "]");
        REQUIRE(!prefabBaseInit);

        // Create nested prefab but with 'old' state where root object is different
        AssetReference<Prefab> prefabNested = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabNested);
        SCOPE_EXIT{ Content::DeleteAsset(prefabNested); };
        Guid::Parse("a71447e947cbd2deea018a8377636ce6", id);
        prefabNested->ChangeID(id);
        auto prefabNestedInit = prefabNested->Init(Prefab::TypeName,
                                                   "["
                                                   "{"
                                                   "\"ID\": \"597ab8ea43a5c58b8d06f58f9364d261\","
                                                   "\"PrefabID\": \"2b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"589bcfaa4bd1a53435129480e5bbdb3b\""
                                                   "},"
                                                   "{"
                                                   "\"ID\": \"1a6228d84897ff3b2f444ea263c3657e\","
                                                   "\"PrefabID\": \"2b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\","
                                                   "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\""
                                                   "},"
                                                   "{"
                                                   "\"ID\": \"f8fbee1349f749396ab6c2ad34f3afec\","
                                                   "\"PrefabID\": \"2b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"9e81c24342e61af456411ea34593841d\","
                                                   "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\""
                                                   "}"
                                                   "]");
        REQUIRE(!prefabNestedInit);

        // Spawn test instances of both prefabs
        ScriptingObjectReference<Actor> instanceBase = PrefabManager::SpawnPrefab(prefabBase);
        ScriptingObjectReference<Actor> instanceNested = PrefabManager::SpawnPrefab(prefabNested);

        // Verify scenario
        REQUIRE(instanceBase);
        REQUIRE(instanceBase->GetName() == TEXT("1"));
        REQUIRE(instanceBase->GetChildrenCount() == 1);
        REQUIRE(instanceBase->Children[0]->GetName() == TEXT("2"));
        REQUIRE(instanceBase->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceBase->Children[0]->Children[0]->GetName() == TEXT("3"));
        REQUIRE(instanceNested);
        REQUIRE(instanceNested->GetName() == TEXT("1"));
        REQUIRE(instanceNested->GetChildrenCount() == 1);
        REQUIRE(instanceNested->Children[0]->GetName() == TEXT("2"));
        REQUIRE(instanceNested->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceNested->Children[0]->Children[0]->GetName() == TEXT("3"));

        // Cleanup
        instanceNested->DeleteObject();
        instanceBase->DeleteObject();
    }
    SECTION("Test Loading Nested Prefab After Changing and Deleting Root")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/2050

        // Create base prefab with 1 object
        AssetReference<Prefab> prefabBase = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabBase);
        SCOPE_EXIT{ Content::DeleteAsset(prefabBase); };
        Guid id;
        Guid::Parse("3b3334524c696dcfa93cabacd2a4f404", id);
        prefabBase->ChangeID(id);
        auto prefabBaseInit = prefabBase->Init(Prefab::TypeName,
                                               "["
                                               "{"
                                               "\"ID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\","
                                               "\"TypeName\": \"FlaxEngine.DirectionalLight\","
                                               "\"Name\": \"New Root\""
                                               "},"
                                               "{"
                                               "\"ID\": \"f8fbee1349f749396ab6c2ad34f3afec\","
                                               "\"TypeName\": \"FlaxEngine.Camera\","
                                               "\"Name\": \"Child 1\","
                                               "\"ParentID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\""
                                               "},"
                                               "{"
                                               "\"ID\": \"5632561847cf96fe2e8919848b7eca79\","
                                               "\"TypeName\": \"FlaxEngine.EmptyActor\","
                                               "\"Name\": \"Child 1.Child\","
                                               "\"ParentID\": \"f8fbee1349f749396ab6c2ad34f3afec\""
                                               "},"
                                               "{"
                                               "\"ID\": \"4e4f3a1847cf96fe2e8919848b7eca79\","
                                               "\"TypeName\": \"FlaxEngine.UICanvas\","
                                               "\"Name\": \"Child 2\","
                                               "\"ParentID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\""
                                               "}"
                                               "]");
        REQUIRE(!prefabBaseInit);

        // Create nested prefab but with 'old' state where root object is different
        AssetReference<Prefab> prefabNested1 = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabNested1);
        SCOPE_EXIT{ Content::DeleteAsset(prefabNested1); };
        Guid::Parse("671447e947cbd2deea018a8377636ce6", id);
        prefabNested1->ChangeID(id);
        auto prefabNestedInit1 = prefabNested1->Init(Prefab::TypeName,
                                                   "["
                                                   "{"
                                                   "\"ID\": \"597ab8ea43a5c58b8d06f58f9364d261\","
                                                   "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"589bcfaa4bd1a53435129480e5bbdb3b\","
                                                   "\"Name\": \"Old Root\""
                                                   "},"
                                                   "{"
                                                   "\"ID\": \"1a6228d84897ff3b2f444ea263c3657e\","
                                                   "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"f8fbee1349f749396ab6c2ad34f3afec\","
                                                   "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\""
                                                   "},"
                                                   "{"
                                                   "\"ID\": \"1212124f4d913e58eb35ab8b0b7e2eef\","
                                                   "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\","
                                                   "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\","
                                                   "\"Name\": \"New Root\""
                                                   "},"
                                                   "{"
                                                   "\"ID\": \"468028d84897ff3b2f444ea263c3657e\","
                                                   "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"2468902349f749396ab6c2ad34f3afec\","
                                                   "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\","
                                                   "\"Name\": \"Old Child\""
                                                   "}"
                                                   "]");
        REQUIRE(!prefabNestedInit1);

        // Create nested prefab but with 'old' state where root object is different and doesn't exist anymore
        AssetReference<Prefab> prefabNested2 = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabNested2);
        SCOPE_EXIT{ Content::DeleteAsset(prefabNested2); };
        Guid::Parse("b71447e947cbd2deea018a8377636ce6", id);
        prefabNested2->ChangeID(id);
        auto prefabNestedInit2 = prefabNested2->Init(Prefab::TypeName,
                                                   "["
                                                   "{"
                                                   "\"ID\": \"597ab8ea43a5c58b8d06f58f9364d261\","
                                                   "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"589bcfaa4bd1a53435129480e5bbdb3b\","
                                                   "\"Name\": \"Old Root\""
                                                   "},"
                                                   "{"
                                                   "\"ID\": \"1a6228d84897ff3b2f444ea263c3657e\","
                                                   "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"f8fbee1349f749396ab6c2ad34f3afec\","
                                                   "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\""
                                                   "},"
                                                   "{"
                                                   "\"ID\": \"468028d84897ff3b2f444ea263c3657e\","
                                                   "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\","
                                                   "\"PrefabObjectID\": \"2468902349f749396ab6c2ad34f3afec\","
                                                   "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\","
                                                   "\"Name\": \"Old Child\""
                                                   "}"
                                                   "]");
        REQUIRE(!prefabNestedInit2);

        // Spawn test instances of both prefabs
        ScriptingObjectReference<Actor> instanceBase = PrefabManager::SpawnPrefab(prefabBase);
        ScriptingObjectReference<Actor> instanceNested1 = PrefabManager::SpawnPrefab(prefabNested1);
        ScriptingObjectReference<Actor> instanceNested2 = PrefabManager::SpawnPrefab(prefabNested2);

        // Verify scenario
        REQUIRE(instanceBase);
        REQUIRE(instanceBase->GetName() == TEXT("New Root"));
        REQUIRE(instanceBase->GetChildrenCount() == 2);
        REQUIRE(instanceBase->Children[0]->GetName() == TEXT("Child 1"));
        REQUIRE(instanceBase->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceBase->Children[1]->GetName() == TEXT("Child 2"));
        REQUIRE(instanceBase->Children[1]->GetChildrenCount() == 0);
        REQUIRE(instanceBase->Children[0]->Children[0]->GetName() == TEXT("Child 1.Child"));
        REQUIRE(instanceBase->Children[0]->Children[0]->GetChildrenCount() == 0);
        REQUIRE(instanceNested1);
        REQUIRE(instanceNested1->GetName() == TEXT("New Root"));
        REQUIRE(instanceNested1->GetChildrenCount() == 2);
        REQUIRE(instanceNested1->Children[0]->GetName() == TEXT("Child 1"));
        REQUIRE(instanceNested1->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceNested1->Children[1]->GetName() == TEXT("Child 2"));
        REQUIRE(instanceNested1->Children[1]->GetChildrenCount() == 0);
        REQUIRE(instanceNested1->Children[0]->Children[0]->GetName() == TEXT("Child 1.Child"));
        REQUIRE(instanceNested1->Children[0]->Children[0]->GetChildrenCount() == 0);
        REQUIRE(instanceNested2);
        REQUIRE(instanceNested2->GetName() == TEXT("New Root"));
        REQUIRE(instanceNested2->GetChildrenCount() == 2);
        REQUIRE(instanceNested2->Children[0]->GetName() == TEXT("Child 1"));
        REQUIRE(instanceNested2->Children[0]->GetChildrenCount() == 1);
        REQUIRE(instanceNested2->Children[1]->GetName() == TEXT("Child 2"));
        REQUIRE(instanceNested2->Children[1]->GetChildrenCount() == 0);
        REQUIRE(instanceNested2->Children[0]->Children[0]->GetName() == TEXT("Child 1.Child"));
        REQUIRE(instanceNested2->Children[0]->Children[0]->GetChildrenCount() == 0);

        // Cleanup
        instanceNested2->DeleteObject();
        instanceNested1->DeleteObject();
        instanceBase->DeleteObject();
    }
    SECTION("Test Applying Prefab Change To Object References")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/3136

        // Create Prefab
        AssetReference<Prefab> prefab = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefab);
        SCOPE_EXIT{ Content::DeleteAsset(prefab); };
        Guid id;
        Guid::Parse("690e55514cd6fdc2a269429a2bf84133", id);
        prefab->ChangeID(id);
        auto prefabInit = prefab->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"fc3f88cf413c2e668039a0bb7429900d\","
                                         "\"TypeName\": \"FlaxEngine.ExponentialHeightFog\","
                                         "\"Name\": \"Fog\","
                                         "\"DirectionalInscatteringLight\": \"44873cc44e950c754f0c7bb59dd432d6\""
                                         "},"
                                         "{"
                                         "\"ID\": \"44873cc44e950c754f0c7bb59dd432d6\","
                                         "\"TypeName\": \"FlaxEngine.DirectionalLight\","
                                         "\"ParentID\": \"fc3f88cf413c2e668039a0bb7429900d\","
                                         "\"Name\": \"Sun 1\""
                                         "},"
                                         "{"
                                         "\"ID\": \"583f91604b622e3b7aa698b51c9966d6\","
                                         "\"TypeName\": \"FlaxEngine.DirectionalLight\","
                                         "\"ParentID\": \"fc3f88cf413c2e668039a0bb7429900d\","
                                         "\"Name\": \"Sun 2\""
                                         "}"
                                         "]");
        REQUIRE(!prefabInit);
        
        // Spawn test instances
        ScriptingObjectReference<Actor> instanceA = PrefabManager::SpawnPrefab(prefab);
        ScriptingObjectReference<Actor> instanceB = PrefabManager::SpawnPrefab(prefab);

        // Swap reference from Sun 1 to Sun 2 on a Fog
        REQUIRE(instanceA);
        REQUIRE(instanceA->Children.Count() == 2);
        CHECK(instanceA.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA->Children[0]);
        instanceA.As<ExponentialHeightFog>()->DirectionalInscatteringLight = (DirectionalLight*)instanceA->Children[1];

        // Apply change on instance A and verify it's applied on instance B
        bool applyResult = PrefabManager::ApplyAll(instanceA);
        REQUIRE(!applyResult);
        REQUIRE(instanceB);
        REQUIRE(instanceB->Children.Count() == 2);
        CHECK(instanceB.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceB->Children[1]);

        // Cleanup
        instanceA->DeleteObject();
        instanceB->DeleteObject();
    }
    SECTION("Test Applying Prefab With Missing Nested Prefab")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/3244

        // Create Prefab B with just root object
        AssetReference<Prefab> prefabB = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabB);
        Guid id;
        Guid::Parse("25dbe4b0416be0777a6ce59e8788b10f", id);
        prefabB->ChangeID(id);
        auto prefabBInit = prefabB->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
                                         "\"TypeName\": \"FlaxEngine.ExponentialHeightFog\","
                                         "\"Name\": \"Prefab B.Root\""
                                         "}"
                                         "]");
        REQUIRE(!prefabBInit);

        // Create Prefab A with nested Prefab B attached to the root
        AssetReference<Prefab> prefabA = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabA);
        SCOPE_EXIT{ Content::DeleteAsset(prefabA); };
        Guid::Parse("4cb744714f746e31855f41815612d14b", id);
        prefabA->ChangeID(id);
        auto prefabAInit = prefabA->Init(Prefab::TypeName,
                                         "["
                                         "{"
                                         "\"ID\": \"244274a04cc60d56a2f024bfeef5772d\","
                                         "\"TypeName\": \"FlaxEngine.SpotLight\","
                                         "\"Name\": \"Prefab A.Root\""
                                         "},"
                                         "{"
                                         "\"ID\": \"1e51f1094f430733333f8280e78dfcc3\","
                                         "\"PrefabID\": \"25dbe4b0416be0777a6ce59e8788b10f\","
                                         "\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
                                         "\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\""
                                         "}"
                                         "]");
        REQUIRE(!prefabAInit);

        // Spawn test instances of both prefabs
        ScriptingObjectReference<Actor> instanceA = PrefabManager::SpawnPrefab(prefabA);
        ScriptingObjectReference<Actor> instanceB = PrefabManager::SpawnPrefab(prefabB);

        // Delete nested prefab
        Content::DeleteAsset(prefabB);
        
        // Apply instance A and verify it's fine even tho prefab B doesn't exist anymore
        bool applyResult = PrefabManager::ApplyAll(instanceA);
        REQUIRE(!applyResult);

        // Check state of objects
        REQUIRE(instanceA);
        REQUIRE(instanceA->Children.Count() == 1);
        REQUIRE(instanceA->Children[0] != nullptr);
        REQUIRE(instanceA->Children[0]->Is<ExponentialHeightFog>());
        REQUIRE(instanceB);
        REQUIRE(instanceB->Is<ExponentialHeightFog>());

        // Verify if prefab has new data to properly spawn another prefab
        ScriptingObjectReference<Actor> instanceC = PrefabManager::SpawnPrefab(prefabA);
        REQUIRE(instanceC);
        REQUIRE(instanceC->Children.Count() == 1);
        REQUIRE(instanceC->Children[0] != nullptr);
        REQUIRE(instanceC->Children[0]->Is<ExponentialHeightFog>());

        // Cleanup
        instanceA->DeleteObject();
        instanceB->DeleteObject();
        instanceC->DeleteObject();
    }
    SECTION("Test Loading Nested Prefab With Multiple Instances of Nested Prefab")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/3255

        // Create Prefab C with basic cross-object references
        AssetReference<Prefab> prefabC = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabC);
        Guid id;
        Guid::Parse("cccbe4b0416be0777a6ce59e8788b10f", id);
        prefabC->ChangeID(id);
        auto prefabCInit = prefabC->Init(Prefab::TypeName,
            "["
            "{"
            "\"ID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
            "\"TypeName\": \"FlaxEngine.ExponentialHeightFog\","
            "\"Name\": \"Prefab C.Root\","
            "\"DirectionalInscatteringLight\": \"bbb6b9644492fbca1a6ab0a7904a557e\""
            "},"
            "{"
            "\"ID\": \"bbb6b9644492fbca1a6ab0a7904a557e\","
            "\"TypeName\": \"FlaxEngine.DirectionalLight\","
            "\"ParentID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
            "\"Name\": \"Prefab C.Light\""
            "}"
            "]");
        REQUIRE(!prefabCInit);

        // Create Prefab B with two nested Prefab C attached to the root
        AssetReference<Prefab> prefabB = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabB);
        SCOPE_EXIT{ Content::DeleteAsset(prefabB); };
        Guid::Parse("bbb744714f746e31855f41815612d14b", id);
        prefabB->ChangeID(id);
        auto prefabBInit = prefabB->Init(Prefab::TypeName,
            "["
            "{"
            "\"ID\": \"244274a04cc60d56a2f024bfeef5772d\","
            "\"TypeName\": \"FlaxEngine.SpotLight\","
            "\"Name\": \"Prefab B.Root\""
            "},"
            "{"
            "\"ID\": \"1111f1094f430733333f8280e78dfcc3\","
            "\"PrefabID\": \"cccbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
            "\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\""
            "},"
            "{"
            "\"ID\": \"2221f1094f430733333f8280e78dfcc3\","
            "\"PrefabID\": \"cccbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"bbb6b9644492fbca1a6ab0a7904a557e\","
            "\"ParentID\": \"1111f1094f430733333f8280e78dfcc3\""
            "},"
            "{"
            "\"ID\": \"3331f1094f430733333f8280e78dfcc3\","
            "\"PrefabID\": \"cccbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
            "\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\""
            "},"
            "{"
            "\"ID\": \"4441f1094f430733333f8280e78dfcc3\","
            "\"PrefabID\": \"cccbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"bbb6b9644492fbca1a6ab0a7904a557e\","
            "\"ParentID\": \"3331f1094f430733333f8280e78dfcc3\""
            "}"
            "]");
        REQUIRE(!prefabBInit);

        // Create Prefab A as variant of Prefab B (no local changes, just object remapped)
        AssetReference<Prefab> prefabA = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabA);
        SCOPE_EXIT{ Content::DeleteAsset(prefabA); };
        Guid::Parse("aaa744714f746e31855f41815612d14b", id);
        prefabA->ChangeID(id);
        auto prefabAInit = prefabA->Init(Prefab::TypeName,
            "["
            "{"
            "\"ID\": \"123274a04cc60d56a2f024bfeef5772d\","
            "\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
            "\"PrefabObjectID\": \"244274a04cc60d56a2f024bfeef5772d\","
            "\"Name\": \"Prefab A.Root\""
            "},"
            "{"
            "\"ID\": \"1211f1094f430733333f8280e78dfcc3\","
            "\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
            "\"PrefabObjectID\": \"1111f1094f430733333f8280e78dfcc3\","
            "\"ParentID\": \"123274a04cc60d56a2f024bfeef5772d\""
            "},"
            "{"
            "\"ID\": \"4221f1094f430733333f8280e78dfcc3\","
            "\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
            "\"PrefabObjectID\": \"2221f1094f430733333f8280e78dfcc3\","
            "\"ParentID\": \"1211f1094f430733333f8280e78dfcc3\""
            "},"
            "{"
            "\"ID\": \"3131f1094f430733333f8280e78dfcc3\","
            "\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
            "\"PrefabObjectID\": \"3331f1094f430733333f8280e78dfcc3\","
            "\"ParentID\": \"123274a04cc60d56a2f024bfeef5772d\""
            "},"
            "{"
            "\"ID\": \"5441f1094f430733333f8280e78dfcc3\","
            "\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
            "\"PrefabObjectID\": \"4441f1094f430733333f8280e78dfcc3\","
            "\"ParentID\": \"3131f1094f430733333f8280e78dfcc3\""
            "}"
            "]");
        REQUIRE(!prefabAInit);

        // Spawn test instances of both prefabs
        ScriptingObjectReference<Actor> instanceA = PrefabManager::SpawnPrefab(prefabA);
        ScriptingObjectReference<Actor> instanceB = PrefabManager::SpawnPrefab(prefabB);
        ScriptingObjectReference<Actor> instanceC = PrefabManager::SpawnPrefab(prefabC);

        // Check state of objects
        REQUIRE(instanceC);
        REQUIRE(instanceC->Is<ExponentialHeightFog>());
        REQUIRE(instanceC->Children.Count() == 1);
        CHECK(instanceC.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceC->Children[0]);
        REQUIRE(instanceB);
        REQUIRE(instanceB->Children.Count() == 2);
        ScriptingObjectReference<Actor> instanceB1 = instanceB->Children[0];
        ScriptingObjectReference<Actor> instanceB2 = instanceB->Children[1];
        REQUIRE(instanceB1->Is<ExponentialHeightFog>());
        REQUIRE(instanceB1->Children.Count() == 1);
        CHECK(instanceB1.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceB1->Children[0]);
        REQUIRE(instanceB2->Is<ExponentialHeightFog>());
        REQUIRE(instanceB2->Children.Count() == 1);
        CHECK(instanceB2.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceB2->Children[0]);
        REQUIRE(instanceA);
        REQUIRE(instanceA->Children.Count() == 2);
        ScriptingObjectReference<Actor> instanceA1 = instanceA->Children[0];
        ScriptingObjectReference<Actor> instanceA2 = instanceA->Children[1];
        REQUIRE(instanceA1->Is<ExponentialHeightFog>());
        REQUIRE(instanceA1->Children.Count() == 1);
        CHECK(instanceA1.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA1->Children[0]);
        REQUIRE(instanceA2->Is<ExponentialHeightFog>());
        REQUIRE(instanceA1->Children.Count() == 1);
        CHECK(instanceA2.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA2->Children[0]);

        // Add instance of Prefab C to Prefab B
        instanceC->SetName(StringView(TEXT("New")));
        instanceC->SetParent(instanceB);
        bool applyResult = PrefabManager::ApplyAll(instanceB);
        REQUIRE(!applyResult);

        // Check if Prefab A reflects that change
        REQUIRE(instanceA);
        REQUIRE(instanceA->Children.Count() == 3);
        instanceA1 = instanceA->Children[0];
        instanceA2 = instanceA->Children[1];
        ScriptingObjectReference<Actor> instanceA3 = instanceA->Children[2];
        REQUIRE(instanceA1->Is<ExponentialHeightFog>());
        REQUIRE(instanceA1->Children.Count() == 1);
        CHECK(instanceA1.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA1->Children[0]);
        REQUIRE(instanceA2->Is<ExponentialHeightFog>());
        REQUIRE(instanceA2->Children.Count() == 1);
        CHECK(instanceA2.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA2->Children[0]);
        REQUIRE(instanceA3->Is<ExponentialHeightFog>());
        REQUIRE(instanceA3->Children.Count() == 1);
        CHECK(instanceA3.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA3->Children[0]);

        // Cleanup
        instanceA->DeleteObject();
        instanceB->DeleteObject();
        instanceC->DeleteObject();
    }
    SECTION("Test Changing Prefab Root")
    {
        // https://github.com/FlaxEngine/FlaxEngine/issues/4034

        // Create base prefab with 2 objects
        AssetReference<Prefab> prefab = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefab);
        SCOPE_EXIT{ Content::DeleteAsset(prefab); };
        Guid id;
        Guid::Parse("333334524c696dcfa93cabacd2a4f404", id);
        prefab->ChangeID(id);
        auto prefabInit = prefab->Init(Prefab::TypeName,
            "["
            "{"
            "\"ID\": \"1111cfaa4bd1a53435129480e5bbdb3b\","
            "\"TypeName\": \"FlaxEngine.Decal\","
            "\"Name\": \"Old Root\""
            "},"
            "{"
            "\"ID\": \"2222814f4d913e58eb35ab8b0b7e2eef\","
            "\"TypeName\": \"FlaxEngine.Camera\","
            "\"ParentID\": \"1111cfaa4bd1a53435129480e5bbdb3b\","
            "\"Name\": \"New Root\""
            "}"
            "]");
        REQUIRE(!prefabInit);

        // Spawn test instances
        ScriptingObjectReference<Actor> instance1 = PrefabManager::SpawnPrefab(prefab);
        ScriptingObjectReference<Actor> instance2 = PrefabManager::SpawnPrefab(prefab);

        // Change root
        Actor* child = instance1->Children[0];
        child->SetParent(nullptr);
        instance1->SetParent(child);
        bool applyResult = PrefabManager::ApplyAll(instance1);
        REQUIRE(!applyResult);

        // Verify scenario
        REQUIRE(instance2);
        REQUIRE(instance2->GetName() == TEXT("Old Root"));
        REQUIRE(instance2->GetChildrenCount() == 0);
        REQUIRE(instance2->GetParent());
        REQUIRE(instance2->GetParent()->GetName() == TEXT("New Root"));
        REQUIRE(instance2->GetParent()->GetChildrenCount() == 1);
        REQUIRE(instance2->GetParent()->Children[0]->GetName() == TEXT("Old Root"));

        // Cleanup
        instance1->DeleteObject();
        instance2->DeleteObject();
    }
    SECTION("Test Adding Object To Base Prefab")
    {
        // https://github.com/LOOPDISK/FlaxEngine/pull/44

        // Create inner prefab with 3 objects in hierarchy
        AssetReference<Prefab> prefabInner = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabInner);
        Guid id;
        Guid::Parse("15dbe4b0416be0777a6ce59e8788b10f", id);
        prefabInner->ChangeID(id);
        auto prefabInnerInit = prefabInner->Init(Prefab::TypeName,
            "["
            "{"
            "\"ID\": \"3de462104f56f681c14650a0171f88fb\","
            "\"TypeName\" : \"FlaxEngine.SpotLight\","
            "\"Name\" : \"Inner.Root\""
            "},"
            "{"
            "\"ID\": \"19b181f846b6911635ffacb902c93c6a\","
            "\"TypeName\" : \"FlaxEngine.StaticModel\","
            "\"ParentID\" : \"3de462104f56f681c14650a0171f88fb\","
            "\"Name\" : \"Inner.Cube\""
            "},"
            "{"
            "\"ID\": \"8950889f4a2e752d55165fbf10eaf184\","
            "\"TypeName\" : \"FlaxEngine.AnimatedModel\","
            "\"ParentID\" : \"19b181f846b6911635ffacb902c93c6a\","
            "\"Name\" : \"Inner.Model\""
            "}"
            "]");
        REQUIRE(!prefabInnerInit);

        // Create outer prefab with 2 instances of inner prefab
        AssetReference<Prefab> prefabOuter = Content::CreateVirtualAsset<Prefab>();
        REQUIRE(prefabOuter);
        SCOPE_EXIT{ Content::DeleteAsset(prefabOuter); };
        Guid::Parse("2ab744714f746e31855f41815612d14b", id);
        prefabOuter->ChangeID(id);
        auto prefabOuterInit = prefabOuter->Init(Prefab::TypeName,
            "["
            "{"
            "\"ID\": \"dba7f4bb4acfd62608b9a8bf550f31a5\","
            "\"TypeName\": \"FlaxEngine.EmptyActor\","
            "\"Name\": \"Outer.Root\""
            "},"
            "{"
            "\"ID\": \"a3b705284432bed9f043829c04a2bc8f\","
            "\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"3de462104f56f681c14650a0171f88fb\","
            "\"ParentID\": \"dba7f4bb4acfd62608b9a8bf550f31a5\","
            "\"Name\": \"Instance 1\""
            "},"
            "{"
            "\"ID\": \"06a8c15a41b822dd27f3ac9d79b142d3\","
            "\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"19b181f846b6911635ffacb902c93c6a\","
            "\"ParentID\": \"a3b705284432bed9f043829c04a2bc8f\""
            "},"
            "{"
            "\"ID\": \"4759fb9e4c4dda3b61ab5ab43949e42f\","
            "\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"8950889f4a2e752d55165fbf10eaf184\","
            "\"ParentID\": \"06a8c15a41b822dd27f3ac9d79b142d3\""
            "},"
            "{"
            "\"ID\": \"1225be664c0c081e714bbf93e09b99e4\","
            "\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"3de462104f56f681c14650a0171f88fb\","
            "\"ParentID\": \"dba7f4bb4acfd62608b9a8bf550f31a5\","
            "\"Name\": \"Instance 2\""
            "},"
            "{"
            "\"ID\": \"b397243540322182b806ad8339b7b617\","
            "\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"19b181f846b6911635ffacb902c93c6a\","
            "\"ParentID\": \"1225be664c0c081e714bbf93e09b99e4\""
            "},"
            "{"
            "\"ID\": \"2c3b8e824daf038a58df528a238ca2de\","
            "\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
            "\"PrefabObjectID\": \"8950889f4a2e752d55165fbf10eaf184\","
            "\"ParentID\": \"b397243540322182b806ad8339b7b617\""
            "}"
            "]");
        REQUIRE(!prefabOuterInit);

        // Spawn test instances of both prefabs
        ScriptingObjectReference<Actor> instanceInner = PrefabManager::SpawnPrefab(prefabInner);
        ScriptingObjectReference<Actor> instanceOuter = PrefabManager::SpawnPrefab(prefabOuter);

        // Add new object to the inner prefab
        instanceInner->Children[0]->GetOrAddChild<DirectionalLight>();

        // Apply changes
        bool applyResult = PrefabManager::ApplyAll(instanceInner);
        REQUIRE(!applyResult);

        // Check state of outer instance to properly reflect hierarchy
        REQUIRE(instanceOuter);
        REQUIRE(instanceOuter->Children.Count() == 2);
        REQUIRE(instanceOuter->Children[0] != nullptr);
        REQUIRE(instanceOuter->Children[0]->Children.Count() == 1);
        REQUIRE(instanceOuter->Children[0]->Children[0]);
        REQUIRE(instanceOuter->Children[0]->Children[0]->Children.Count() == 2);
        REQUIRE(instanceOuter->Children[0]->Children[0]->Children[0]->Is<AnimatedModel>());
        REQUIRE(instanceOuter->Children[0]->Children[0]->Children[1]->Is<DirectionalLight>());
        REQUIRE(instanceOuter->Children[1] != nullptr);
        REQUIRE(instanceOuter->Children[1]->Children.Count() == 1);
        REQUIRE(instanceOuter->Children[1]->Children[0]);
        REQUIRE(instanceOuter->Children[1]->Children[0]->Children.Count() == 2);
        REQUIRE(instanceOuter->Children[1]->Children[0]->Children[0]->Is<AnimatedModel>());
        REQUIRE(instanceOuter->Children[0]->Children[0]->Children[1]->Is<DirectionalLight>());
        REQUIRE(instanceOuter->Children[0]->Children[0] != instanceOuter->Children[1]->Children[0]);
        REQUIRE(instanceOuter->Children[0]->Children[0]->Children[0] != instanceOuter->Children[1]->Children[0]->Children[0]);
        REQUIRE(instanceOuter->Children[0]->Children[0]->Children[1] != instanceOuter->Children[1]->Children[0]->Children[1]);

        // Cleanup
        instanceInner->DeleteObject();
        instanceOuter->DeleteObject();
    }
}
