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

package com.hypixel.hytale.builtin.blocktick.system;

import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.server.core.asset.type.blocktick.config.TickProcedure;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickManager;
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import java.time.Instant;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.builtin.blocktick.BlockTickPlugin;
import com.hypixel.hytale.logger.HytaleLogger;

public class ChunkBlockTickSystem
{
    protected static final HytaleLogger LOGGER;
    
    static {
        LOGGER = BlockTickPlugin.get().getLogger();
    }
    
    public static class PreTick extends EntityTickingSystem<ChunkStore>
    {
        private static final ComponentType<ChunkStore, BlockChunk> COMPONENT_TYPE;
        
        @Override
        public Query<ChunkStore> getQuery() {
            return PreTick.COMPONENT_TYPE;
        }
        
        @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<ChunkStore> archetypeChunk, @Nonnull final Store<ChunkStore> store, @Nonnull final CommandBuffer<ChunkStore> commandBuffer) {
            final Instant time = commandBuffer.getExternalData().getWorld().getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()).getGameTime();
            final BlockChunk chunk = archetypeChunk.getComponent(index, PreTick.COMPONENT_TYPE);
            assert chunk != null;
            try {
                chunk.preTick(time);
            }
            catch (final Throwable t) {
                ChunkBlockTickSystem.LOGGER.at(Level.SEVERE).withCause(t).log("Failed to pre-tick chunk: %s", chunk);
            }
        }
        
        static {
            COMPONENT_TYPE = BlockChunk.getComponentType();
        }
    }
    
    public static class Ticking extends EntityTickingSystem<ChunkStore>
    {
        private static final ComponentType<ChunkStore, WorldChunk> COMPONENT_TYPE;
        private static final Set<Dependency<ChunkStore>> DEPENDENCIES;
        
        @Override
        public Query<ChunkStore> getQuery() {
            return Ticking.COMPONENT_TYPE;
        }
        
        @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 Ref<ChunkStore> reference = archetypeChunk.getReferenceTo(index);
            final WorldChunk worldChunk = archetypeChunk.getComponent(index, Ticking.COMPONENT_TYPE);
            try {
                tick(reference, worldChunk);
            }
            catch (final Throwable t) {
                ChunkBlockTickSystem.LOGGER.at(Level.SEVERE).withCause(t).log("Failed to tick chunk: %s", worldChunk);
            }
        }
        
        protected static void tick(final Ref<ChunkStore> ref, @Nonnull final WorldChunk worldChunk) {
            final int ticked = worldChunk.getBlockChunk().forEachTicking(ref, worldChunk, (r, c, localX, localY, localZ, blockId) -> {
                final World world = c.getWorld();
                final int blockX = c.getX() << 5 | localX;
                final int blockZ = c.getZ() << 5 | localZ;
                return tickProcedure(world, c, blockX, localY, blockZ, blockId);
            });
            if (ticked > 0) {
                ChunkBlockTickSystem.LOGGER.at(Level.FINER).log("Ticked %d blocks in chunk (%d, %d)", ticked, worldChunk.getX(), worldChunk.getZ());
            }
        }
        
        protected static BlockTickStrategy tickProcedure(@Nonnull final World world, @Nonnull final WorldChunk chunk, final int blockX, final int blockY, final int blockZ, final int blockId) {
            if (!world.getWorldConfig().isBlockTicking() || !BlockTickManager.hasBlockTickProvider()) {
                return BlockTickStrategy.IGNORED;
            }
            final TickProcedure procedure = BlockTickPlugin.get().getTickProcedure(blockId);
            if (procedure == null) {
                return BlockTickStrategy.IGNORED;
            }
            try {
                return procedure.onTick(world, chunk, blockX, blockY, blockZ, blockId);
            }
            catch (final Throwable t) {
                final BlockType blockType = BlockType.getAssetMap().getAsset(blockId);
                ChunkBlockTickSystem.LOGGER.at(Level.WARNING).withCause(t).log("Failed to tick block at (%d, %d, %d) ID %s in world %s:", blockX, blockY, blockZ, blockType.getId(), world.getName());
                return BlockTickStrategy.SLEEP;
            }
        }
        
        static {
            COMPONENT_TYPE = WorldChunk.getComponentType();
            DEPENDENCIES = Set.of(new SystemDependency(Order.AFTER, PreTick.class));
        }
    }
}
