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

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

import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.system.RefChangeSystem;
import java.util.Map;
import com.hypixel.hytale.protocol.ComponentUpdateType;
import com.hypixel.hytale.protocol.ComponentUpdate;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems;
import com.hypixel.hytale.server.core.entity.EntityUtils;
import com.hypixel.hytale.server.core.entity.InteractionChain;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.protocol.InteractionType;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause;
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
import com.hypixel.hytale.server.core.modules.entitystats.asset.DefaultEntityStatTypes;
import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType;
import it.unimi.dsi.fastutil.floats.FloatList;
import com.hypixel.hytale.protocol.EntityStatUpdate;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.server.core.entity.InteractionManager;
import com.hypixel.hytale.server.core.modules.interaction.InteractionModule;
import com.hypixel.hytale.component.SystemType;
import com.hypixel.hytale.component.dependency.SystemTypeDependency;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import java.util.Iterator;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import java.time.Instant;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.modules.entity.component.Invulnerable;
import java.util.List;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.modules.time.TimeResource;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.server.core.entity.LivingEntity;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Holder;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.HolderSystem;

public class EntityStatsSystems
{
    public static class Setup extends HolderSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityStatMap> componentType;
        
        public Setup(final ComponentType<EntityStore, EntityStatMap> componentType) {
            this.componentType = componentType;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return AllLegacyLivingEntityTypesQuery.INSTANCE;
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            EntityStatMap stats = holder.getComponent(this.componentType);
            if (stats == null) {
                stats = holder.ensureAndGetComponent(this.componentType);
                stats.update();
            }
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
    }
    
    public static class Regenerate<EntityType extends LivingEntity> extends EntityTickingSystem<EntityStore> implements StatModifyingSystem
    {
        private final ComponentType<EntityStore, EntityStatMap> componentType;
        private final ComponentType<EntityStore, EntityType> entityTypeComponent;
        private final Query<EntityStore> query;
        
        public Regenerate(final ComponentType<EntityStore, EntityStatMap> componentType, final ComponentType<EntityStore, EntityType> entityTypeComponent) {
            this.componentType = componentType;
            this.entityTypeComponent = entityTypeComponent;
            this.query = (Query<EntityStore>)Query.and(componentType, entityTypeComponent);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return false;
        }
        
