bool FStructureEditorUtils::ChangeVariableType(UUserDefinedStruct* Struct, FGuid VarGuid, const FEdGraphPinType& NewType)
{
    if (Struct)
    {
        FString ErrorMessage;
        if(!CanHaveAMemberVariableOfType(Struct, NewType, &ErrorMessage))
        {
            UE_LOG(LogBlueprint, Warning, TEXT("%s"), *ErrorMessage);
            return false;
        }

        auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
        if(VarDesc)
        {
            const bool bChangedType = (VarDesc->ToPinType() != NewType);
            if (bChangedType)
            {
                const FScopedTransaction Transaction(LOCTEXT("ChangeVariableType", "Change Variable Type"));
                ModifyStructData(Struct);

                VarDesc->VarName = FMemberVariableNameHelper::Generate(Struct, VarDesc->FriendlyName, VarDesc->VarGuid);
                VarDesc->DefaultValue = FString();
                VarDesc->SetPinType(NewType);

                OnStructureChanged(Struct);
                return true;
            }
        }
    }
    return false;
}
bool FStructureEditorUtils::Change3dWidgetEnabled(UUserDefinedStruct* Struct, FGuid VarGuid, bool bIsEnabled)
{
    auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
    const auto PropertyStruct = VarDesc ? Cast<const UStruct>(VarDesc->SubCategoryObject.Get()) : NULL;
    if (FEdMode::CanCreateWidgetForStructure(PropertyStruct) && (VarDesc->bEnable3dWidget != bIsEnabled))
    {
        const FScopedTransaction Transaction(LOCTEXT("Change3dWidgetEnabled", "Change 3d Widget Enabled"));
        ModifyStructData(Struct);

        VarDesc->bEnable3dWidget = bIsEnabled;
        auto Property = FindField<UProperty>(Struct, VarDesc->VarName);
        if (Property)
        {
            if (VarDesc->bEnable3dWidget)
            {
                Property->SetMetaData(FEdMode::MD_MakeEditWidget, TEXT("true"));
            }
            else
            {
                Property->RemoveMetaData(FEdMode::MD_MakeEditWidget);
            }
        }
        return true;
    }
    return false;
}
bool FStructureEditorUtils::RenameVariable(UUserDefinedStruct* Struct, FGuid VarGuid, const FString& NewDisplayNameStr)
{
    if (Struct)
    {
        auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
        if (VarDesc
                && !NewDisplayNameStr.IsEmpty()
                && FName(*NewDisplayNameStr).IsValidXName(INVALID_OBJECTNAME_CHARACTERS)
                && IsUniqueVariableDisplayName(Struct, NewDisplayNameStr))
        {
            const FScopedTransaction Transaction(LOCTEXT("RenameVariable", "Rename Variable"));
            ModifyStructData(Struct);

            VarDesc->FriendlyName = NewDisplayNameStr;
            //>>> TEMPORARY it's more important to prevent changes in structs instances, than to have consistent names
            if (GetGuidFromPropertyName(VarDesc->VarName).IsValid())
                //<<< TEMPORARY
            {
                const FName NewName = FMemberVariableNameHelper::Generate(Struct, NewDisplayNameStr, VarGuid);
                check(NULL == GetVarDesc(Struct).FindByPredicate(FFindByNameHelper<FStructVariableDescription>(NewName)))
                VarDesc->VarName = NewName;
            }
            OnStructureChanged(Struct);
            return true;
        }
    }
    return false;
}
bool FStructureEditorUtils::ChangeMultiLineTextEnabled(UUserDefinedStruct* Struct, FGuid VarGuid, bool bIsEnabled)
{
	auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
	if (CanEnableMultiLineText(Struct, VarGuid) && VarDesc->bEnableMultiLineText != bIsEnabled)
	{
		const FScopedTransaction Transaction(LOCTEXT("ChangeMultiLineTextEnabled", "Change Multi-line Text Enabled"));
		ModifyStructData(Struct);

		VarDesc->bEnableMultiLineText = bIsEnabled;
		auto Property = FindField<UProperty>(Struct, VarDesc->VarName);
		if (Property)
		{
			if (VarDesc->bEnableMultiLineText)
			{
				Property->SetMetaData("MultiLine", TEXT("true"));
			}
			else
			{
				Property->RemoveMetaData("MultiLine");
			}
		}
		OnStructureChanged(Struct);
		return true;
	}
	return false;
}
bool FStructureEditorUtils::ChangeVariableDefaultValue(UUserDefinedStruct* Struct, FGuid VarGuid, const FString& NewDefaultValue)
{
	auto ValidateDefaultValue = [](const FStructVariableDescription& VarDesc, const FString& InNewDefaultValue) -> bool
	{
		const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
		const FEdGraphPinType PinType = VarDesc.ToPinType();

		bool bResult = false;
		//TODO: validation for values, that are not passed by string
		if (PinType.PinCategory == K2Schema->PC_Text)
		{
			bResult = true;
		}
		else if ((PinType.PinCategory == K2Schema->PC_Object) 
			|| (PinType.PinCategory == K2Schema->PC_Interface) 
			|| (PinType.PinCategory == K2Schema->PC_Class)
			|| (PinType.PinCategory == K2Schema->PC_AssetClass)
			|| (PinType.PinCategory == K2Schema->PC_Asset))
		{
			// K2Schema->DefaultValueSimpleValidation finds an object, passed by path, invalid
			bResult = true;
		}
		else
		{
			bResult = K2Schema->DefaultValueSimpleValidation(PinType, FString(), InNewDefaultValue, NULL, FText::GetEmpty());
		}
		return bResult;
	};

	auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
	if (VarDesc 
		&& (NewDefaultValue != VarDesc->DefaultValue)
		&& ValidateDefaultValue(*VarDesc, NewDefaultValue))
	{
		bool bAdvancedValidation = true;
		if (!NewDefaultValue.IsEmpty())
		{
			const auto Property = FindField<UProperty>(Struct, VarDesc->VarName);
			FStructOnScope StructDefaultMem(Struct);
			bAdvancedValidation = StructDefaultMem.IsValid() && Property &&
				FBlueprintEditorUtils::PropertyValueFromString(Property, NewDefaultValue, StructDefaultMem.GetStructMemory());
		}

		if (bAdvancedValidation)
		{
			const FScopedTransaction Transaction(LOCTEXT("ChangeVariableDefaultValue", "Change Variable Default Value"));
			
			TGuardValue<FStructureEditorUtils::EStructureEditorChangeInfo> ActiveChangeGuard(FStructureEditorUtils::FStructEditorManager::ActiveChange, EStructureEditorChangeInfo::DefaultValueChanged);

			ModifyStructData(Struct);
			
			VarDesc->DefaultValue = NewDefaultValue;
			OnStructureChanged(Struct, EStructureEditorChangeInfo::DefaultValueChanged);
			return true;
		}
	}
	return false;
}
bool FStructureEditorUtils::IsMultiLineTextEnabled(const UUserDefinedStruct* Struct, FGuid VarGuid)
{
	auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
	if (CanEnableMultiLineText(Struct, VarGuid))
	{
		return VarDesc->bEnableMultiLineText;
	}
	return false;
}
bool FStructureEditorUtils::ChangeEditableOnBPInstance(UUserDefinedStruct* Struct, FGuid VarGuid, bool bInIsEditable)
{
    const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
    auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
    const bool bNewDontEditoOnInstance = !bInIsEditable;
    if (VarDesc && (bNewDontEditoOnInstance != VarDesc->bDontEditoOnInstance))
    {
        const FScopedTransaction Transaction(LOCTEXT("ChangeVariableOnBPInstance", "Change variable editable on BP instance"));
        ModifyStructData(Struct);

        VarDesc->bDontEditoOnInstance = bNewDontEditoOnInstance;
        OnStructureChanged(Struct);
        return true;
    }
    return false;
}
bool FStructureEditorUtils::ChangeVariableTooltip(UUserDefinedStruct* Struct, FGuid VarGuid, const FString& InTooltip)
{
    auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
    if (VarDesc && (InTooltip != VarDesc->ToolTip))
    {
        const FScopedTransaction Transaction(LOCTEXT("ChangeVariableTooltip", "Change UDS Variable Tooltip"));
        ModifyStructData(Struct);
        VarDesc->ToolTip = InTooltip;

        auto Property = FindField<UProperty>(Struct, VarDesc->VarName);
        if (Property)
        {
            Property->SetMetaData(FBlueprintMetadata::MD_Tooltip, *VarDesc->ToolTip);
        }

        return true;
    }
    return false;
}
bool FStructureEditorUtils::CanEnableMultiLineText(const UUserDefinedStruct* Struct, FGuid VarGuid)
{
	auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
	if (VarDesc)
	{
		UProperty* Property = FindField<UProperty>(Struct, VarDesc->VarName);

		// If this is an array, we need to test its inner property as that's the real type
		if (UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property))
		{
			Property = ArrayProperty->Inner;
		}

		if (Property)
		{
			// Can only set multi-line text on string and text properties
			return Property->IsA(UStrProperty::StaticClass())
				|| Property->IsA(UTextProperty::StaticClass());
		}
	}
	return false;
}
UProperty* FStructureEditorUtils::GetPropertyByGuid(const UUserDefinedStruct* Struct, const FGuid VarGuid)
{
    const auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
    return VarDesc ? FindField<UProperty>(Struct, VarDesc->VarName) : NULL;
}
bool FStructureEditorUtils::Is3dWidgetEnabled(const UUserDefinedStruct* Struct, FGuid VarGuid)
{
    const auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
    const auto PropertyStruct = VarDesc ? Cast<const UStruct>(VarDesc->SubCategoryObject.Get()) : NULL;
    return VarDesc && VarDesc->bEnable3dWidget && FEdMode::CanCreateWidgetForStructure(PropertyStruct);
}
FString FStructureEditorUtils::GetVariableTooltip(const UUserDefinedStruct* Struct, FGuid VarGuid)
{
    const auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
    return VarDesc ? VarDesc->ToolTip : FString();
}
FString FStructureEditorUtils::GetVariableDisplayName(const UUserDefinedStruct* Struct, FGuid VarGuid)
{
    const auto VarDesc = GetVarDescByGuid(Struct, VarGuid);
    return VarDesc ? VarDesc->FriendlyName : FString();
}