#include "WingFactories.h"
#include "WingServer.h"
#include "WingUtils.h"
#include "PackageTools.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Kismet2/EnumEditorUtils.h"

bool WingFactories::CheckNewAssetPath(const FString& Path)
{
	if (UPackageTools::SanitizePackageName(Path) != Path)
	{
		UWingServer::Printf(TEXT("ERROR: Package path '%s' is not a valid package name\n"), *Path);
		return false;
	}
	if (!FPackageName::IsValidTextForLongPackageName(Path))
	{
		UWingServer::Printf(TEXT("ERROR: Package path '%s' is not a valid package name\n"), *Path);
		return false;
	}
	if (!Path.StartsWith(TEXT("/Game")))
	{
		UWingServer::Printf(TEXT("ERROR: Package path '%s' must start with '/Game'\n"), *Path);
		return false;
	}
	if (FindObject<UPackage>(nullptr, *Path))
	{
		UWingServer::Printf(TEXT("ERROR: An asset already exists at '%s'\n"), *Path);
		return false;
	}
	return true;
}

bool WingFactories::IsBlacklisted(UClass* FactoryClass)
{
	FName Name = FactoryClass->GetFName();
	if (Name == TEXT("PhysicsAssetFactory")) return true;  // Pops a modal dialog
	if (Name == TEXT("EnumFactory")) return true;           // Pops a modal dialog
	return false;
}

UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory)
{
	// Check the blacklist.
	if (IsBlacklisted(Factory->GetClass()))
	{
		UWingServer::Printf(TEXT("ERROR: Factory '%s' is blacklisted\n"), *Factory->GetClass()->GetName());
		return nullptr;
	}

	// Validate the path, and that there's not already something there.
	if (!CheckNewAssetPath(Path)) return nullptr;
	FName Name = FName(FPackageName::GetShortName(Path));

	// Create the package.
	UPackage *Package = CreatePackage(*Path);
	if (!Package)
	{
		UWingServer::Printf(TEXT("ERROR: Failed to create package at '%s'\n"), *Path);
		return nullptr;
	}

	// Create the asset.
	UObject* NewAsset = Factory->FactoryCreateNew(
		Factory->GetSupportedClass(),
		Package,
		Name,
		RF_Public | RF_Standalone | RF_Transactional,
		nullptr,
		GWarn
	);

	// If the asset was created, turn the package into a
	// permanent package and encourge the editor to save it
	// to disk.  Otherwise, mark the package for deletion.
	if (NewAsset)
	{
		Package->ClearFlags(RF_Transient);
		Package->SetIsExternallyReferenceable(true);
		Package->MarkPackageDirty();
		FAssetRegistryModule::AssetCreated(NewAsset);
		UWingServer::AddTouchedObject(NewAsset);
	}
	else
	{
		UWingServer::Printf(TEXT("ERROR: Factory '%s' failed to create an object\n"), *Factory->GetClass()->GetName());
		Package->ClearDirtyFlag();
		Package->MarkAsGarbage();
		return nullptr;
	}
	return NewAsset;
}

FString WingFactories::DeriveFactoryName(UClass* FactoryClass)
{
	FString Name = FactoryClass->GetName();
	int32 Index = Name.Find(TEXT("Factory"));
	if (Index != INDEX_NONE)
	{
		Name.LeftInline(Index);
	}
	if (Name.EndsWith(TEXT("_")))
	{
		Name.LeftChopInline(1);
	}
	return Name;
}

UObject* UEnumFactoryWing::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
	if (!FEnumEditorUtils::IsNameAvailebleForUserDefinedEnum(Name))
	{
		UWingServer::Printf(TEXT("ERROR: Enum name is already taken: %s\n"), *Name.ToString());
		return nullptr;
	}
	return FEnumEditorUtils::CreateUserDefinedEnum(InParent, Name, Flags);
}

