// Oleg Petruny proprietary.

#pragma once

#include "Components/PrimitiveComponent.h"

#include "CollisionSceneComponent.generated.h"

// Modified implementation of the UPrimitiveComponent
UCLASS(ClassGroup = (Utility, Common), BlueprintType, hideCategories = (Trigger, PhysicsVolume), meta = (BlueprintSpawnableComponent, IgnoreCategoryKeywordsInSubclasses, ShortTooltip = "A Scene Component that use targeted collisions."), MinimalAPI)
class UCollisionSceneComponent : public UPrimitiveComponent
{
    GENERATED_BODY()

public:
    virtual bool MoveComponentImpl(const FVector& Delta, const FQuat& NewRotation, bool bSweep, FHitResult* OutHit = NULL, EMoveComponentFlags MoveFlags = MOVECOMP_NoFlags, ETeleportType Teleport = ETeleportType::None) override;

protected:
    virtual void BeginPlay() override;

    /** Convert a set of overlaps from a sweep to a subset that includes only those at the end location (filling in OverlapsAtEndLocation). */
    template<typename AllocatorType>
    bool ConvertSweptOverlapsToCurrentOverlaps(TArray<FOverlapInfo, AllocatorType>& OutOverlapsAtEndLocation, const TOverlapArrayView& SweptOverlaps, int32 SweptOverlapsIndex, const FVector& EndLocation, const FQuat& EndRotationQuat);

    /** Convert a set of overlaps from a symmetric change in rotation to a subset that includes only those at the end location (filling in OverlapsAtEndLocation). */
    template<typename AllocatorType>
    bool ConvertRotationOverlapsToCurrentOverlaps(TArray<FOverlapInfo, AllocatorType>& OutOverlapsAtEndLocation, const TOverlapArrayView& CurrentOverlaps);

    template<typename AllocatorType>
    bool GetOverlapsWithActor_Template(const AActor* Actor, TArray<FOverlapInfo, AllocatorType>& OutOverlaps) const;

    // FScopedMovementUpdate needs access to the above two functions.
    friend FScopedMovementUpdate;

    TArray<UPrimitiveComponent*> targetCollisions;
};

template<typename AllocatorType>
bool UCollisionSceneComponent::ConvertSweptOverlapsToCurrentOverlaps(
    TArray<FOverlapInfo, AllocatorType>& OverlapsAtEndLocation, const TOverlapArrayView& SweptOverlaps, int32 SweptOverlapsIndex,
    const FVector& EndLocation, const FQuat& EndRotationQuat)
{
    checkSlow(SweptOverlapsIndex >= 0);

    bool bResult = false;
    const bool bForceGatherOverlaps = !ShouldCheckOverlapFlagToQueueOverlaps(*this);
    if((GetGenerateOverlapEvents() || bForceGatherOverlaps) && PrimitiveComponentCVars::bAllowCachedOverlapsCVar)
    {
        const AActor* Actor = GetOwner();
        if(Actor && Actor->GetRootComponent() == this)
        {
            // We know we are not overlapping any new components at the end location. Children are ignored here (see note below).
            if(PrimitiveComponentCVars::bEnableFastOverlapCheck)
            {
                SCOPE_CYCLE_COUNTER(STAT_MoveComponent_FastOverlap);

                // Check components we hit during the sweep, keep only those still overlapping
                const FCollisionQueryParams UnusedQueryParams(NAME_None, FCollisionQueryParams::GetUnknownStatId());
                const int32 NumSweptOverlaps = SweptOverlaps.Num();
                OverlapsAtEndLocation.Reserve(OverlapsAtEndLocation.Num() + NumSweptOverlaps);
                for(int32 Index = SweptOverlapsIndex; Index < NumSweptOverlaps; ++Index)
                {
                    const FOverlapInfo& OtherOverlap = SweptOverlaps[Index];
                    UPrimitiveComponent* OtherPrimitive = OtherOverlap.OverlapInfo.GetComponent();
                    if(OtherPrimitive && (OtherPrimitive->GetGenerateOverlapEvents() || bForceGatherOverlaps))
                    {
                        if(OtherPrimitive->bMultiBodyOverlap)
                        {
                            // Not handled yet. We could do it by checking every body explicitly and track each body index in the overlap test, but this seems like a rare need.
                            return false;
                        }
                        else if(Cast<USkeletalMeshComponent>(OtherPrimitive) || Cast<USkeletalMeshComponent>(this))
                        {
                            // SkeletalMeshComponent does not support this operation, and would return false in the test when an actual query could return true.
                            return false;
                        }
                        else if(OtherPrimitive->ComponentOverlapComponent(this, EndLocation, EndRotationQuat, UnusedQueryParams))
                        {
                            OverlapsAtEndLocation.Add(OtherOverlap);
                        }
                    }
                }

                // Note: we don't worry about adding any child components here, because they are not included in the sweep results.
                // Children test for their own overlaps after we update our own, and we ignore children in our own update.
                checkfSlow(OverlapsAtEndLocation.FindByPredicate(FPredicateOverlapHasSameActor(*Actor)) == nullptr,
                    TEXT("Child overlaps should not be included in the SweptOverlaps() array in UPrimitiveComponent::ConvertSweptOverlapsToCurrentOverlaps()."));

                bResult = true;
            }
            else
            {
                if(SweptOverlaps.Num() == 0 && AreAllCollideableDescendantsRelative())
                {
                    // Add overlaps with components in this actor.
                    GetOverlapsWithActor_Template(Actor, OverlapsAtEndLocation);
                    bResult = true;
                }
            }
        }
    }

    return bResult;
}

template<typename AllocatorType>
bool UCollisionSceneComponent::ConvertRotationOverlapsToCurrentOverlaps(TArray<FOverlapInfo, AllocatorType>& OutOverlapsAtEndLocation, const TOverlapArrayView& CurrentOverlaps)
{
    bool bResult = false;
    const bool bForceGatherOverlaps = !ShouldCheckOverlapFlagToQueueOverlaps(*this);
    if((GetGenerateOverlapEvents() || bForceGatherOverlaps) && PrimitiveComponentCVars::bAllowCachedOverlapsCVar)
    {
        const AActor* Actor = GetOwner();
        if(Actor && Actor->GetRootComponent() == this)
        {
            if(PrimitiveComponentCVars::bEnableFastOverlapCheck)
            {
                // Add all current overlaps that are not children. Children test for their own overlaps after we update our own, and we ignore children in our own update.
                OutOverlapsAtEndLocation.Reserve(OutOverlapsAtEndLocation.Num() + CurrentOverlaps.Num());
                Algo::CopyIf(CurrentOverlaps, OutOverlapsAtEndLocation, FPredicateOverlapHasDifferentActor(*Actor));
                bResult = true;
            }
        }
    }

    return bResult;
}

template<typename AllocatorType>
bool UCollisionSceneComponent::GetOverlapsWithActor_Template(const AActor* Actor, TArray<FOverlapInfo, AllocatorType>& OutOverlaps) const
{
    const int32 InitialCount = OutOverlaps.Num();
    if(Actor)
    {
        for(int32 OverlapIdx = 0; OverlapIdx < OverlappingComponents.Num(); ++OverlapIdx)
        {
            UPrimitiveComponent const* const PrimComp = OverlappingComponents[OverlapIdx].OverlapInfo.Component.Get();
            if(PrimComp && (PrimComp->GetOwner() == Actor))
            {
                OutOverlaps.Add(OverlappingComponents[OverlapIdx]);
            }
        }
    }

    return InitialCount != OutOverlaps.Num();
}
