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

package com.hypixel.hytale.server.core.modules.entity.tracker;

import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.asset.type.gameplay.PlayerConfig;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.protocol.ItemArmorSlot;
import java.util.Arrays;
import com.hypixel.hytale.protocol.Equipment;
import com.hypixel.hytale.server.core.entity.LivingEntity;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSkinComponent;
import com.hypixel.hytale.protocol.ComponentUpdateType;
import com.hypixel.hytale.protocol.ComponentUpdate;
import java.util.Map;
import com.hypixel.hytale.server.core.modules.entity.component.EntityScaleComponent;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.entity.Entity;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.entity.EntityUtils;
import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSettings;
import java.util.Iterator;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.ArchetypeChunk;
import javax.annotation.Nullable;
import com.hypixel.hytale.component.SystemGroup;
import java.util.Collections;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Holder;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.entity.entities.Player;

public class LegacyEntityTrackerSystems
{
    @Deprecated
    public static boolean clear(@Nonnull final Player player, @Nonnull final Holder<EntityStore> holder) {
        final World world = player.getWorld();
        if (world != null && world.isInThread()) {
            final Ref<EntityStore> ref = player.getReference();
            return ref != null && ref.isValid() && EntityTrackerSystems.clear(ref, world.getEntityStore().getStore());
        }
        final EntityTrackerSystems.EntityViewer entityViewerComponent = holder.getComponent(EntityTrackerSystems.EntityViewer.getComponentType());
        if (entityViewerComponent == null) {
            return false;
        }
        entityViewerComponent.sent.clear();
        return true;
    }
    
    public static class LegacyLODCull extends EntityTickingSystem<EntityStore>
    {
        public static final double ENTITY_LOD_RATIO_DEFAULT = 3.5E-5;
        public static double ENTITY_LOD_RATIO;
        private final ComponentType<EntityStore, EntityTrackerSystems.EntityViewer> entityViewerComponentType;
        private final ComponentType<EntityStore, BoundingBox> boundingBoxComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        
        public LegacyLODCull(final ComponentType<EntityStore, EntityTrackerSystems.EntityViewer> entityViewerComponentType) {
            this.entityViewerComponentType = entityViewerComponentType;
            this.boundingBoxComponentType = BoundingBox.getComponentType();
            this.query = (Query<EntityStore>)Query.and(entityViewerComponentType, TransformComponent.getComponentType());
            this.dependencies = (Set<Dependency<EntityStore>>)Collections.singleton(new SystemDependency(Order.AFTER, EntityTrackerSystems.CollectVisible.class));
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityTrackerSystems.FIND_VISIBLE_ENTITIES_GROUP;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final EntityTrackerSystems.EntityViewer entityViewerComponent = archetypeChunk.getComponent(index, this.entityViewerComponentType);
            assert entityViewerComponent != null;
            final TransformComponent transformComponent = archetypeChunk.getComponent(index, TransformComponent.getComponentType());
            assert transformComponent != null;
            final Vector3d position = transformComponent.getPosition();
            final Iterator<Ref<EntityStore>> iterator = entityViewerComponent.visible.iterator();
            while (iterator.hasNext()) {
                final Ref<EntityStore> targetRef = iterator.next();
                final BoundingBox targetBoundingBoxComponent = commandBuffer.getComponent(targetRef, this.boundingBoxComponentType);
                if (targetBoundingBoxComponent == null) {
                    continue;
                }
                final TransformComponent targetTransformComponent = commandBuffer.getComponent(targetRef, TransformComponent.getComponentType());
                if (targetTransformComponent == null) {
                    continue;
                }
                final double distanceSq = targetTransformComponent.getPosition().distanceSquaredTo(position);
                final double maximumThickness = targetBoundingBoxComponent.getBoundingBox().getMaximumThickness();
                if (maximumThickness >= LegacyLODCull.ENTITY_LOD_RATIO * distanceSq) {
                    continue;
                }
                final EntityTrackerSystems.EntityViewer entityViewer = entityViewerComponent;
                ++entityViewer.lodExcludedCount;
                iterator.remove();
            }
        }
        
        static {
            LegacyLODCull.ENTITY_LOD_RATIO = 3.5E-5;
        }
    }
    
