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

package com.hypixel.hytale.server.npc.systems;

import java.util.function.Supplier;
import com.hypixel.hytale.server.npc.role.support.EntityList;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.spawning.SpawningPlugin;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.protocol.BlockMaterial;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.entity.LivingEntity;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.OrderPriority;
import com.hypixel.hytale.server.core.modules.entity.system.PlayerSpatialSystem;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.common.collection.BucketItemPool;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.flock.FlockMembership;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.RefChangeSystem;
import java.util.Iterator;
import java.util.List;
import com.hypixel.hytale.server.npc.statetransition.StateTransitionController;
import com.hypixel.hytale.server.npc.instructions.Instruction;
import com.hypixel.hytale.server.npc.role.support.PositionCache;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.decisionmaker.stateevaluator.StateEvaluator;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.role.Role;

public class PositionCacheSystems
{
    public static void initialisePositionCache(@Nonnull final Role role, @Nullable final StateEvaluator stateEvaluator, final double flockInfluenceRange) {
        final PositionCache positionCache = role.getPositionCache();
        positionCache.reset(true);
        if (role.isAvoidingEntities()) {
            final double collisionProbeDistance = role.getCollisionProbeDistance();
            positionCache.requireEntityDistanceAvoidance(collisionProbeDistance);
            positionCache.requirePlayerDistanceAvoidance(collisionProbeDistance);
        }
        if (role.isApplySeparation()) {
            final double separationDistance = role.getSeparationDistance();
            positionCache.requireEntityDistanceAvoidance(separationDistance);
            positionCache.requirePlayerDistanceAvoidance(separationDistance);
        }
        if (flockInfluenceRange > 0.0) {
            positionCache.requireEntityDistanceAvoidance(flockInfluenceRange);
            positionCache.requirePlayerDistanceAvoidance(flockInfluenceRange);
        }
        final Instruction instruction = role.getRootInstruction();
        instruction.registerWithSupport(role);
        final Instruction interactionInstruction = role.getInteractionInstruction();
        if (interactionInstruction != null) {
            interactionInstruction.registerWithSupport(role);
            positionCache.requirePlayerDistanceUnsorted(10.0);
        }
        final Instruction deathInstruction = role.getDeathInstruction();
        if (deathInstruction != null) {
            deathInstruction.registerWithSupport(role);
        }
        final StateTransitionController stateTransitions = role.getStateSupport().getStateTransitionController();
        if (stateTransitions != null) {
            stateTransitions.registerWithSupport(role);
        }
        if (stateEvaluator != null) {
            stateEvaluator.setupNPC(role);
        }
        final List<Consumer<Role>> externalRegistrations = positionCache.getExternalRegistrations();
        for (final Consumer<Role> registration : externalRegistrations) {
            registration.accept(role);
        }
        positionCache.finalizeConfiguration();
    }
    
