void FChunkManifestGenerator::GenerateChunkManifestForPackage(const FName& PackageFName, const FString& PackagePathName, const FString& SandboxFilename, const FString& LastLoadedMapName, FSandboxPlatformFile* InSandboxFile)
{
	TArray<int32> TargetChunks;
	TArray<int32> ExistingChunkIDs;

	if (!bGenerateChunks)
	{
		TargetChunks.AddUnique(0);
		ExistingChunkIDs.AddUnique(0);
	}

	if (bGenerateChunks)
	{
		// Collect all chunk IDs associated with this package from the asset registry
		TArray<int32> RegistryChunkIDs = GetAssetRegistryChunkAssignments(PackageFName);
		ExistingChunkIDs = GetExistingPackageChunkAssignments(PackageFName);

		// Try to call game-specific delegate to determine the target chunk ID
		// FString Name = Package->GetPathName();
		if (FGameDelegates::Get().GetAssignStreamingChunkDelegate().IsBound())
		{
			FGameDelegates::Get().GetAssignStreamingChunkDelegate().ExecuteIfBound(PackagePathName, LastLoadedMapName, RegistryChunkIDs, ExistingChunkIDs, TargetChunks);
		}
		else
		{
			//Take asset registry assignments and existing assignments
			TargetChunks.Append(RegistryChunkIDs);
			TargetChunks.Append(ExistingChunkIDs);
		}
	}

	bool bAssignedToChunk = false;
	// if the delegate requested a specific chunk assignment, add them package to it now.
	for (const auto& PackageChunk : TargetChunks)
	{
		AddPackageToManifest(SandboxFilename, PackageFName, PackageChunk);
		bAssignedToChunk = true;
	}
	// If the delegate requested to remove the package from any chunk, remove it now
	for (const auto& PackageChunk : ExistingChunkIDs)
	{
		if (!TargetChunks.Contains(PackageChunk))
		{
			RemovePackageFromManifest(PackageFName, PackageChunk);
		}
	}

}
void FChunkManifestGenerator::AddPackageToChunkManifest(UPackage* Package, const FString& SandboxFilename, const FString& LastLoadedMapName, FSandboxPlatformFile* InSandboxFile)
{
	TArray<int32> TargetChunks;
	TArray<int32> ExistingChunkIDs;
	
	if (!bGenerateChunks)
	{
		TargetChunks.AddUnique(0);
		ExistingChunkIDs.AddUnique(0);
	}
	
	auto PackageFName = Package->GetFName();
	if (bGenerateChunks)
	{
		// Try to determine if this package has been loaded as a result of loading a map package.
		FString MapThisAssetWasLoadedWith;
		if (!LastLoadedMapName.IsEmpty())
		{
			if (AssetsLoadedWithLastPackage.Contains(PackageFName))
			{
				MapThisAssetWasLoadedWith = LastLoadedMapName;
			}
		}

		// Collect all chunk IDs associated with this package from the asset registry
		TArray<int32> RegistryChunkIDs = GetAssetRegistryChunkAssignments(Package);
		ExistingChunkIDs = GetExistingPackageChunkAssignments(PackageFName);

		// Try to call game-specific delegate to determine the target chunk ID
		FString Name = Package->GetPathName();
		if (FGameDelegates::Get().GetAssignStreamingChunkDelegate().IsBound())
		{
			FGameDelegates::Get().GetAssignStreamingChunkDelegate().ExecuteIfBound(Name, MapThisAssetWasLoadedWith, RegistryChunkIDs, ExistingChunkIDs, TargetChunks);
		}
		else
		{
			//Take asset registry assignments and existing assignments
			TargetChunks.Append(RegistryChunkIDs);
			TargetChunks.Append(ExistingChunkIDs);
		}
	}

	NotifyPackageWasCooked(SandboxFilename, PackageFName);

	bool bAssignedToChunk = false;
	// if the delegate requested a specific chunk assignment, add them package to it now.
	for (const auto& PackageChunk : TargetChunks)
	{
		AddPackageToManifest(SandboxFilename, PackageFName, PackageChunk);
		bAssignedToChunk = true;
	}
	// If the delegate requested to remove the package from any chunk, remove it now
	for (const auto& PackageChunk : ExistingChunkIDs)
	{
		if (!TargetChunks.Contains(PackageChunk))
		{
			RemovePackageFromManifest(PackageFName, PackageChunk);
		}
	}

	if (!bAssignedToChunk)
	{
		NotifyPackageWasNotAssigned(SandboxFilename, PackageFName);
	}
}
void FChunkManifestGenerator::BuildChunkManifest(const TArray<FName>& CookedPackages, FSandboxPlatformFile* InSandboxFile, bool bGenerateStreamingInstallManifest)
{
	bGenerateChunks = bGenerateStreamingInstallManifest;

	// initialize LargestChunkId, FoundIDList, PackageChunkIDMap, AssetRegistryData

	// Calculate the largest chunk id used by the registry to get the indices for the default chunks
	AssetRegistry.GetAllAssets(AssetRegistryData);
	int32 LargestChunkID = -1;

	for (int32 Index = 0; Index < AssetRegistryData.Num(); ++Index)
	{
		auto& AssetData = AssetRegistryData[Index];
		auto& RegistryChunkIDs = RegistryChunkIDsMap.FindOrAdd(AssetData.PackageName);
		for (auto ChunkIt = AssetData.ChunkIDs.CreateConstIterator(); ChunkIt; ++ChunkIt)
		{
			int32 ChunkID = *ChunkIt;
			if (ChunkID < 0)
			{
				UE_LOG(LogChunkManifestGenerator, Warning, TEXT("Out of range ChunkID: %d"), ChunkID);
				ChunkID = 0;
			}
			else if (ChunkID > LargestChunkID)
			{
				LargestChunkID = ChunkID;
			}
			if (!RegistryChunkIDs.Contains(ChunkID))
			{
				RegistryChunkIDs.Add(ChunkID);
			}
			auto* FoundIDList = PackageChunkIDMap.Find(AssetData.PackageName);
			if (!FoundIDList)
			{
				FoundIDList = &PackageChunkIDMap.Add(AssetData.PackageName);
			}
			FoundIDList->AddUnique(ChunkID);
		}
		// Now clear the original chunk id list. We will fill it with real IDs when cooking.
		AssetData.ChunkIDs.Empty();
		// Map asset data to its package (there can be more than one asset data per package).
		auto& PackageData = PackageToRegistryDataMap.FindOrAdd(AssetData.PackageName);
		PackageData.Add(Index);
	}


	// add all the packages to the unassigned package list
	for (const auto& CookedPackage : CookedPackages)
	{
		const FString SandboxPath = InSandboxFile->ConvertToAbsolutePathForExternalAppForWrite(*FPackageName::LongPackageNameToFilename(CookedPackage.ToString()));

		AllCookedPackages.Add(CookedPackage, SandboxPath);
		UnassignedPackageSet.Add(CookedPackage, SandboxPath);
	}
	
	for (const auto& CookedPackage : StartupPackages)
	{
		const FString SandboxPath = InSandboxFile->ConvertToAbsolutePathForExternalAppForWrite(*FPackageName::LongPackageNameToFilename(CookedPackage.ToString()));

		AllCookedPackages.Add(CookedPackage, SandboxPath);
		AddPackageToManifest(SandboxPath, CookedPackage, 0);
	}


	// assign chunks for all the map packages
	for (const auto& CookedPackage : UnassignedPackageSet)
	{
		// ignore non map packages for now
		const FName MapFName = CookedPackage.Key;

		// this package could be missing from the map because it didn't get cooked. 
		// the reason for this might be that it's a redirector therefore we cooked the package which actually contains the asset
		if (PackageToRegistryDataMap.Find(MapFName) == nullptr)
			continue;

		if (ContainsMap(MapFName) == false)
			continue;

		// get all the dependencies for this map
		TArray<FName> MapDependencies;
		ensure(GatherAllPackageDependencies(MapFName, MapDependencies));

		
		for (const auto& RawPackageFName : MapDependencies)
		{
			const FName PackageFName = GetPackageNameFromDependencyPackageName(RawPackageFName);
			
			if (PackageFName == NAME_None)
			{
				continue;
			}
			
			const FString PackagePathName = PackageFName.ToString();
			const FString MapName = MapFName.ToString();
			const FString* SandboxFilenamePtr = AllCookedPackages.Find(PackageFName);
			if (!SandboxFilenamePtr)
			{
				const FString SandboxPath = InSandboxFile->ConvertToAbsolutePathForExternalAppForWrite(*FPackageName::LongPackageNameToFilename(PackagePathName));

				AllCookedPackages.Add(PackageFName, SandboxPath);

				SandboxFilenamePtr = AllCookedPackages.Find(PackageFName);
				check(SandboxFilenamePtr);
			}
			const FString& SandboxFilename = *SandboxFilenamePtr;


			GenerateChunkManifestForPackage(PackageFName, PackagePathName, SandboxFilename, MapName, InSandboxFile);


		}

	}

	// process the remaining unassigned packages, they don't have a map associated with them
	// probably a good reason for it but maybe not
	for (const auto& CookedPackage : UnassignedPackageSet)
	{
		const FName& PackageFName = CookedPackage.Key;
		const FString& SandboxFilename = AllCookedPackages.FindChecked(PackageFName);
		const FString PackagePathName = PackageFName.ToString();

		GenerateChunkManifestForPackage(PackageFName, PackagePathName, SandboxFilename, FString(), InSandboxFile);
	}


	// anything that remains in the UnAssignedPackageSet will be put in chunk0 when we save the asset registry

}