/**
 * Debug spew for components
 * @param Object object to dump component spew for
 */
void DumpComponents(UObject *Object)
{
    for ( FObjectIterator It; It; ++It )
    {
        It->UnMark(EObjectMark(OBJECTMARK_TagImp | OBJECTMARK_TagExp));
    }

    if (FPlatformMisc::IsDebuggerPresent() )
    {
        // if we have a debugger attached, the watch window won't be able to display the full output if we attempt to log it as a single string
        // so pass in GLog instead so that each line is sent separately;  this causes the output to have an extra line break between each log statement,
        // but at least we'll be able to see the full output in the debugger's watch window
        UE_LOG(LogExporter, Log, TEXT("Components for '%s':"), *Object->GetFullName());
        ExportProperties( NULL, *GLog, Object->GetClass(), (uint8*)Object, 0, NULL, NULL, Object, PPF_SubobjectsOnly );
        UE_LOG(LogExporter, Log, TEXT("<--- DONE!"));
    }
    else
    {
        FStringOutputDevice Output;
        Output.Logf(TEXT("Components for '%s':\r\n"), *Object->GetFullName());
        ExportProperties( NULL, Output, Object->GetClass(), (uint8*)Object, 2, NULL, NULL, Object, PPF_SubobjectsOnly );
        Output.Logf(TEXT("<--- DONE!\r\n"));
        UE_LOG(LogExporter, Log, TEXT("%s"), *Output);
    }
}
FString DumpComponentsToString(UObject *Object)
{
    UnMarkAllObjects(EObjectMark(OBJECTMARK_TagExp | OBJECTMARK_TagImp));

    FStringOutputDevice Output;
    Output.Logf(TEXT("Components for '%s':\r\n"), *Object->GetFullName());
    ExportProperties(NULL, Output, Object->GetClass(), (uint8*)Object, 2, NULL, NULL, Object, PPF_SubobjectsOnly);
    Output.Logf(TEXT("<--- DONE!\r\n"));
    return Output;
}
void LogIncludeCount(const TArray<FString>& IncludesInPCH, const TMap<FString, int32>& IncludeHeaderCount, int32 FileCount, float IncludePercentageThreshold)
{
	for (const auto& FilenameAbsolutePath : IncludesInPCH)
	{
		auto IncludeCount = GetIncludeCount(IncludeHeaderCount, FilenameAbsolutePath);
		auto IncludePercentage = static_cast<float>(IncludeCount) / static_cast<float>(FileCount);
		if (IncludePercentage < IncludePercentageThreshold)
		{
			OutputFileContents.Logf(TEXT("%s %i(%f%%) with threshold %f%%"), *FilenameAbsolutePath, IncludeCount, IncludePercentage, IncludePercentageThreshold);
		}
	}
}
int AnalyzePCHFile(const FString& CmdLine)
{
	auto IncludePercentageThreshold = ParseUsageThreshold(*CmdLine);
	auto IncludeSearchPaths = ParseIncludeSearchPaths(*CmdLine);

	FString IncludesDirectory;
	FParse::Value(*CmdLine, TEXT("-HeaderDataPath="), IncludesDirectory);

	int32 FileCount = 0;
	TMap<FString, int32> IncludeHeaderCount;
	GatherModuleHeaderData(*IncludesDirectory, FileCount, IncludeHeaderCount);

	// Open PCH file
	FString PCHFilePath;
	FParse::Value(*CmdLine, TEXT("-PCHFile="), PCHFilePath);

	TArray<FString> IncludesInPCH = GetIncludesInPCH(PCHFilePath, IncludeSearchPaths);

	OutputFileContents.Logf(TEXT("Remove the following #includes from %s"), *PCHFilePath);
	LogIncludeCount(IncludesInPCH, IncludeHeaderCount, FileCount, IncludePercentageThreshold);


	OutputFileContents.Logf(TEXT("Add the following #includes to %s"), *PCHFilePath);
	for (auto KVP : IncludeHeaderCount)
	{
		const auto& Filename = KVP.Key;
		const auto& HeaderIncludeCount = KVP.Value;
		auto IncludePercentage = static_cast<float>(HeaderIncludeCount) / static_cast<float>(FileCount);
		if (IncludePercentage > IncludePercentageThreshold)
		{
			OutputFileContents.Logf(TEXT("%s (%i/%i)%f%%"), *Filename, HeaderIncludeCount, FileCount, IncludePercentage);
		}
	}

	return 0;
}
bool UDiffAssetsCommandlet::ExportFile(const FString& Filename, const TArray<UObject *>& LoadedObjects)
{
	FString Extension = TEXT("t3d");
	FStringOutputDevice Buffer;
	const FExportObjectInnerContext Context;
	for (int32 Index = 0; Index < LoadedObjects.Num(); Index++)
	{
		UExporter* Exporter = UExporter::FindExporter( LoadedObjects[Index], *Extension );
		if (!Exporter)
		{
			UE_LOG(LogDiffAssetsCommandlet, Warning, TEXT("Could not find exporter."));
			return false;
		}
		UExporter::ExportToOutputDevice( &Context, LoadedObjects[Index], Exporter, Buffer, *Extension, 0, PPF_ExportsNotFullyQualified, false );
		TMap<FString,FString> NativePropertyValues;
		if ( LoadedObjects[Index]->GetNativePropertyValues(NativePropertyValues) && NativePropertyValues.Num())
		{
			int32 LargestKey = 0;
			for ( TMap<FString,FString>::TIterator It(NativePropertyValues); It; ++It )
			{
				LargestKey = FMath::Max(LargestKey, It.Key().Len());
			}
			for ( TMap<FString,FString>::TIterator It(NativePropertyValues); It; ++It )
			{
				Buffer.Logf(TEXT("  %s=%s"), *It.Key().RightPad(LargestKey), *It.Value());
			}
		}
	}
	if (!Buffer.Len())
	{
		UE_LOG(LogDiffAssetsCommandlet, Warning, TEXT("No text was exported!"));
		return false;
	}
	if( !FFileHelper::SaveStringToFile( Buffer, *Filename ) )
	{
		UE_LOG(LogDiffAssetsCommandlet, Warning, TEXT("Could not write %s"), *Filename);
		return false;
	}
	return true;
}