FString FLuaScriptCodeGenerator::ExportFunction(const FString& ClassNameCPP, UClass* Class, UFunction* Function)
{
	FString GeneratedGlue = GenerateWrapperFunctionDeclaration(ClassNameCPP, Class, Function);
	GeneratedGlue += TEXT("\r\n{\r\n");

	UProperty* ReturnValue = NULL;
	UClass* FuncSuper = NULL;
	
	if (Function->GetOwnerClass() != Class)
	{
		// Find the base definition of the function
		if (ExportedClasses.Contains(Function->GetOwnerClass()->GetFName()))
		{
			FuncSuper = Function->GetOwnerClass();
		}
	}

	FString FunctionBody;
	if (FuncSuper == NULL)
	{
		FunctionBody += FString::Printf(TEXT("\t%s\r\n"), *GenerateObjectDeclarationFromContext(ClassNameCPP, Class));
		FunctionBody += GenerateFunctionDispatch(Function);

		FString FunctionCallArguments;
		FString ReturnValueDeclaration;
		for (TFieldIterator<UProperty> ParamIt(Function); !ReturnValue && ParamIt; ++ParamIt)
		{
			UProperty* Param = *ParamIt;
			if (Param->GetPropertyFlags() & CPF_ReturnParm)
			{
				ReturnValue = Param;
			}
		}
		FString ReturnValueName;
		if (ReturnValue)
		{
			ReturnValueName = FString::Printf(TEXT("Params.%s"), *ReturnValue->GetName());
		}
		FunctionBody += FString::Printf(TEXT("\t%s\r\n"), *GenerateReturnValueHandler(ClassNameCPP, Class, Function, ReturnValue, *ReturnValueName));
	}
	else
	{
		FunctionBody = FString::Printf(TEXT("\treturn %s_%s(InScriptContext);\r\n"), *FuncSuper->GetName(), *Function->GetName());
	}

	GeneratedGlue += FunctionBody;
	GeneratedGlue += TEXT("}\r\n\r\n");

	auto& Exports = ClassExportedFunctions.FindOrAdd(Class);
	Exports.Add(Function->GetFName());

	return GeneratedGlue;
}
void FObjectReplicator::StartReplicating( class UActorChannel * InActorChannel )
{
	check( OwningChannel == NULL );

	OwningChannel = InActorChannel;

	// Cache off netGUID so if this object gets deleted we can close it
	ObjectNetGUID = OwningChannel->Connection->PackageMap->GetObjectNetGUID( GetObject() );

	if ( !ObjectNetGUID.IsValid() )
	{
		ObjectNetGUID = OwningChannel->Connection->PackageMap->AssignNewNetGUID( GetObject() );
		check( !ObjectNetGUID.IsDefault() && ObjectNetGUID.IsValid() );
	}

	TempBitWriter = new FNetBitWriter( OwningChannel->Connection->PackageMap, 0 );

	// Allocate retirement list.
	// SetNum now constructs, so this is safe
	Retirement.SetNum( ObjectClass->ClassReps.Num() );

	// figure out list of replicated object properties
	for ( UProperty* Prop = ObjectClass->PropertyLink; Prop != NULL; Prop = Prop->PropertyLinkNext )
	{
		if ( Prop->PropertyFlags & CPF_Net )
		{
			if ( IsCustomDeltaProperty( Prop ) )
			{
				for ( int32 i = 0; i < Prop->ArrayDim; i++ )
				{
					Retirement[Prop->RepIndex + i].CustomDelta = 1;
				}
			}

			if ( Prop->GetPropertyFlags() & CPF_Config )
			{
				for ( int32 i = 0; i < Prop->ArrayDim; i++ )
				{
					Retirement[Prop->RepIndex + i].Config = 1;
				}
			}
		}
	}
}
void FObjectReplicator::StartReplicating( class UActorChannel * InActorChannel )
{
	check( OwningChannel == NULL );

	if ( GetObject() == NULL )
	{
		UE_LOG(LogRep, Error, TEXT("StartReplicating: Object == NULL"));
		return;
	}

	OwningChannel = InActorChannel;

	// Cache off netGUID so if this object gets deleted we can close it
	ObjectNetGUID = OwningChannel->Connection->Driver->GuidCache->GetOrAssignNetGUID( GetObject() );
	check( !ObjectNetGUID.IsDefault() && ObjectNetGUID.IsValid() );

	// Allocate retirement list.
	// SetNum now constructs, so this is safe
	Retirement.SetNum( ObjectClass->ClassReps.Num() );

	// figure out list of replicated object properties
	for ( UProperty* Prop = ObjectClass->PropertyLink; Prop != NULL; Prop = Prop->PropertyLinkNext )
	{
		if ( Prop->PropertyFlags & CPF_Net )
		{
			if ( IsCustomDeltaProperty( Prop ) )
			{
				for ( int32 i = 0; i < Prop->ArrayDim; i++ )
				{
					Retirement[Prop->RepIndex + i].CustomDelta = 1;
				}
			}

			if ( Prop->GetPropertyFlags() & CPF_Config )
			{
				for ( int32 i = 0; i < Prop->ArrayDim; i++ )
				{
					Retirement[Prop->RepIndex + i].Config = 1;
				}
			}
		}
	}
}
static void GetLifetimeBlueprintReplicationList( const AActor* ThisActor, const UBlueprintGeneratedClass* MyClass, TArray< FLifetimeProperty > & OutLifetimeProps )
{
	if ( MyClass == NULL )
	{
		return;
	}

	uint32 PropertiesLeft = MyClass->NumReplicatedProperties;

	for ( TFieldIterator<UProperty> It( MyClass, EFieldIteratorFlags::ExcludeSuper ); It && PropertiesLeft > 0; ++It )
	{
		UProperty * Prop = *It;
		if ( Prop != NULL && Prop->GetPropertyFlags() & CPF_Net )
		{
			PropertiesLeft--;
			OutLifetimeProps.Add( FLifetimeProperty( Prop->RepIndex ) );
		}
	}

	return GetLifetimeBlueprintReplicationList( ThisActor, Cast< UBlueprintGeneratedClass >( MyClass->GetSuperStruct() ), OutLifetimeProps );
}
	void CallJavascriptFunction(Handle<Context> context, Handle<Value> This, UFunction* SignatureFunction, Handle<Function> func, void* Parms)
	{
		SCOPE_CYCLE_COUNTER(STAT_JavascriptFunctionCallToJavascript);

		auto isolate = context->GetIsolate();

		HandleScope handle_scope(isolate);

		auto Buffer = reinterpret_cast<uint8*>(Parms);		

		enum { MaxArgs = 32 };

		Handle<Value> argv[MaxArgs];
		int argc = 0;

		TFieldIterator<UProperty> Iter(SignatureFunction);
		for (; Iter && argc < MaxArgs && (Iter->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm; ++Iter)
		{
			UProperty* Param = *Iter;
			argv[argc++] = ReadProperty(isolate, Param, Buffer, FNoPropertyOwner());
		}

		UProperty* ReturnParam = nullptr;
		for (; Iter; ++Iter)
		{
			UProperty* Param = *Iter;
			if (Param->GetPropertyFlags() & CPF_ReturnParm)
			{
				ReturnParam = Param;
				break;
			}
		}

		TryCatch try_catch;		

		auto value = func->Call(This, argc, argv);

		if (try_catch.HasCaught())
		{
			FV8Exception::Report(try_catch);
		}

		bool bHasAnyOutParams = false;
		if (SignatureFunction && SignatureFunction->HasAnyFunctionFlags(FUNC_HasOutParms))
		{
			// Iterate over input parameters
			for (TFieldIterator<UProperty> It(SignatureFunction); It && (It->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm; ++It)
			{
				// This is 'out ref'!
				if ((It->PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
				{
					bHasAnyOutParams = true;
					break;
				}
			}
		}

		if (bHasAnyOutParams)
		{
			FIsolateHelper I(isolate);
			if (value.IsEmpty() || !value->IsObject())
			{
				I.Throw(TEXT("..."));
				return;
			}

			auto Object = value->ToObject();

			// Iterate over parameters again
			for (TFieldIterator<UProperty> It(SignatureFunction); It; ++It)
			{
				UProperty* Param = *It;

				auto PropertyFlags = Param->GetPropertyFlags();					

				// pass return parameter as '$'
				if (PropertyFlags & CPF_ReturnParm)
				{
					auto sub_value = Object->Get(I.Keyword("$"));

					WriteProperty(isolate, ReturnParam, Buffer, sub_value);						
				}
				// rejects 'const T&' and pass 'T&' as its name
				else if ((PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
				{
					auto sub_value = Object->Get(I.Keyword(Param->GetName()));

					if (!sub_value.IsEmpty())
					{
						// value can be null if isolate is in trouble
						WriteProperty(isolate, Param, Buffer, sub_value);
					}						
				}
			}			
		}
		else
		{
			if (ReturnParam)
			{
				WriteProperty(isolate, ReturnParam, Buffer, value);
			}
		}		
	}
UProperty* FCSharpWrapperGenerator::GetWrapperArgsAndReturnType(
	const UFunction* Function, FString& OutFormalInteropArgs, FString& OutActualInteropArgs,
	FString& OutFormalManagedArgs, FString& OutActualManagedArgs
)
{
	OutFormalInteropArgs = TEXT("UObjectHandle self");
	OutActualInteropArgs = NativeThisPointer;
	OutFormalManagedArgs.Empty();
	OutActualManagedArgs.Empty();
	UProperty* returnValue = nullptr;

	for (TFieldIterator<UProperty> paramIt(Function); paramIt; ++paramIt)
	{
		UProperty* param = *paramIt;
		if (param->GetPropertyFlags() & CPF_ReturnParm)
		{
			returnValue = param;
		}
		else
		{
			FString argName = param->GetName();
			FString argInteropType = GetPropertyInteropType(param);
			FString argAttrs = GetPropertyInteropTypeAttributes(param);
			FString argMods = GetPropertyInteropTypeModifiers(param);
			
			OutFormalInteropArgs += TEXT(",");
			if (!argAttrs.IsEmpty())
			{
				OutFormalInteropArgs += TEXT(" ");
				OutFormalInteropArgs += argAttrs;
			}
			if (!argMods.IsEmpty())
			{
				OutFormalInteropArgs += TEXT(" ");
				OutFormalInteropArgs += argMods;
			}
			OutFormalInteropArgs += FString::Printf(
				TEXT(" %s %s"), *argInteropType, *argName
			);

			OutActualInteropArgs += TEXT(",");
			if (!argMods.IsEmpty())
			{
				OutActualInteropArgs += TEXT(" ");
				OutActualInteropArgs += argMods;
			}
			OutActualInteropArgs += TEXT(" ");
			if (param->IsA<UObjectProperty>())
			{
				OutActualInteropArgs += TEXT("(UObjectHandle)");
			}
			OutActualInteropArgs += argName;
									
			if (!OutFormalManagedArgs.IsEmpty())
			{
				OutFormalManagedArgs += TEXT(", ");
			}
			if (!argMods.IsEmpty())
			{
				OutFormalManagedArgs += argMods + TEXT(" ");
			}
			FString ArgManagedType = GetPropertyManagedType(param);
			OutFormalManagedArgs += FString::Printf(TEXT("%s %s"), *ArgManagedType, *argName);
			
			if (!OutActualManagedArgs.IsEmpty())
			{
				OutActualManagedArgs += TEXT(", ");
			}
			OutActualManagedArgs += argName;
		}
	}

	return returnValue;
}