    public static class OnFlockJoinSystem extends RefChangeSystem<EntityStore, FlockMembership>
    {
        @Nonnull
        private final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType;
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, StateEvaluator> stateEvaluatorComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public OnFlockJoinSystem(@Nonnull final ComponentType<EntityStore, NPCEntity> npcComponentType, @Nonnull final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType) {
            this.flockMembershipComponentType = flockMembershipComponentType;
            this.npcComponentType = npcComponentType;
            this.stateEvaluatorComponentType = StateEvaluator.getComponentType();
            this.query = (Query<EntityStore>)Archetype.of(npcComponentType, flockMembershipComponentType);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, FlockMembership> componentType() {
            return this.flockMembershipComponentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final FlockMembership component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            final Role role = npcComponent.getRole();
            PositionCacheSystems.initialisePositionCache(role, store.getComponent(ref, this.stateEvaluatorComponentType), role.getFlockInfluenceRange());
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final FlockMembership oldComponent, @Nonnull final FlockMembership newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            final Role role = npcComponent.getRole();
            PositionCacheSystems.initialisePositionCache(role, store.getComponent(ref, this.stateEvaluatorComponentType), role.getFlockInfluenceRange());
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final FlockMembership component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
    }
    
    public static class RoleActivateSystem extends HolderSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, StateEvaluator> stateEvaluatorComponentType;
        
        public RoleActivateSystem(@Nonnull final ComponentType<EntityStore, NPCEntity> npcComponentType, @Nonnull final ComponentType<EntityStore, StateEvaluator> stateEvaluatorComponentType) {
            this.npcComponentType = npcComponentType;
            this.stateEvaluatorComponentType = stateEvaluatorComponentType;
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            final NPCEntity npcComponent = holder.getComponent(this.npcComponentType);
            assert npcComponent != null;
            final Role role = npcComponent.getRole();
            double influenceRadius;
            if (holder.getComponent(FlockMembership.getComponentType()) != null) {
                influenceRadius = role.getFlockInfluenceRange();
            }
            else {
                influenceRadius = 0.0;
            }
            final StateEvaluator stateEvaluator = holder.getComponent(this.stateEvaluatorComponentType);
            if (stateEvaluator != null) {
                stateEvaluator.setupNPC(holder);
            }
            PositionCacheSystems.initialisePositionCache(role, stateEvaluator, influenceRadius);
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
            final NPCEntity npcComponent = holder.getComponent(this.npcComponentType);
            assert npcComponent != null;
            npcComponent.getRole().getPositionCache().reset(false);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.npcComponentType;
        }
    }
    