    public static class LegacyHideFromEntity extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityTrackerSystems.EntityViewer> entityViewerComponentType;
        private final ComponentType<EntityStore, PlayerSettings> playerSettingsComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        
        public LegacyHideFromEntity(final ComponentType<EntityStore, EntityTrackerSystems.EntityViewer> entityViewerComponentType) {
            this.entityViewerComponentType = entityViewerComponentType;
            this.playerSettingsComponentType = EntityModule.get().getPlayerSettingsComponentType();
            this.query = (Query<EntityStore>)Query.and(entityViewerComponentType, AllLegacyLivingEntityTypesQuery.INSTANCE);
            this.dependencies = (Set<Dependency<EntityStore>>)Collections.singleton(new SystemDependency(Order.AFTER, EntityTrackerSystems.CollectVisible.class));
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityTrackerSystems.FIND_VISIBLE_ENTITIES_GROUP;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Ref<EntityStore> viewerRef = archetypeChunk.getReferenceTo(index);
            PlayerSettings settings = archetypeChunk.getComponent(index, this.playerSettingsComponentType);
            if (settings == null) {
                settings = PlayerSettings.defaults();
            }
            final EntityTrackerSystems.EntityViewer entityViewerComponent = archetypeChunk.getComponent(index, this.entityViewerComponentType);
            assert entityViewerComponent != null;
            final Iterator<Ref<EntityStore>> iterator = entityViewerComponent.visible.iterator();
            while (iterator.hasNext()) {
                final Ref<EntityStore> ref = iterator.next();
                final Entity entity = EntityUtils.getEntity(ref, commandBuffer);
                if (entity == null) {
                    continue;
                }
                if (!entity.isHiddenFromLivingEntity(ref, viewerRef, commandBuffer) || !canHideEntities(entity, settings)) {
                    continue;
                }
                final EntityTrackerSystems.EntityViewer entityViewer = entityViewerComponent;
                ++entityViewer.hiddenCount;
                iterator.remove();
            }
        }
        
