// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.modules.collision;

import com.hypixel.hytale.server.core.entity.Entity;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.entity.EntityUtils;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.modules.projectile.component.Projectile;
import com.hypixel.hytale.server.core.asset.type.model.config.DetailBox;
import java.util.Map;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import java.util.Iterator;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.function.consumer.TriConsumer;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import javax.annotation.Nonnull;
import java.util.List;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import java.util.function.BiPredicate;
import javax.annotation.Nullable;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector2d;

public class EntityRefCollisionProvider
{
    protected static final int ALLOC_SIZE = 4;
    protected static final double EXTRA_DISTANCE = 8.0;
    protected EntityContactData[] contacts;
    protected EntityContactData[] sortBuffer;
    protected int count;
    protected final Vector2d minMax;
    protected final Vector3d collisionPosition;
    protected final Box tempBox;
    protected double nearestCollisionStart;
    @Nullable
    protected Vector3d position;
    @Nullable
    protected Vector3d direction;
    @Nullable
    protected Box boundingBox;
    @Nullable
    protected BiPredicate<Ref<EntityStore>, CommandBuffer<EntityStore>> entityFilter;
    @Nullable
    protected Ref<EntityStore> ignoreSelf;
    @Nullable
    protected Ref<EntityStore> ignoreOther;
    @Nonnull
    protected List<Ref<EntityStore>> tmpResults;
    @Nonnull
    protected Vector3d tmpVector;
    @Nullable
    protected String hitDetail;
    
    public EntityRefCollisionProvider() {
        this.minMax = new Vector2d();
        this.collisionPosition = new Vector3d();
        this.tempBox = new Box();
        this.tmpResults = new ObjectArrayList<Ref<EntityStore>>();
        this.tmpVector = new Vector3d();
        this.contacts = new EntityContactData[4];
        this.sortBuffer = new EntityContactData[4];
        for (int i = 0; i < this.contacts.length; ++i) {
            this.contacts[i] = new EntityContactData();
        }
    }
    
    public int getCount() {
        return this.count;
    }
    
    @Nonnull
    public EntityContactData getContact(final int i) {
        return this.contacts[i];
    }
    
    public void clear() {
        for (int i = 0; i < this.count; ++i) {
            this.contacts[i].clear();
        }
        this.count = 0;
    }
    
    public double computeNearest(@Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final Box entityBoundingBox, @Nonnull final Vector3d pos, @Nonnull final Vector3d dir, @Nullable final Ref<EntityStore> ignoreSelf, @Nullable final Ref<EntityStore> ignore) {
        return this.computeNearest(commandBuffer, pos, dir, entityBoundingBox, dir.length() + 8.0, EntityRefCollisionProvider::defaultEntityFilter, ignoreSelf, ignore);
    }
    
    public double computeNearest(@Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final Vector3d pos, @Nonnull final Vector3d dir, @Nonnull final Box boundingBox, final double radius, @Nonnull final BiPredicate<Ref<EntityStore>, CommandBuffer<EntityStore>> entityFilter, @Nullable final Ref<EntityStore> ignoreSelf, @Nullable final Ref<EntityStore> ignoreOther) {
        this.ignoreSelf = ignoreSelf;
        this.ignoreOther = ignoreOther;
        this.nearestCollisionStart = Double.MAX_VALUE;
        this.entityFilter = entityFilter;
        this.iterateEntitiesInSphere(commandBuffer, pos, dir, boundingBox, radius, EntityRefCollisionProvider::acceptNearestIgnore);
        if (this.count == 0) {
            this.nearestCollisionStart = -1.7976931348623157E308;
        }
        this.clearRefs();
        this.ignoreSelf = null;
        this.ignoreOther = null;
        return this.nearestCollisionStart;
    }
    
    protected void iterateEntitiesInSphere(@Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final Vector3d pos, @Nonnull final Vector3d dir, @Nonnull final Box boundingBox, final double radius, @Nonnull final TriConsumer<EntityRefCollisionProvider, Ref<EntityStore>, CommandBuffer<EntityStore>> consumer) {
        this.position = pos;
        this.direction = dir;
        this.boundingBox = boundingBox;
        final SpatialResource<Ref<EntityStore>, EntityStore> spatial = commandBuffer.getResource(CollisionModule.get().getTangiableEntitySpatialComponent());
        this.tmpResults.clear();
        spatial.getSpatialStructure().collect(pos, radius, this.tmpResults);
        for (final Ref<EntityStore> result : this.tmpResults) {
            consumer.accept(this, result, commandBuffer);
        }
    }
    
    protected void setContact(@Nonnull final Ref<EntityStore> ref, @Nonnull final String detailName) {
        this.collisionPosition.assign(this.position).addScaled(this.direction, this.minMax.x);
        this.contacts[0].assign(this.collisionPosition, this.minMax.x, this.minMax.y, ref, detailName);
        this.count = 1;
    }
    
    protected boolean isColliding(@Nonnull final Ref<EntityStore> ref, @Nonnull final Vector2d minMax, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        final BoundingBox boundingBoxComponent = commandBuffer.getComponent(ref, BoundingBox.getComponentType());
        assert boundingBoxComponent != null;
        final TransformComponent transformComponent = commandBuffer.getComponent(ref, TransformComponent.getComponentType());
        assert transformComponent != null;
        final Box entityBoundingBox = boundingBoxComponent.getBoundingBox();
        if (boundingBoxComponent.getDetailBoxes() != null && !boundingBoxComponent.getDetailBoxes().isEmpty()) {
            for (final Map.Entry<String, DetailBox[]> e : boundingBoxComponent.getDetailBoxes().entrySet()) {
                for (final DetailBox v : e.getValue()) {
                    this.tmpVector.assign(v.getOffset());
                    this.tmpVector.rotateY(transformComponent.getRotation().getYaw());
                    this.tmpVector.add(transformComponent.getPosition());
                    if (CollisionMath.intersectSweptAABBs(this.position, this.direction, this.boundingBox, this.tmpVector, v.getBox(), minMax, this.tempBox) && minMax.x <= 1.0) {
                        this.hitDetail = e.getKey();
                        return true;
                    }
                }
            }
            this.hitDetail = null;
            return false;
        }
        this.hitDetail = null;
        return CollisionMath.intersectSweptAABBs(this.position, this.direction, this.boundingBox, transformComponent.getPosition(), entityBoundingBox, minMax, this.tempBox) && minMax.x <= 1.0;
    }
    
    protected void clearRefs() {
        this.position = null;
        this.direction = null;
        this.boundingBox = null;
        this.entityFilter = null;
    }
    
    public static boolean defaultEntityFilter(@Nonnull final Ref<EntityStore> entity, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        if (!entity.isValid()) {
            return false;
        }
        final Archetype<EntityStore> archetype = commandBuffer.getArchetype(entity);
        if (archetype.contains(Projectile.getComponentType())) {
            return false;
        }
        if (archetype.contains(DeathComponent.getComponentType())) {
            return false;
        }
        final Entity legacy = EntityUtils.getEntity(entity, commandBuffer);
        return legacy == null || legacy.isCollidable();
    }
    
    protected void acceptNearestIgnore(@Nonnull final Ref<EntityStore> entity, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        if (this.entityFilter.test(entity, commandBuffer) && !entity.equals(this.ignoreSelf) && !entity.equals(this.ignoreOther) && this.isColliding(entity, this.minMax, commandBuffer) && this.minMax.x < this.nearestCollisionStart) {
            this.nearestCollisionStart = this.minMax.x;
            this.setContact(entity, this.hitDetail);
        }
    }
}