        @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> ref = archetypeChunk.getReferenceTo(index);
            final EntityStatMap map = archetypeChunk.getComponent(index, this.componentType);
            assert map != null;
            final Instant now = store.getResource(TimeResource.getResourceType()).getNow();
            final int size = map.size();
            if (map.tempRegenerationValues.length < size) {
                map.tempRegenerationValues = new float[size];
            }
            for (int statIndex = 1; statIndex < size; ++statIndex) {
                final EntityStatValue value = map.get(statIndex);
                if (value != null) {
                    map.tempRegenerationValues[statIndex] = 0.0f;
                    final RegeneratingValue[] regenerating = value.getRegeneratingValues();
                    if (regenerating != null) {
                        for (final RegeneratingValue regeneratingValue : regenerating) {
                            Label_0237: {
                                if (regeneratingValue.getRegenerating().getAmount() > 0.0f) {
                                    if (value.get() >= value.getMax()) {
                                        break Label_0237;
                                    }
                                }
                                else if (value.get() <= value.getMin()) {
                                    break Label_0237;
                                }
                                final float[] tempRegenerationValues = map.tempRegenerationValues;
                                final int n = statIndex;
                                tempRegenerationValues[n] += regeneratingValue.regenerate(commandBuffer, ref, now, dt, value, map.tempRegenerationValues[statIndex]);
                            }
                        }
                    }
                }
            }
            final EntityType entity = archetypeChunk.getComponent(index, this.entityTypeComponent);
            assert entity != null;
            final ItemContainer armorContainer = entity.getInventory().getArmor();
            for (short armorContainerCapacity = armorContainer.getCapacity(), i = 0; i < armorContainerCapacity; ++i) {
                final ItemStack itemStack = armorContainer.getItemStack(i);
                if (!ItemStack.isEmpty(itemStack)) {
                    final Item item = itemStack.getItem();
                    if (item.getArmor() != null) {
                        if (item.getArmor().getRegeneratingValues() != null) {
                            if (!item.getArmor().getRegeneratingValues().isEmpty()) {
                                for (int statIndex2 = 1; statIndex2 < size; ++statIndex2) {
                                    final EntityStatValue value2 = map.get(statIndex2);
                                    if (value2 != null) {
                                        final List<RegeneratingValue> regenValues = item.getArmor().getRegeneratingValues().get(statIndex2);
                                        if (regenValues != null) {
                                            if (!regenValues.isEmpty()) {
                                                for (final RegeneratingValue regeneratingValue2 : regenValues) {
                                                    if (regeneratingValue2.getRegenerating().getAmount() > 0.0f) {
                                                        if (value2.get() >= value2.getMax()) {
                                                            continue;
                                                        }
                                                    }
                                                    else if (value2.get() <= value2.getMin()) {
                                                        continue;
                                                    }
                                                    final float[] tempRegenerationValues2 = map.tempRegenerationValues;
                                                    final int n2 = statIndex2;
                                                    tempRegenerationValues2[n2] += regeneratingValue2.regenerate(commandBuffer, ref, now, dt, value2, map.tempRegenerationValues[statIndex2]);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            for (int statIndex3 = 1; statIndex3 < size; ++statIndex3) {
                final EntityStatValue value3 = map.get(statIndex3);
                if (value3 != null) {
                    final float amount = map.tempRegenerationValues[statIndex3];
                    final boolean invulnerable = commandBuffer.getArchetype(ref).contains(Invulnerable.getComponentType());
                    if (amount < 0.0f && !value3.getIgnoreInvulnerability() && invulnerable) {
                        return;
                    }
                    if (amount != 0.0f) {
                        map.addStatValue(statIndex3, amount);
                    }
                }
            }
        }
    }
    
    public static class Changes extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityStatMap> componentType;
        @Nonnull
        private final Query<EntityStore> query;
        private final Set<Dependency<EntityStore>> dependencies;
        
        public Changes(final ComponentType<EntityStore, EntityStatMap> componentType) {
            this.dependencies = Set.of((Dependency<EntityStore>)new SystemDependency(Order.BEFORE, (Class<ISystem>)EntityTrackerUpdate.class), (Dependency<EntityStore>)new SystemTypeDependency(Order.AFTER, (SystemType<Object, ISystem>)EntityStatsModule.get().getStatModifyingSystemType()));
            this.componentType = componentType;
            this.query = (Query<EntityStore>)Query.and(componentType, InteractionModule.get().getInteractionManagerComponent(), AllLegacyLivingEntityTypesQuery.INSTANCE);
        }
        
        @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 false;
        }
        
        @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> ref = archetypeChunk.getReferenceTo(index);
            final EntityStatMap entityStatMapComponent = archetypeChunk.getComponent(index, this.componentType);
            assert entityStatMapComponent != null;
            final InteractionManager interactionManagerComponent = archetypeChunk.getComponent(index, InteractionModule.get().getInteractionManagerComponent());
            assert interactionManagerComponent != null;
            boolean isDead = archetypeChunk.getArchetype().contains(DeathComponent.getComponentType());
            final Int2ObjectMap<List<EntityStatUpdate>> statChanges = entityStatMapComponent.getSelfUpdates();
            final Int2ObjectMap<FloatList> statValues = entityStatMapComponent.getSelfStatValues();
            for (int statIndex = 0; statIndex < entityStatMapComponent.size(); ++statIndex) {
                final List<EntityStatUpdate> updates = statChanges.get(statIndex);
                if (updates != null) {
                    if (!updates.isEmpty()) {
                        final FloatList statChangeList = statValues.get(statIndex);
                        final EntityStatValue entityStatValue = entityStatMapComponent.get(statIndex);
                        if (entityStatValue != null) {
                            final EntityStatType entityStatType = EntityStatType.getAssetMap().getAsset(statIndex);
                            for (int i = 0; i < updates.size(); ++i) {
                                final EntityStatUpdate update = updates.get(i);
                                final float statPrevious = statChangeList.getFloat(i * 2);
                                final float statValue = statChangeList.getFloat(i * 2 + 1);
                                if (testMaxValue(statValue, statPrevious, entityStatValue, entityStatType.getMaxValueEffects())) {
                                    runInteractions(ref, interactionManagerComponent, entityStatType.getMaxValueEffects(), commandBuffer);
                                }
                                if (testMinValue(statValue, statPrevious, entityStatValue, entityStatType.getMinValueEffects())) {
                                    runInteractions(ref, interactionManagerComponent, entityStatType.getMinValueEffects(), commandBuffer);
                                }
                                if (!isDead && statIndex == DefaultEntityStatTypes.getHealth()) {
                                    if (update.value <= 0.0f) {
                                        if (statValue <= entityStatValue.getMin()) {
                                            DeathComponent.tryAddComponent(commandBuffer, archetypeChunk.getReferenceTo(index), new Damage(Damage.NULL_SOURCE, DamageCause.COMMAND, 0.0f));
                                            isDead = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        private static boolean testMaxValue(final float value, final float previousValue, @Nonnull final EntityStatValue stat, @Nullable final EntityStatType.EntityStatEffects valueEffects) {
            if (valueEffects == null) {
                return false;
            }
            if (valueEffects.triggerAtZero() && stat.getMax() > 0.0f) {
                return previousValue < 0.0f && value >= 0.0f;
            }
            return previousValue != stat.getMax() && value == stat.getMax();
        }
        
        private static boolean testMinValue(final float value, final float previousValue, @Nonnull final EntityStatValue stat, @Nullable final EntityStatType.EntityStatEffects valueEffects) {
            if (valueEffects == null) {
                return false;
            }
            if (valueEffects.triggerAtZero() && stat.getMin() < 0.0f) {
                return previousValue > 0.0f && value < 0.0f;
            }
            return previousValue != stat.getMin() && value == stat.getMin();
        }
        
        private static void runInteractions(@Nonnull final Ref<EntityStore> ref, @Nonnull final InteractionManager interactionManager, @Nullable final EntityStatType.EntityStatEffects valueEffects, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
            if (valueEffects == null) {
                return;
            }
            final String interactions = valueEffects.getInteractions();
            if (interactions == null) {
                return;
            }
            final InteractionContext context = InteractionContext.forInteraction(interactionManager, ref, InteractionType.EntityStatEffect, componentAccessor);
            final InteractionChain chain = interactionManager.initChain(InteractionType.EntityStatEffect, context, RootInteraction.getRootInteractionOrUnknown(interactions), true);
            interactionManager.queueExecuteChain(chain);
        }
    }
    
    public static class Recalculate extends EntityTickingSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, EntityStatMap> entityStatMapComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public Recalculate(@Nonnull final ComponentType<EntityStore, EntityStatMap> entityStatMapComponentType) {
            this.entityStatMapComponentType = entityStatMapComponentType;
            this.query = (Query<EntityStore>)Query.and(AllLegacyLivingEntityTypesQuery.INSTANCE, entityStatMapComponentType);
        }
        
        @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 LivingEntity livingEntity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk);
            assert livingEntity != null;
            final EntityStatMap entityStatMapComponent = archetypeChunk.getComponent(index, this.entityStatMapComponentType);
            assert entityStatMapComponent != null;
            final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
            livingEntity.getStatModifiersManager().recalculateEntityStatModifiers(ref, entityStatMapComponent, commandBuffer);
        }
    }
    
    public static class EntityTrackerUpdate extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityStatMap> componentType;
        private final ComponentType<EntityStore, EntityTrackerSystems.Visible> visibleComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        private final Set<Dependency<EntityStore>> dependencies;
        
        public EntityTrackerUpdate(final ComponentType<EntityStore, EntityStatMap> componentType) {
            this.visibleComponentType = EntityTrackerSystems.Visible.getComponentType();
            this.dependencies = Set.of((Dependency<EntityStore>)new SystemDependency(Order.BEFORE, (Class<ISystem>)EntityTrackerSystems.EffectControllerSystem.class), (Dependency<EntityStore>)new SystemTypeDependency(Order.AFTER, (SystemType<Object, ISystem>)EntityStatsModule.get().getStatModifyingSystemType()));
            this.componentType = componentType;
            this.query = (Query<EntityStore>)Query.and(this.visibleComponentType, componentType);
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityTrackerSystems.QUEUE_UPDATE_GROUP;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @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> ref = archetypeChunk.getReferenceTo(index);
            final EntityTrackerSystems.Visible visible = archetypeChunk.getComponent(index, this.visibleComponentType);
            final EntityStatMap statMap = archetypeChunk.getComponent(index, this.componentType);
            if (!visible.newlyVisibleTo.isEmpty()) {
                queueUpdatesForNewlyVisible(ref, statMap, visible.newlyVisibleTo);
            }
            if (statMap.consumeSelfNetworkOutdated()) {
                final EntityTrackerSystems.EntityViewer selfEntityViewer = visible.visibleTo.get(ref);
                if (selfEntityViewer != null && !visible.newlyVisibleTo.containsKey(ref)) {
                    final ComponentUpdate update = new ComponentUpdate();
                    update.type = ComponentUpdateType.EntityStats;
                    update.entityStatUpdates = statMap.consumeSelfUpdates();
                    selfEntityViewer.queueUpdate(ref, update);
                }
            }
            if (statMap.consumeNetworkOutdated()) {
                final ComponentUpdate update2 = new ComponentUpdate();
                update2.type = ComponentUpdateType.EntityStats;
                update2.entityStatUpdates = statMap.consumeOtherUpdates();
                for (final Map.Entry<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> entry : visible.visibleTo.entrySet()) {
                    final Ref<EntityStore> viewerRef = entry.getKey();
                    if (visible.newlyVisibleTo.containsKey(viewerRef)) {
                        continue;
                    }
                    if (ref.equals(viewerRef)) {
                        continue;
                    }
                    entry.getValue().queueUpdate(ref, update2);
                }
            }
        }
        
        private static void queueUpdatesForNewlyVisible(@Nonnull final Ref<EntityStore> ref, @Nonnull final EntityStatMap statMap, @Nonnull final Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> newlyVisibleTo) {
            final ComponentUpdate update = new ComponentUpdate();
            update.type = ComponentUpdateType.EntityStats;
            update.entityStatUpdates = statMap.createInitUpdate(false);
            for (final Map.Entry<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> entry : newlyVisibleTo.entrySet()) {
                if (ref.equals(entry.getKey())) {
                    queueUpdateForNewlyVisibleSelf(ref, statMap, entry.getValue());
                }
                else {
                    entry.getValue().queueUpdate(ref, update);
                }
            }
        }
        
        private static void queueUpdateForNewlyVisibleSelf(final Ref<EntityStore> ref, @Nonnull final EntityStatMap statMap, @Nonnull final EntityTrackerSystems.EntityViewer viewer) {
            final ComponentUpdate update = new ComponentUpdate();
            update.type = ComponentUpdateType.EntityStats;
            update.entityStatUpdates = statMap.createInitUpdate(true);
            viewer.queueUpdate(ref, update);
        }
    }
    
    public static class EntityTrackerRemove extends RefChangeSystem<EntityStore, EntityStatMap>
    {
        private final ComponentType<EntityStore, EntityStatMap> componentType;
        private final ComponentType<EntityStore, EntityTrackerSystems.Visible> visibleComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public EntityTrackerRemove(final ComponentType<EntityStore, EntityStatMap> componentType) {
            this.visibleComponentType = EntityTrackerSystems.Visible.getComponentType();
            this.componentType = componentType;
            this.query = (Query<EntityStore>)Query.and(this.visibleComponentType, componentType);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, EntityStatMap> componentType() {
            return this.componentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final EntityStatMap component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final EntityStatMap oldComponent, @Nonnull final EntityStatMap newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final EntityStatMap component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            for (final EntityTrackerSystems.EntityViewer viewer : store.getComponent(ref, this.visibleComponentType).visibleTo.values()) {
                viewer.queueRemove(ref, ComponentUpdateType.EntityStats);
            }
        }
    }
    
    public static class ClearChanges extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityStatMap> componentType;
        private final Set<Dependency<EntityStore>> dependencies;
        
        public ClearChanges(final ComponentType<EntityStore, EntityStatMap> componentType) {
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, EntityTrackerUpdate.class));
            this.componentType = componentType;
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.componentType;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @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 EntityStatMap statMap = archetypeChunk.getComponent(index, this.componentType);
            statMap.clearUpdates();
        }
    }
    
    public interface StatModifyingSystem extends ISystem<EntityStore>
    {
    }
}