        private static boolean canHideEntities(final Entity entity, @Nonnull final PlayerSettings settings) {
            return entity instanceof Player && !settings.showEntityMarkers();
        }
    }
    
    public static class LegacyEntityModel extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityTrackerSystems.Visible> componentType;
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public LegacyEntityModel(final ComponentType<EntityStore, EntityTrackerSystems.Visible> componentType) {
            this.componentType = componentType;
            this.modelComponentType = ModelComponent.getComponentType();
            this.query = (Query<EntityStore>)Query.and(componentType, this.modelComponentType);
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityTrackerSystems.QUEUE_UPDATE_GROUP;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final EntityTrackerSystems.Visible visibleComponent = archetypeChunk.getComponent(index, this.componentType);
            assert visibleComponent != null;
            final ModelComponent modelComponent = archetypeChunk.getComponent(index, this.modelComponentType);
            assert modelComponent != null;
            float entityScale = 0.0f;
            boolean scaleOutdated = false;
            final EntityScaleComponent entityScaleComponent = archetypeChunk.getComponent(index, EntityScaleComponent.getComponentType());
            if (entityScaleComponent != null) {
                entityScale = entityScaleComponent.getScale();
                scaleOutdated = entityScaleComponent.consumeNetworkOutdated();
            }
            final boolean modelOutdated = modelComponent.consumeNetworkOutdated();
            if (modelOutdated || scaleOutdated) {
                queueUpdatesFor(archetypeChunk.getReferenceTo(index), modelComponent, entityScale, visibleComponent.visibleTo);
            }
            else if (!visibleComponent.newlyVisibleTo.isEmpty()) {
                queueUpdatesFor(archetypeChunk.getReferenceTo(index), modelComponent, entityScale, visibleComponent.newlyVisibleTo);
            }
        }
        
        private static void queueUpdatesFor(final Ref<EntityStore> ref, @Nullable final ModelComponent model, final float entityScale, @Nonnull final Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> visibleTo) {
            final ComponentUpdate update = new ComponentUpdate();
            update.type = ComponentUpdateType.Model;
            update.model = ((model != null) ? model.getModel().toPacket() : null);
            update.entityScale = entityScale;
            for (final EntityTrackerSystems.EntityViewer viewer : visibleTo.values()) {
                viewer.queueUpdate(ref, update);
            }
        }
    }
    
    public static class LegacyEntitySkin extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, PlayerSkinComponent> playerSkinComponentComponentType;
        private final ComponentType<EntityStore, EntityTrackerSystems.Visible> visibleComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public LegacyEntitySkin(final ComponentType<EntityStore, EntityTrackerSystems.Visible> visibleComponentType, final ComponentType<EntityStore, PlayerSkinComponent> playerSkinComponentComponentType) {
            this.visibleComponentType = visibleComponentType;
            this.playerSkinComponentComponentType = playerSkinComponentComponentType;
            this.query = (Query<EntityStore>)Query.and(visibleComponentType, playerSkinComponentComponentType);
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityTrackerSystems.QUEUE_UPDATE_GROUP;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final EntityTrackerSystems.Visible visibleComponent = archetypeChunk.getComponent(index, this.visibleComponentType);
            assert visibleComponent != null;
            final PlayerSkinComponent playerSkinComponent = archetypeChunk.getComponent(index, this.playerSkinComponentComponentType);
            assert playerSkinComponent != null;
            if (playerSkinComponent.consumeNetworkOutdated()) {
                queueUpdatesFor(archetypeChunk.getReferenceTo(index), playerSkinComponent, visibleComponent.visibleTo);
            }
            else if (!visibleComponent.newlyVisibleTo.isEmpty()) {
                queueUpdatesFor(archetypeChunk.getReferenceTo(index), playerSkinComponent, visibleComponent.newlyVisibleTo);
            }
        }
        
        private static void queueUpdatesFor(final Ref<EntityStore> ref, @Nonnull final PlayerSkinComponent component, @Nonnull final Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> visibleTo) {
            final ComponentUpdate update = new ComponentUpdate();
            update.type = ComponentUpdateType.PlayerSkin;
            update.skin = component.getPlayerSkin();
            for (final EntityTrackerSystems.EntityViewer viewer : visibleTo.values()) {
                viewer.queueUpdate(ref, update);
            }
        }
    }
    
    public static class LegacyEquipment extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityTrackerSystems.Visible> componentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public LegacyEquipment(final ComponentType<EntityStore, EntityTrackerSystems.Visible> componentType) {
            this.componentType = componentType;
            this.query = (Query<EntityStore>)Query.and(componentType, AllLegacyLivingEntityTypesQuery.INSTANCE);
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityTrackerSystems.QUEUE_UPDATE_GROUP;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final EntityTrackerSystems.Visible visibleComponent = archetypeChunk.getComponent(index, this.componentType);
            assert visibleComponent != null;
            final LivingEntity entity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk);
            assert entity != null;
            if (entity.consumeEquipmentNetworkOutdated()) {
                queueUpdatesFor(archetypeChunk.getReferenceTo(index), entity, visibleComponent.visibleTo);
            }
            else if (!visibleComponent.newlyVisibleTo.isEmpty()) {
                queueUpdatesFor(archetypeChunk.getReferenceTo(index), entity, visibleComponent.newlyVisibleTo);
            }
        }
        
        private static void queueUpdatesFor(@Nonnull final Ref<EntityStore> ref, @Nonnull final LivingEntity entity, @Nonnull final Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> visibleTo) {
            final ComponentUpdate update = new ComponentUpdate();
            update.type = ComponentUpdateType.Equipment;
            update.equipment = new Equipment();
            final Inventory inventory = entity.getInventory();
            final ItemContainer armor = inventory.getArmor();
            Arrays.fill(update.equipment.armorIds = new String[armor.getCapacity()], "");
            armor.forEachWithMeta((slot, itemStack, armorIds) -> armorIds[slot] = itemStack.getItemId(), update.equipment.armorIds);
            final Store<EntityStore> store = ref.getStore();
            final PlayerSettings playerSettings = store.getComponent(ref, PlayerSettings.getComponentType());
            if (playerSettings != null) {
                final PlayerConfig.ArmorVisibilityOption armorVisibilityOption = store.getExternalData().getWorld().getGameplayConfig().getPlayerConfig().getArmorVisibilityOption();
                if (armorVisibilityOption.canHideHelmet() && playerSettings.hideHelmet()) {
                    update.equipment.armorIds[ItemArmorSlot.Head.ordinal()] = "";
                }
                if (armorVisibilityOption.canHideCuirass() && playerSettings.hideCuirass()) {
                    update.equipment.armorIds[ItemArmorSlot.Chest.ordinal()] = "";
                }
                if (armorVisibilityOption.canHideGauntlets() && playerSettings.hideGauntlets()) {
                    update.equipment.armorIds[ItemArmorSlot.Hands.ordinal()] = "";
                }
                if (armorVisibilityOption.canHidePants() && playerSettings.hidePants()) {
                    update.equipment.armorIds[ItemArmorSlot.Legs.ordinal()] = "";
                }
            }
            final ItemStack itemInHand = inventory.getItemInHand();
            update.equipment.rightHandItemId = ((itemInHand != null) ? itemInHand.getItemId() : "Empty");
            final ItemStack utilityItem = inventory.getUtilityItem();
            update.equipment.leftHandItemId = ((utilityItem != null) ? utilityItem.getItemId() : "Empty");
            for (final EntityTrackerSystems.EntityViewer viewer : visibleTo.values()) {
                viewer.queueUpdate(ref, update);
            }
        }
    }
}
