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

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

import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.modules.physics.component.Velocity;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.component.DisableProcessingAssert;
import java.util.Iterator;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.protocol.ComponentUpdateType;
import com.hypixel.hytale.protocol.ComponentUpdate;
import java.util.Map;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.modules.entity.component.EntityScaleComponent;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ArchetypeChunk;
import javax.annotation.Nullable;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.server.core.modules.physics.SimplePhysicsProvider;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import com.hypixel.hytale.math.shape.Box;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.AddReason;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.server.core.entity.entities.BlockEntity;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.logger.HytaleLogger;

public class BlockEntitySystems
{
    private static final HytaleLogger LOGGER;
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
    }
    
    public static class BlockEntitySetupSystem extends HolderSystem<EntityStore>
    {
        private final ComponentType<EntityStore, BlockEntity> blockEntityComponentType;
        
        public BlockEntitySetupSystem(final ComponentType<EntityStore, BlockEntity> blockEntityComponentType) {
            this.blockEntityComponentType = blockEntityComponentType;
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            if (!holder.getArchetype().contains(NetworkId.getComponentType())) {
                holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
            }
            final BlockEntity blockEntityComponent = holder.getComponent(this.blockEntityComponentType);
            BoundingBox boundingBoxComponent = blockEntityComponent.createBoundingBoxComponent();
            if (boundingBoxComponent == null) {
                BlockEntitySystems.LOGGER.at(Level.SEVERE).log("Bounding box could not be initialized properly, defaulting to 1x1x1 dimensions for Block Entity bounding box");
                boundingBoxComponent = new BoundingBox(Box.horizontallyCentered(1.0, 1.0, 1.0));
            }
            holder.putComponent(BoundingBox.getComponentType(), boundingBoxComponent);
            final SimplePhysicsProvider simplePhysicsProvider = blockEntityComponent.initPhysics(boundingBoxComponent);
            simplePhysicsProvider.setMoveOutOfSolid(false);
        }
        
        @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.blockEntityComponentType;
        }
    }
    
    public static class BlockEntityTrackerSystem extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, EntityTrackerSystems.Visible> visibleComponentType;
        private final ComponentType<EntityStore, BlockEntity> blockEntityComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public BlockEntityTrackerSystem(final ComponentType<EntityStore, EntityTrackerSystems.Visible> visibleComponentType, final ComponentType<EntityStore, BlockEntity> blockEntityComponentType) {
            this.visibleComponentType = visibleComponentType;
            this.blockEntityComponentType = blockEntityComponentType;
            this.query = (Query<EntityStore>)Query.and(visibleComponentType, blockEntityComponentType);
        }
        
        @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 visible = archetypeChunk.getComponent(index, this.visibleComponentType);
            final BlockEntity blockEntity = archetypeChunk.getComponent(index, this.blockEntityComponentType);
            assert blockEntity != null;
            float entityScale = 2.0f;
            boolean scaleOutdated = false;
            final EntityScaleComponent entityScaleComponent = archetypeChunk.getComponent(index, EntityScaleComponent.getComponentType());
            if (entityScaleComponent != null) {
                entityScale = entityScaleComponent.getScale();
                scaleOutdated = entityScaleComponent.consumeNetworkOutdated();
            }
            final boolean blockIdOutdated = blockEntity.consumeBlockIdNetworkOutdated();
            if (blockIdOutdated || scaleOutdated) {
                queueUpdatesFor(archetypeChunk.getReferenceTo(index), blockEntity, visible.visibleTo, entityScale);
            }
            else if (!visible.newlyVisibleTo.isEmpty()) {
                queueUpdatesFor(archetypeChunk.getReferenceTo(index), blockEntity, visible.newlyVisibleTo, entityScale);
            }
        }
        
        private static void queueUpdatesFor(final Ref<EntityStore> ref, @Nonnull final BlockEntity entity, @Nonnull final Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> visibleTo, final float entityScale) {
            final ComponentUpdate update = new ComponentUpdate();
            update.type = ComponentUpdateType.Block;
            final String key = entity.getBlockTypeKey();
            final int index = BlockType.getAssetMap().getIndex(key);
            if (index == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + key);
            }
            update.blockId = index;
            update.entityScale = entityScale;
            for (final EntityTrackerSystems.EntityViewer viewer : visibleTo.values()) {
                viewer.queueUpdate(ref, update);
            }
        }
    }
    
    public static class Ticking extends EntityTickingSystem<EntityStore> implements DisableProcessingAssert
    {
        private final ComponentType<EntityStore, TransformComponent> transformComponentType;
        private final ComponentType<EntityStore, BlockEntity> blockEntityComponentType;
        private final Archetype<EntityStore> archetype;
        
        public Ticking(@Nonnull final ComponentType<EntityStore, BlockEntity> blockEntityComponentType) {
            this.transformComponentType = TransformComponent.getComponentType();
            this.blockEntityComponentType = blockEntityComponentType;
            this.archetype = Archetype.of(this.transformComponentType, blockEntityComponentType, Velocity.getComponentType());
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.archetype;
        }
        
        @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 TransformComponent transformComponent = archetypeChunk.getComponent(index, this.transformComponentType);
            assert transformComponent != null;
            final BlockEntity blockEntityComponent = archetypeChunk.getComponent(index, this.blockEntityComponentType);
            assert blockEntityComponent != null;
            final Velocity velocityComponent = archetypeChunk.getComponent(index, Velocity.getComponentType());
            assert velocityComponent != null;
            try {
                blockEntityComponent.getSimplePhysicsProvider().tick(dt, velocityComponent, store.getExternalData().getWorld(), transformComponent, archetypeChunk.getReferenceTo(index), commandBuffer);
            }
            catch (final Throwable throwable) {
                BlockEntitySystems.LOGGER.at(Level.SEVERE).withCause(throwable).log("Exception while ticking entity. Removing entity %s", blockEntityComponent);
                commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE);
            }
        }
    }
}
