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

package com.hypixel.hytale.builtin.blockphysics;

import java.util.function.Supplier;
import com.hypixel.hytale.math.util.ChunkUtil;
import javax.annotation.Nullable;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.server.core.universe.world.chunk.AbstractCachedAccessor;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.builtin.blocktick.system.ChunkBlockTickSystem;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.server.core.universe.world.World;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.ArchetypeChunk;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.DisableProcessingAssert;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.logger.HytaleLogger;

public class BlockPhysicsSystems
{
    private static final HytaleLogger LOGGER;
    public static final int MAX_SUPPORT_RADIUS = 14;
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
    }
    
    public static class Ticking extends EntityTickingSystem<ChunkStore> implements DisableProcessingAssert
    {
        private static final Query<ChunkStore> QUERY;
        private static final Set<Dependency<ChunkStore>> DEPENDENCIES;
        
        @Nonnull
        @Override
        public Query<ChunkStore> getQuery() {
            return Ticking.QUERY;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<ChunkStore>> getDependencies() {
            return Ticking.DEPENDENCIES;
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<ChunkStore> archetypeChunk, @Nonnull final Store<ChunkStore> store, @Nonnull final CommandBuffer<ChunkStore> commandBuffer) {
            final ChunkSection section = archetypeChunk.getComponent(index, ChunkSection.getComponentType());
            assert section != null;
            try {
                final BlockSection blockSection = archetypeChunk.getComponent(index, BlockSection.getComponentType());
                assert blockSection != null;
                if (blockSection.getTickingBlocksCountCopy() <= 0) {
                    return;
                }
                final BlockPhysics blockPhysics = archetypeChunk.getComponent(index, BlockPhysics.getComponentType());
                assert blockPhysics != null;
                final FluidSection fluidSection = archetypeChunk.getComponent(index, FluidSection.getComponentType());
                assert fluidSection != null;
                final WorldChunk worldChunk = commandBuffer.getComponent(section.getChunkColumnReference(), WorldChunk.getComponentType());
                final CachedAccessor accessor = CachedAccessor.of(commandBuffer, blockSection, blockPhysics, fluidSection, section.getX(), section.getY(), section.getZ(), 14);
                blockSection.forEachTicking(worldChunk, accessor, section.getY(), (wc, accessor1, localX, localY, localZ, blockId) -> {
                    final BlockPhysics phys = accessor1.selfPhysics;
                    final boolean isDeco = phys.isDeco(localX, localY, localZ);
                    final BlockType blockType = BlockType.getAssetMap().getAsset(blockId);
                    if (blockType == null || blockId == 0) {
                        return BlockTickStrategy.IGNORED;
                    }
                    else if (blockType.canBePlacedAsDeco() && isDeco) {
                        return BlockTickStrategy.IGNORED;
                    }
                    else {
                        final World world = wc.getWorld();
                        final Store<EntityStore> entityStore = world.getEntityStore().getStore();
                        final int blockX = wc.getX() << 5 | localX;
                        final int blockY = localY;
                        final int blockZ = wc.getZ() << 5 | localZ;
                        final int filler = accessor1.selfBlockSection.getFiller(localX, localY, localZ);
                        final int rotation = accessor1.selfBlockSection.getRotationIndex(localX, localY, localZ);
                        return switch (BlockPhysicsUtil.applyBlockPhysics(entityStore, wc.getReference(), accessor, accessor1.selfBlockSection, accessor1.selfPhysics, accessor1.selfFluidSection, blockX, blockY, blockZ, blockType, rotation, filler)) {
                            default -> throw new MatchException(null, null);
                            case WAITING_CHUNK -> BlockTickStrategy.WAIT_FOR_ADJACENT_CHUNK_LOAD;
                            case VALID -> BlockTickStrategy.IGNORED;
                            case INVALID -> BlockTickStrategy.SLEEP;
                        };
                    }
                });
            }
            catch (final Exception t) {
                BlockPhysicsSystems.LOGGER.at(Level.SEVERE).withCause(t).log("Failed to tick chunk: %s", section);
            }
        }
        
        static {
            QUERY = Query.and(ChunkSection.getComponentType(), BlockSection.getComponentType(), BlockPhysics.getComponentType(), FluidSection.getComponentType());
            DEPENDENCIES = Set.of(new SystemDependency(Order.AFTER, ChunkBlockTickSystem.PreTick.class), new SystemDependency(Order.BEFORE, ChunkBlockTickSystem.Ticking.class));
        }
    }
    
    public static class CachedAccessor extends AbstractCachedAccessor
    {
        private static final ThreadLocal<CachedAccessor> THREAD_LOCAL;
        private static final int PHYSICS_COMPONENT = 0;
        private static final int FLUID_COMPONENT = 1;
        private static final int BLOCK_COMPONENT = 2;
        protected BlockSection selfBlockSection;
        protected BlockPhysics selfPhysics;
        protected FluidSection selfFluidSection;
        
        protected CachedAccessor() {
            super(3);
        }
        
        @Nonnull
        public static CachedAccessor of(final ComponentAccessor<ChunkStore> commandBuffer, final BlockSection blockSection, final BlockPhysics section, final FluidSection fluidSection, final int cx, final int cy, final int cz, final int radius) {
            final CachedAccessor accessor = CachedAccessor.THREAD_LOCAL.get();
            accessor.init(commandBuffer, cx, cy, cz, radius);
            accessor.insertSectionComponent(0, section, cx, cy, cz);
            accessor.insertSectionComponent(1, fluidSection, cx, cy, cz);
            accessor.insertSectionComponent(2, blockSection, cx, cy, cz);
            accessor.selfBlockSection = blockSection;
            accessor.selfPhysics = section;
            accessor.selfFluidSection = fluidSection;
            return accessor;
        }
        
        @Nullable
        public BlockPhysics getBlockPhysics(final int cx, final int cy, final int cz) {
            return this.getComponentSection(cx, cy, cz, 0, BlockPhysics.getComponentType());
        }
        
        @Nullable
        public FluidSection getFluidSection(final int cx, final int cy, final int cz) {
            return this.getComponentSection(cx, cy, cz, 1, FluidSection.getComponentType());
        }
        
        @Nullable
        public BlockSection getBlockSection(final int cx, final int cy, final int cz) {
            return this.getComponentSection(cx, cy, cz, 2, BlockSection.getComponentType());
        }
        
        public void performBlockUpdate(final int x, final int y, final int z, final int maxSupportDistance) {
            for (int ix = -1; ix < 2; ++ix) {
                final int wx = x + ix;
                for (int iz = -1; iz < 2; ++iz) {
                    final int wz = z + iz;
                    for (int iy = -1; iy < 2; ++iy) {
                        final int wy = y + iy;
                        final BlockPhysics physics = this.getBlockPhysics(ChunkUtil.chunkCoordinate(wx), ChunkUtil.chunkCoordinate(wy), ChunkUtil.chunkCoordinate(wz));
                        final int support = (physics != null) ? physics.get(wx, wy, wz) : 0;
                        if (support <= maxSupportDistance) {
                            final BlockSection blockChunk = this.getBlockSection(ChunkUtil.chunkCoordinate(wx), ChunkUtil.chunkCoordinate(wy), ChunkUtil.chunkCoordinate(wz));
                            if (blockChunk != null) {
                                blockChunk.setTicking(wx, wy, wz, true);
                            }
                        }
                    }
                }
            }
        }
        
        public void performBlockUpdate(final int x, final int y, final int z) {
            for (int ix = -1; ix < 2; ++ix) {
                final int wx = x + ix;
                for (int iz = -1; iz < 2; ++iz) {
                    final int wz = z + iz;
                    for (int iy = -1; iy < 2; ++iy) {
                        final int wy = y + iy;
                        final BlockSection blockChunk = this.getBlockSection(ChunkUtil.chunkCoordinate(wx), ChunkUtil.chunkCoordinate(wy), ChunkUtil.chunkCoordinate(wz));
                        if (blockChunk != null) {
                            blockChunk.setTicking(wx, wy, wz, true);
                        }
                    }
                }
            }
        }
        
        static {
            THREAD_LOCAL = ThreadLocal.withInitial((Supplier<? extends CachedAccessor>)CachedAccessor::new);
        }
    }
}