    public static class UpdateSystem extends SteppableTickingSystem
    {
        @Nonnull
        private static final ThreadLocal<BucketItemPool<Ref<EntityStore>>> BUCKET_POOL_THREAD_LOCAL;
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        @Nonnull
        private final ComponentType<EntityStore, TransformComponent> transformComponentType;
        @Nonnull
        private final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> playerSpatialResource;
        @Nonnull
        private final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> npcSpatialResource;
        @Nonnull
        private final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> itemSpatialResource;
        @Nonnull
        private final Query<EntityStore> query;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        
        public UpdateSystem(@Nonnull final ComponentType<EntityStore, NPCEntity> npcComponentType, @Nonnull final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> npcSpatialResource) {
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, PlayerSpatialSystem.class, OrderPriority.CLOSEST), new SystemDependency(Order.BEFORE, RoleSystems.PreBehaviourSupportTickSystem.class));
            this.npcComponentType = npcComponentType;
            this.modelComponentType = ModelComponent.getComponentType();
            this.transformComponentType = TransformComponent.getComponentType();
            this.playerSpatialResource = EntityModule.get().getPlayerSpatialResourceType();
            this.npcSpatialResource = npcSpatialResource;
            this.itemSpatialResource = EntityModule.get().getItemSpatialResourceType();
            this.query = (Query<EntityStore>)Query.and(npcComponentType, this.transformComponentType, this.modelComponentType);
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return false;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public void steppedTick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
            final NPCEntity npcComponent = archetypeChunk.getComponent(index, this.npcComponentType);
            assert npcComponent != null;
            final Role role = npcComponent.getRole();
            final PositionCache positionCache = role.getPositionCache();
            positionCache.setBenchmarking(NPCPlugin.get().isBenchmarkingSensorSupport());
            final long packed = LivingEntity.getPackedMaterialAndFluidAtBreathingHeight(ref, commandBuffer);
            final BlockMaterial material = BlockMaterial.VALUES[MathUtil.unpackLeft(packed)];
            final int fluidId = MathUtil.unpackRight(packed);
            positionCache.setCouldBreathe(role.canBreathe(material, fluidId));
            if (!positionCache.tickPositionCacheNextUpdate(dt)) {
                return;
            }
            positionCache.resetPositionCacheNextUpdate();
            final TransformComponent transformComponent = archetypeChunk.getComponent(index, this.transformComponentType);
            assert transformComponent != null;
            final Vector3d position = transformComponent.getPosition();
            final EntityList players = positionCache.getPlayers();
            if (players.getSearchRadius() > 0) {
                final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(this.playerSpatialResource);
                players.setBucketItemPool(UpdateSystem.BUCKET_POOL_THREAD_LOCAL.get());
                if (positionCache.isBenchmarking()) {
                    final long startTime = java.lang.System.nanoTime();
                    addEntities(ref, position, players, spatialResource, commandBuffer);
                    final long getTime = java.lang.System.nanoTime();
                    NPCPlugin.get().collectSensorSupportPlayerList(role.getRoleIndex(), getTime - startTime, players.getMaxDistanceSorted(), players.getMaxDistanceUnsorted(), players.getMaxDistanceAvoidance(), 0);
                }
                else {
                    addEntities(ref, position, players, spatialResource, commandBuffer);
                }
            }
            final EntityList npcEntities = positionCache.getNpcs();
            if (npcEntities.getSearchRadius() > 0) {
                final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource2 = store.getResource(this.npcSpatialResource);
                npcEntities.setBucketItemPool(UpdateSystem.BUCKET_POOL_THREAD_LOCAL.get());
                if (positionCache.isBenchmarking()) {
                    final long startTime2 = java.lang.System.nanoTime();
                    addEntities(ref, position, npcEntities, spatialResource2, commandBuffer);
                    final long getTime2 = java.lang.System.nanoTime();
                    NPCPlugin.get().collectSensorSupportEntityList(role.getRoleIndex(), getTime2 - startTime2, npcEntities.getMaxDistanceSorted(), npcEntities.getMaxDistanceUnsorted(), npcEntities.getMaxDistanceAvoidance(), 0);
                }
                else {
                    addEntities(ref, position, npcEntities, spatialResource2, commandBuffer);
                }
            }
            final double maxDroppedItemDistance = positionCache.getMaxDroppedItemDistance();
            if (maxDroppedItemDistance > 0.0) {
                final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource3 = store.getResource(this.itemSpatialResource);
                final List<Ref<EntityStore>> list = positionCache.getDroppedItemList();
                list.clear();
                spatialResource3.getSpatialStructure().ordered(position, (int)maxDroppedItemDistance + 1, list);
            }
            final double maxSpawnMarkerDistance = positionCache.getMaxSpawnMarkerDistance();
            if (maxSpawnMarkerDistance > 0.0) {
                final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource4 = store.getResource(SpawningPlugin.get().getSpawnMarkerSpatialResource());
                final List<Ref<EntityStore>> list2 = positionCache.getSpawnMarkerList();
                list2.clear();
                spatialResource4.getSpatialStructure().collect(position, (int)maxSpawnMarkerDistance + 1, list2);
            }
            final int maxSpawnBeaconDistance = positionCache.getMaxSpawnBeaconDistance();
            if (maxSpawnBeaconDistance > 0) {
                final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource5 = store.getResource(SpawningPlugin.get().getManualSpawnBeaconSpatialResource());
                final List<Ref<EntityStore>> list3 = positionCache.getSpawnBeaconList();
                list3.clear();
                spatialResource5.getSpatialStructure().ordered(position, maxSpawnBeaconDistance + 1, list3);
            }
        }
        
        private static void addEntities(@Nonnull final Ref<EntityStore> self, @Nonnull final Vector3d position, @Nonnull final EntityList entityList, @Nonnull final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final List<Ref<EntityStore>> results = (List<Ref<EntityStore>>)SpatialResource.getThreadLocalReferenceList();
            spatialResource.getSpatialStructure().collect(position, entityList.getSearchRadius(), results);
            for (final Ref<EntityStore> result : results) {
                if (result.isValid()) {
                    if (result.equals(self)) {
                        continue;
                    }
                    entityList.add(result, position, commandBuffer);
                }
            }
        }
        
        static {
            BUCKET_POOL_THREAD_LOCAL = ThreadLocal.withInitial((Supplier<? extends BucketItemPool<Ref<EntityStore>>>)BucketItemPool::new);
        }
    }
}
