void UAblRayCastQueryTask::OnTaskTick(const TWeakObjectPtr<const UAblAbilityContext>& Context, float deltaTime) const
{
	Super::OnTaskTick(Context, deltaTime);

	if (IsAsyncFriendly() && UAbleSettings::IsAsyncEnabled())
	{
		UAblRayCastQueryTaskScratchPad* ScratchPad = Cast<UAblRayCastQueryTaskScratchPad>(Context->GetScratchPadForTask(this));
		check(ScratchPad);
		if (!ScratchPad->AsyncProcessed && ScratchPad->AsyncHandle._Handle != 0)
		{
			AActor* SourceActor = m_QueryLocation.GetSourceActor(*Context.Get());
			check(SourceActor);

			UWorld* World = SourceActor->GetWorld();

			FTraceDatum Datum;
			if (World->QueryTraceData(ScratchPad->AsyncHandle, Datum))
			{
				if (m_CopyResultsToContext)
				{
					CopyResultsToContext(Datum.OutHits, Context);
				}

				if (m_FireEvent)
				{
					Context->GetAbility()->OnRaycastEvent(Context.Get(), m_Name, Datum.OutHits);
				}

				ScratchPad->AsyncProcessed = true;
			}

		}
	}
}
bool UAblRayCastQueryTask::IsDone(const TWeakObjectPtr<const UAblAbilityContext>& Context) const
{
	if (IsAsyncFriendly() && UAbleSettings::IsAsyncEnabled())
	{
		UAblRayCastQueryTaskScratchPad* ScratchPad = Cast<UAblRayCastQueryTaskScratchPad>(Context->GetScratchPadForTask(this));
		check(ScratchPad);
		return ScratchPad->AsyncProcessed;
	}
	else
	{
		return UAblAbilityTask::IsDone(Context);
	}
}
void UAblSetShaderParameterTask::OnTaskStart(const TWeakObjectPtr<const UAblAbilityContext>& Context) const
{
	Super::OnTaskStart(Context);

	if (!m_Value)
	{
		UE_LOG(LogAble, Warning, TEXT("No Value set for SetShaderParameter in Ability [%s]"), *Context->GetAbility()->GetDisplayName());
		return;
	}

	// We need to convert our Actors to primitive components.
	TArray<TWeakObjectPtr<AActor>> TargetArray;
	GetActorsForTask(Context, TargetArray);

	TArray<TWeakObjectPtr<UPrimitiveComponent>> PrimitiveComponents;

	for (TWeakObjectPtr<AActor>& Target : TargetArray)
	{
		for (UActorComponent* Component : Target->GetComponentsByClass(UPrimitiveComponent::StaticClass()))
		{
			PrimitiveComponents.Add(Cast<UPrimitiveComponent>(Component));
		}
	}

	UAblSetShaderParameterTaskScratchPad* ScratchPad = Cast<UAblSetShaderParameterTaskScratchPad>(Context->GetScratchPadForTask(this));
	check(ScratchPad);

	ScratchPad->BlendIn = m_BlendIn;
	ScratchPad->BlendIn.Reset();

	ScratchPad->BlendOut = m_BlendOut;
	ScratchPad->BlendOut.Reset();

	UAblSetParameterValue* CachedValue = nullptr;
	for (TWeakObjectPtr<UPrimitiveComponent>& PrimitiveComponent : PrimitiveComponents)
	{
		if (PrimitiveComponent.IsValid())
		{
			for (int32 i = 0; i < PrimitiveComponent->GetNumMaterials(); ++i)
			{
				UMaterialInterface* MaterialInterface = PrimitiveComponent->GetMaterial(i);
				UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(MaterialInterface);

				// If our material currently isn't dynamic, but we have the parameter we're looking for - instantiate a dynamic version.
				if (!DynamicMaterial && CheckMaterialHasParameter(MaterialInterface))
				{
					DynamicMaterial = PrimitiveComponent->CreateDynamicMaterialInstance(i, MaterialInterface);
				}

				if (DynamicMaterial)
				{
					CachedValue = CacheShaderValue(ScratchPad, DynamicMaterial);
					if (CachedValue)
					{
						ScratchPad->DynamicMaterials.Add(DynamicMaterial);
						ScratchPad->PreviousValues.Add(CachedValue);

						if (ScratchPad->BlendIn.IsComplete())
						{
							// If there isn't any blend. Just set it here since we won't be ticking.
							InternalSetShaderValue(DynamicMaterial, m_Value, CachedValue, ScratchPad->BlendIn.GetAlpha());
						}
					}
				}
			}
		}
	}

}
void UAblSetShaderParameterTask::OnTaskEnd(const TWeakObjectPtr<const UAblAbilityContext>& Context, const EAblAbilityTaskResult Result) const
{
	Super::OnTaskEnd(Context, Result);

	if (m_RestoreValueOnEnd)
	{
		UAblSetShaderParameterTaskScratchPad* ScratchPad = Cast<UAblSetShaderParameterTaskScratchPad>(Context->GetScratchPadForTask(this));
		check(ScratchPad);

		verify(ScratchPad->DynamicMaterials.Num() == ScratchPad->PreviousValues.Num());
		for (int32 i = 0; i < ScratchPad->DynamicMaterials.Num(); ++i)
		{
			InternalSetShaderValue(ScratchPad->DynamicMaterials[i].Get(), ScratchPad->PreviousValues[i].Get(), m_Value, 1.0);
		}
	}

}
void UAblSetShaderParameterTask::OnTaskTick(const TWeakObjectPtr<const UAblAbilityContext>& Context, float deltaTime) const
{
	Super::OnTaskTick(Context, deltaTime);

	UAblSetShaderParameterTaskScratchPad* ScratchPad = Cast<UAblSetShaderParameterTaskScratchPad>(Context->GetScratchPadForTask(this));
	check(ScratchPad);

	if (!ScratchPad->BlendIn.IsComplete())
	{
		ScratchPad->BlendIn.Update(deltaTime);

		verify(ScratchPad->DynamicMaterials.Num() == ScratchPad->PreviousValues.Num());
		for (int32 i = 0; i < ScratchPad->DynamicMaterials.Num(); ++i)
		{
			InternalSetShaderValue(ScratchPad->DynamicMaterials[i].Get(), m_Value, ScratchPad->PreviousValues[i].Get(), ScratchPad->BlendIn.GetBlendedValue());
		}
	}
	else if (m_RestoreValueOnEnd && !ScratchPad->BlendOut.IsComplete())
	{
		// If we're within range to start blending out, go ahead and start that process.
		if (GetEndTime() - Context->GetCurrentTime() < ScratchPad->BlendOut.GetBlendTime())
		{
			ScratchPad->BlendOut.Update(deltaTime);
			
			verify(ScratchPad->DynamicMaterials.Num() == ScratchPad->PreviousValues.Num());
			for (int32 i = 0; i < ScratchPad->DynamicMaterials.Num(); ++i)
			{
				InternalSetShaderValue(ScratchPad->DynamicMaterials[i].Get(), ScratchPad->PreviousValues[i].Get(), m_Value, ScratchPad->BlendOut.GetBlendedValue());
			}
		}
	}
}
void UAblSetCollisionChannelResponseTask::OnTaskStart(const TWeakObjectPtr<const UAblAbilityContext>& Context) const
{
	Super::OnTaskStart(Context);

	UAblSetCollisionChannelResponseTaskScratchPad* ScratchPad = nullptr;
	
	if (m_RestoreOnEnd)
	{
		ScratchPad = Cast<UAblSetCollisionChannelResponseTaskScratchPad>(Context->GetScratchPadForTask(this));
		check(ScratchPad);
	}

	// We need to convert our Actors to primitive components.
	TArray<TWeakObjectPtr<AActor>> TargetArray;
	GetActorsForTask(Context, TargetArray);

	TArray<TWeakObjectPtr<UPrimitiveComponent>> PrimitiveComponents;

	for (TWeakObjectPtr<AActor>& Target : TargetArray)
	{
		if (UPrimitiveComponent* PrimitiveComponent = Cast<UPrimitiveComponent>(Target->GetRootComponent()))
		{
			PrimitiveComponents.AddUnique(PrimitiveComponent);
		}
	}

	for (TWeakObjectPtr<UPrimitiveComponent>& Component : PrimitiveComponents)
	{
		if (Component.IsValid())
		{
			if (m_RestoreOnEnd)
			{
				if (m_SetAllChannelsToResponse)
				{
					const FCollisionResponseContainer& Container = Component->GetCollisionResponseToChannels();
					for (ECollisionChannel Channel : TEnumRange<ECollisionChannel>())
					{
						ScratchPad->PreviousCollisionValues.Add(FCollisionLayerResponseEntry(Component.Get(), Channel, Container.GetResponse(Channel)));
					}
				}
				else
				{
					ScratchPad->PreviousCollisionValues.Add(FCollisionLayerResponseEntry(Component.Get(), m_Channel.GetValue(), Component->GetCollisionResponseToChannel(m_Channel)));
				}
			}

			if (m_SetAllChannelsToResponse)
			{
#if !(UE_BUILD_SHIPPING)
				if (IsVerbose())
				{
					PrintVerbose(FString::Printf(TEXT("Setting All Collision Responses on Actor %s to %s."), *Component->GetOwner()->GetName(), *FAbleLogHelper::GetCollisionResponseEnumAsString(m_Response.GetValue())));
				}
#endif
				Component->SetCollisionResponseToAllChannels(m_Response.GetValue());
			}
			else
			{
#if !(UE_BUILD_SHIPPING)
				if (IsVerbose())
				{
					PrintVerbose(FString::Printf(TEXT("Setting Collision Channel %s Response on Actor %s to %s."), *FAbleLogHelper::GetCollisionChannelEnumAsString(m_Channel.GetValue()), *Component->GetOwner()->GetName(), *FAbleLogHelper::GetCollisionResponseEnumAsString(m_Response.GetValue())));
				}
#endif
				Component->SetCollisionResponseToChannel(m_Channel.GetValue(), m_Response.GetValue());
			}
		}
	}
}
void UAblSetCollisionChannelResponseTask::OnTaskEnd(const TWeakObjectPtr<const UAblAbilityContext>& Context, const EAblAbilityTaskResult result) const
{
	Super::OnTaskEnd(Context, result);

	if (m_RestoreOnEnd)
	{
		UAblSetCollisionChannelResponseTaskScratchPad* ScratchPad = Cast<UAblSetCollisionChannelResponseTaskScratchPad>(Context->GetScratchPadForTask(this));
		check(ScratchPad);

		for (const FCollisionLayerResponseEntry& Entry : ScratchPad->PreviousCollisionValues)
		{
			if (Entry.Primitive.IsValid())
			{
#if !(UE_BUILD_SHIPPING)
				if (IsVerbose())
				{
					PrintVerbose(FString::Printf(TEXT("Setting Collision Channel %s Response on Actor %s to %s."), *FAbleLogHelper::GetCollisionChannelEnumAsString(Entry.Channel), *Entry.Primitive->GetOwner()->GetName(), *FAbleLogHelper::GetCollisionResponseEnumAsString(Entry.Response)));
				}
#endif
				Entry.Primitive->SetCollisionResponseToChannel(Entry.Channel, Entry.Response);
			}
		}
	}
}
void UAblRayCastQueryTask::OnTaskStart(const TWeakObjectPtr<const UAblAbilityContext>& Context) const
{
	Super::OnTaskStart(Context);

	AActor* SourceActor = m_QueryLocation.GetSourceActor(*Context.Get());
	check(SourceActor);

	UWorld* World = SourceActor->GetWorld();

	FTransform QueryTransform;
	m_QueryLocation.GetTransform(*Context.Get(), QueryTransform);

	const FVector RayStart = QueryTransform.GetLocation();
	const FVector RayEnd = RayStart + QueryTransform.GetRotation().GetForwardVector() * m_Length;

	if (m_UseAsyncQuery && UAbleSettings::IsAsyncEnabled())
	{
		UAblRayCastQueryTaskScratchPad* ScratchPad = Cast<UAblRayCastQueryTaskScratchPad>(Context->GetScratchPadForTask(this));
		check(ScratchPad);
		if (m_OnlyReturnBlockingHit)
		{
			ScratchPad->AsyncHandle = World->AsyncLineTraceByChannel(EAsyncTraceType::Single, RayStart, RayEnd, m_CollisionChannel);
		}
		else
		{
			ScratchPad->AsyncHandle = World->AsyncLineTraceByChannel(EAsyncTraceType::Multi, RayStart, RayEnd, m_CollisionChannel);
		}
	}
	else
	{
		TArray<FHitResult> HitResults;
		FHitResult TraceResult;
		if (m_OnlyReturnBlockingHit)
		{
			if (World->LineTraceSingleByChannel(TraceResult, RayStart, RayEnd, m_CollisionChannel))
			{
				HitResults.Add(TraceResult);
			}
		}
		else
		{
			World->LineTraceMultiByChannel(HitResults, RayStart, RayEnd, m_CollisionChannel);		
		}

#if !(UE_BUILD_SHIPPING)
		if (IsVerbose())
		{
			PrintVerbose(FString::Printf(TEXT("Raycast found %d results."), HitResults.Num()));
		}
#endif

		if (HitResults.Num())
		{
#if !(UE_BUILD_SHIPPING)
			if (IsVerbose())
			{
				// Quick distance print help to see if we hit ourselves.
				float DistanceToBlocker = HitResults[HitResults.Num() - 1].Distance;
				PrintVerbose(FString::Printf(TEXT("Raycast blocking hit distance: %4.2f."), DistanceToBlocker));
			}
#endif

			if (m_CopyResultsToContext)
			{
#if !(UE_BUILD_SHIPPING)
				if (IsVerbose())
				{
					PrintVerbose(FString::Printf(TEXT("Copying %d results into Context."), HitResults.Num()));
				}
#endif
				CopyResultsToContext(HitResults, Context);
			}

			if (m_FireEvent)
			{
#if !(UE_BUILD_SHIPPING)
				if (IsVerbose())
				{
					PrintVerbose(FString::Printf(TEXT("Firing Raycast Event %s with %d results."), *m_Name.ToString(), HitResults.Num()));
				}
#endif
				Context->GetAbility()->OnRaycastEvent(Context.Get(), m_Name, HitResults);
			}
		}
	}

#if !UE_BUILD_SHIPPING
	if (FAblAbilityDebug::ShouldDrawQueries())
	{
		FAblAbilityDebug::DrawRaycastQuery(World, QueryTransform, m_Length);
	}
#endif
}