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

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

import java.util.Iterator;
import com.hypixel.hytale.protocol.ComponentUpdateType;
import com.hypixel.hytale.protocol.ComponentUpdate;
import java.util.Map;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.server.core.modules.entity.component.ActiveAnimationComponent;
import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager;
import java.util.Collections;
import com.hypixel.hytale.server.core.entity.movement.MovementStatesSystems;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.protocol.MovementStates;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent;
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.protocol.PlayerSkin;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSkinComponent;
import java.util.Random;
import com.hypixel.hytale.common.util.RandomUtil;
import com.hypixel.hytale.server.core.cosmetics.CosmeticsModule;
import com.hypixel.hytale.server.core.modules.entity.player.ApplyRandomSkinPersistedComponent;
import com.hypixel.hytale.server.core.prefab.PrefabCopyableComponent;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.server.core.modules.entity.component.PropComponent;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Holder;
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 com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
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 javax.annotation.Nonnull;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.RefChangeSystem;

public class ModelSystems
{
    public static class ModelChange extends RefChangeSystem<EntityStore, ModelComponent>
    {
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        private final ComponentType<EntityStore, PersistentModel> persistentModelComponentType;
        
        public ModelChange() {
            this.modelComponentType = ModelComponent.getComponentType();
            this.persistentModelComponentType = PersistentModel.getComponentType();
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.persistentModelComponentType;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, ModelComponent> componentType() {
            return this.modelComponentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final ModelComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final ModelComponent oldComponent, @Nonnull final ModelComponent newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final PersistentModel persistentModel = store.getComponent(ref, this.persistentModelComponentType);
            persistentModel.setModelReference(newComponent.getModel().toReference());
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final ModelComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            commandBuffer.removeComponent(ref, this.persistentModelComponentType);
        }
    }
    
    public static class ModelSpawned extends HolderSystem<EntityStore>
    {
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        private final ComponentType<EntityStore, BoundingBox> boundingBoxComponentType;
        private final Set<Dependency<EntityStore>> dependencies;
        
        public ModelSpawned() {
            this.modelComponentType = ModelComponent.getComponentType();
            this.boundingBoxComponentType = BoundingBox.getComponentType();
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, SetRenderedModel.class));
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            final Model model = holder.getComponent(this.modelComponentType).getModel();
            final Box modelBoundingBox = model.getBoundingBox();
            if (modelBoundingBox == null) {
                return;
            }
            BoundingBox boundingBox = holder.getComponent(this.boundingBoxComponentType);
            if (boundingBox == null) {
                boundingBox = new BoundingBox();
                holder.addComponent(this.boundingBoxComponentType, boundingBox);
            }
            boundingBox.setBoundingBox(modelBoundingBox);
            boundingBox.setDetailBoxes(model.getDetailBoxes());
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.modelComponentType;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
    }
    
    public static class SetRenderedModel extends HolderSystem<EntityStore>
    {
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        private final ComponentType<EntityStore, PersistentModel> persistentModelComponentType;
        private final Query<EntityStore> query;
        
        public SetRenderedModel() {
            this.modelComponentType = ModelComponent.getComponentType();
            this.persistentModelComponentType = PersistentModel.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.persistentModelComponentType, Query.not((Query<Object>)this.modelComponentType));
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            final PersistentModel persistentModel = holder.getComponent(this.persistentModelComponentType);
            holder.putComponent(this.modelComponentType, new ModelComponent(persistentModel.getModelReference().toModel()));
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
    }
    
    public static class AssignNetworkIdToProps extends HolderSystem<EntityStore>
    {
        private final ComponentType<EntityStore, PropComponent> propComponentType;
        private final ComponentType<EntityStore, NetworkId> networkIdComponentType;
        private final Query<EntityStore> query;
        
        public AssignNetworkIdToProps() {
            this.propComponentType = PropComponent.getComponentType();
            this.networkIdComponentType = NetworkId.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.propComponentType, Query.not((Query<Object>)this.networkIdComponentType));
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            holder.addComponent(this.networkIdComponentType, new NetworkId(store.getExternalData().takeNextNetworkId()));
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
    }
    
    public static class EnsurePropsPrefabCopyable extends HolderSystem<EntityStore>
    {
        private final ComponentType<EntityStore, PropComponent> propComponentType;
        
        public EnsurePropsPrefabCopyable() {
            this.propComponentType = PropComponent.getComponentType();
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            holder.ensureComponent(PrefabCopyableComponent.getComponentType());
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.propComponentType;
        }
    }
    
    public static class ApplyRandomSkin extends HolderSystem<EntityStore>
    {
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        private final ComponentType<EntityStore, ApplyRandomSkinPersistedComponent> randomSkinComponent;
        private final Query<EntityStore> query;
        
        public ApplyRandomSkin() {
            this.modelComponentType = ModelComponent.getComponentType();
            this.randomSkinComponent = ApplyRandomSkinPersistedComponent.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.randomSkinComponent, this.modelComponentType);
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            final PlayerSkin playerSkin = CosmeticsModule.get().generateRandomSkin(RandomUtil.getSecureRandom());
            holder.putComponent(PlayerSkinComponent.getComponentType(), new PlayerSkinComponent(playerSkin));
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
    }
    
    public static class PlayerConnect extends HolderSystem<EntityStore>
    {
        private final ComponentType<EntityStore, Player> playerComponentType;
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        private final Query<EntityStore> query;
        private final Set<Dependency<EntityStore>> dependencies;
        
        public PlayerConnect() {
            this.playerComponentType = Player.getComponentType();
            this.modelComponentType = ModelComponent.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.playerComponentType, Query.not((Query<Object>)this.modelComponentType));
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.BEFORE, ModelSpawned.class));
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            final Player player = holder.getComponent(this.playerComponentType);
            final DefaultAssetMap<String, ModelAsset> assetMap = ModelAsset.getAssetMap();
            final String preset = player.getPlayerConfigData().getPreset();
            final ModelAsset modelAsset = (preset != null) ? assetMap.getAsset(preset) : null;
            if (modelAsset != null) {
                final Model model = Model.createUnitScaleModel(modelAsset);
                holder.addComponent(this.modelComponentType, new ModelComponent(model));
                return;
            }
            final ModelAsset defaultModelAsset = assetMap.getAsset("Player");
            if (defaultModelAsset != null) {
                final Model defaultModel = Model.createUnitScaleModel(defaultModelAsset);
                holder.addComponent(this.modelComponentType, new ModelComponent(defaultModel));
            }
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
    }
    
    public static class UpdateBoundingBox extends RefChangeSystem<EntityStore, ModelComponent>
    {
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        private final ComponentType<EntityStore, BoundingBox> boundingBoxComponentType;
        private final ComponentType<EntityStore, MovementStatesComponent> movementStatesComponentType;
        
        public UpdateBoundingBox() {
            this.modelComponentType = ModelComponent.getComponentType();
            this.boundingBoxComponentType = BoundingBox.getComponentType();
            this.movementStatesComponentType = MovementStatesComponent.getComponentType();
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.boundingBoxComponentType;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, ModelComponent> componentType() {
            return this.modelComponentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final ModelComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final BoundingBox boundingBox = commandBuffer.getComponent(ref, this.boundingBoxComponentType);
            final MovementStatesComponent movementStates = commandBuffer.getComponent(ref, this.movementStatesComponentType);
            updateBoundingBox(component.getModel(), boundingBox, movementStates);
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final ModelComponent oldComponent, @Nonnull final ModelComponent newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final BoundingBox boundingBox = commandBuffer.getComponent(ref, this.boundingBoxComponentType);
            final MovementStatesComponent movementStates = commandBuffer.getComponent(ref, this.movementStatesComponentType);
            updateBoundingBox(newComponent.getModel(), boundingBox, movementStates);
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final ModelComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            commandBuffer.getComponent(ref, this.boundingBoxComponentType).setBoundingBox(new Box());
        }
        
        protected static void updateBoundingBox(@Nonnull final Model model, @Nonnull final BoundingBox boundingBox, @Nullable final MovementStatesComponent movementStatesComponent) {
            updateBoundingBox(model, boundingBox, (movementStatesComponent != null) ? movementStatesComponent.getMovementStates() : null);
        }
        
        protected static void updateBoundingBox(@Nonnull final Model model, @Nonnull final BoundingBox boundingBox, @Nullable final MovementStates movementStates) {
            Box modelBoundingBox = model.getBoundingBox(movementStates);
            if (modelBoundingBox == null) {
                modelBoundingBox = new Box();
            }
            boundingBox.setBoundingBox(modelBoundingBox);
        }
    }
    
    public static class UpdateCrouchingBoundingBox extends EntityTickingSystem<EntityStore>
    {
        public static final Set<Dependency<EntityStore>> DEPENDENCIES;
        private final ComponentType<EntityStore, MovementStatesComponent> movementStatesComponentType;
        private final ComponentType<EntityStore, BoundingBox> boundingBoxComponentType;
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        private final Query<EntityStore> query;
        
        public UpdateCrouchingBoundingBox() {
            this.movementStatesComponentType = MovementStatesComponent.getComponentType();
            this.boundingBoxComponentType = BoundingBox.getComponentType();
            this.modelComponentType = ModelComponent.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.movementStatesComponentType, this.boundingBoxComponentType, this.modelComponentType);
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return UpdateCrouchingBoundingBox.DEPENDENCIES;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @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 MovementStatesComponent movementStates = archetypeChunk.getComponent(index, this.movementStatesComponentType);
            final MovementStates newMovementStates = movementStates.getMovementStates();
            final MovementStates sentMovementStates = movementStates.getSentMovementStates();
            if (newMovementStates.crouching == sentMovementStates.crouching && newMovementStates.forcedCrouching == sentMovementStates.forcedCrouching) {
                return;
            }
            final Model model = archetypeChunk.getComponent(index, this.modelComponentType).getModel();
            final BoundingBox boundingBox = archetypeChunk.getComponent(index, this.boundingBoxComponentType);
            UpdateBoundingBox.updateBoundingBox(model, boundingBox, newMovementStates);
        }
        
        static {
            DEPENDENCIES = Collections.singleton(new SystemDependency(Order.BEFORE, MovementStatesSystems.TickingSystem.class));
        }
    }
    
    public static class PlayerUpdateMovementManager extends RefChangeSystem<EntityStore, ModelComponent>
    {
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        private final ComponentType<EntityStore, Player> playerComponentType;
        private final Query<EntityStore> query;
        private final Set<Dependency<EntityStore>> dependencies;
        
        public PlayerUpdateMovementManager() {
            this.modelComponentType = ModelComponent.getComponentType();
            this.playerComponentType = Player.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.playerComponentType, MovementManager.getComponentType());
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, UpdateBoundingBox.class));
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, ModelComponent> componentType() {
            return this.modelComponentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final ModelComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            this.updateMovementController(ref, commandBuffer);
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final ModelComponent oldComponent, @Nonnull final ModelComponent newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            this.updateMovementController(ref, commandBuffer);
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final ModelComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            this.updateMovementController(ref, commandBuffer);
        }
        
        private void updateMovementController(@Nonnull final Ref<EntityStore> ref, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
            final MovementManager movementManagerComponent = componentAccessor.getComponent(ref, MovementManager.getComponentType());
            assert movementManagerComponent != null;
            movementManagerComponent.resetDefaultsAndUpdate(ref, componentAccessor);
        }
    }
    
    public static class AnimationEntityTrackerUpdate extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityTrackerSystems.Visible> visibleComponentType;
        private final ComponentType<EntityStore, ActiveAnimationComponent> activeAnimationComponentType;
        private final Query<EntityStore> query;
        
        public AnimationEntityTrackerUpdate() {
            this.visibleComponentType = EntityTrackerSystems.Visible.getComponentType();
            this.activeAnimationComponentType = ActiveAnimationComponent.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.visibleComponentType, this.activeAnimationComponentType);
        }
        
        @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 ActiveAnimationComponent activeAnimationComponent = archetypeChunk.getComponent(index, this.activeAnimationComponentType);
            assert activeAnimationComponent != null;
            final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
            if (activeAnimationComponent.consumeNetworkOutdated()) {
                queueUpdatesFor(ref, activeAnimationComponent, visibleComponent.visibleTo);
            }
            else if (!visibleComponent.newlyVisibleTo.isEmpty()) {
                queueUpdatesFor(ref, activeAnimationComponent, visibleComponent.newlyVisibleTo);
            }
        }
        
        private static void queueUpdatesFor(@Nonnull final Ref<EntityStore> ref, @Nonnull final ActiveAnimationComponent animationComponent, @Nonnull final Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> visibleTo) {
            final ComponentUpdate update = new ComponentUpdate();
            update.type = ComponentUpdateType.ActiveAnimations;
            update.activeAnimations = animationComponent.getActiveAnimations();
            for (final Map.Entry<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> entry : visibleTo.entrySet()) {
                entry.getValue().queueUpdate(ref, update);
            }
        }
    }
}
