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

package com.hypixel.hytale.builtin.fluid;

import javax.annotation.Nullable;
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy;
import java.time.Instant;
import com.hypixel.hytale.math.util.ChunkUtil;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.universe.world.events.ChunkPreLoadProcessEvent;
import com.hypixel.hytale.event.EventPriority;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.server.core.asset.type.fluid.FiniteFluidTicker;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.server.core.asset.type.fluid.DefaultFluidTicker;
import com.hypixel.hytale.codec.lookup.Priority;
import com.hypixel.hytale.server.core.asset.type.fluid.FluidTicker;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class FluidPlugin extends JavaPlugin
{
    private static final HytaleLogger LOGGER;
    private static FluidPlugin instance;
    
    public static FluidPlugin get() {
        return FluidPlugin.instance;
    }
    
    public FluidPlugin(@Nonnull final JavaPluginInit init) {
        super(init);
        FluidPlugin.instance = this;
    }
    
    @Override
    protected void setup() {
        FluidTicker.CODEC.register(Priority.DEFAULT, "Default", DefaultFluidTicker.class, DefaultFluidTicker.CODEC);
        FluidTicker.CODEC.register("Finite", FiniteFluidTicker.class, FiniteFluidTicker.CODEC);
        this.getChunkStoreRegistry().registerSystem(new FluidSystems.EnsureFluidSection());
        this.getChunkStoreRegistry().registerSystem(new FluidSystems.MigrateFromColumn());
        this.getChunkStoreRegistry().registerSystem(new FluidSystems.SetupSection());
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new FluidSystems.LoadPacketGenerator());
        this.getChunkStoreRegistry().registerSystem(new FluidSystems.ReplicateChanges());
        this.getChunkStoreRegistry().registerSystem(new FluidSystems.Ticking());
        this.getEventRegistry().registerGlobal(EventPriority.FIRST, ChunkPreLoadProcessEvent.class, FluidPlugin::onChunkPreProcess);
        this.getCommandRegistry().registerCommand(new FluidCommand());
    }
    
    private static void onChunkPreProcess(@Nonnull final ChunkPreLoadProcessEvent event) {
        if (!event.isNewlyGenerated()) {
            return;
        }
        final WorldChunk wc = event.getChunk();
        final Holder<ChunkStore> holder = event.getHolder();
        final ChunkColumn column = holder.getComponent(ChunkColumn.getComponentType());
        if (column == null) {
            return;
        }
        final BlockChunk blockChunk = holder.getComponent(BlockChunk.getComponentType());
        if (blockChunk == null) {
            return;
        }
        final IndexedLookupTableAssetMap<String, Fluid> fluidMap = Fluid.getAssetMap();
        final BlockTypeAssetMap<String, BlockType> blockMap = BlockType.getAssetMap();
        final Holder<ChunkStore>[] sections = column.getSectionHolders();
        if (sections == null) {
            return;
        }
        int x = 0;
        int y = 0;
        int z = 0;
        for (int i = 0; i < sections.length && i < 10; ++i) {
            final Holder<ChunkStore> section = sections[i];
            final FluidSection fluid = section.getComponent(FluidSection.getComponentType());
            if (fluid != null) {
                if (!fluid.isEmpty()) {
                    final BlockSection blockSection = section.ensureAndGetComponent(BlockSection.getComponentType());
                    for (int idx = 0; idx < 32768; ++idx) {
                        final int fluidId = fluid.getFluidId(idx);
                        if (fluidId != 0) {
                            final Fluid fluidType = fluidMap.getAsset(fluidId);
                            if (fluidType == null) {
                                FluidPlugin.LOGGER.at(Level.WARNING).log("Invalid fluid found in chunk section: %d, %d %d with id %d", fluid.getX(), fluid.getY(), fluid.getZ(), fluid);
                                fluid.setFluid(idx, 0, (byte)0);
                            }
                            else {
                                final FluidTicker ticker = fluidType.getTicker();
                                if (FluidTicker.isSolid(blockMap.getAsset(blockSection.get(idx)))) {
                                    fluid.setFluid(idx, 0, (byte)0);
                                }
                                else {
                                    if (!ticker.canDemote()) {
                                        x = ChunkUtil.minBlock(fluid.getX()) + ChunkUtil.xFromIndex(idx);
                                        y = ChunkUtil.minBlock(fluid.getY()) + ChunkUtil.yFromIndex(idx);
                                        z = ChunkUtil.minBlock(fluid.getZ()) + ChunkUtil.zFromIndex(idx);
                                        boolean canSpread = ChunkUtil.isBorderBlock(x, z) || (fluid.getFluidId(x - 1, y, z) == 0 && !FluidTicker.isSolid(blockMap.getAsset(blockSection.get(x - 1, y, z)))) || (fluid.getFluidId(x + 1, y, z) == 0 && !FluidTicker.isSolid(blockMap.getAsset(blockSection.get(x + 1, y, z)))) || (fluid.getFluidId(x, y, z - 1) == 0 && !FluidTicker.isSolid(blockMap.getAsset(blockSection.get(x, y, z - 1)))) || (fluid.getFluidId(x, y, z + 1) == 0 && !FluidTicker.isSolid(blockMap.getAsset(blockSection.get(x, y, z + 1))));
                                        if (y > 0) {
                                            if (ChunkUtil.chunkCoordinate(y) == ChunkUtil.chunkCoordinate(y - 1)) {
                                                canSpread |= (fluid.getFluidId(x, y - 1, z) == 0 && !FluidTicker.isSolid(blockMap.getAsset(blockSection.get(x, y - 1, z))));
                                            }
                                            else {
                                                final FluidSection fluidSection2 = sections[i - 1].getComponent(FluidSection.getComponentType());
                                                canSpread |= (fluidSection2.getFluidId(x, y - 1, z) == 0 && !FluidTicker.isSolid(blockMap.getAsset(blockChunk.getBlock(x, y - 1, z))));
                                            }
                                        }
                                        if (!canSpread) {
                                            blockSection.setTicking(idx, false);
                                            continue;
                                        }
                                    }
                                    blockSection.setTicking(idx, true);
                                }
                            }
                        }
                    }
                }
            }
        }
        int tickingBlocks = blockChunk.getTickingBlocksCount();
        if (tickingBlocks == 0) {
            return;
        }
        final PreprocesorAccessor accessor = new PreprocesorAccessor(wc, blockChunk, sections);
        do {
            blockChunk.preTick(Instant.MIN);
            for (int j = 0; j < sections.length; ++j) {
                final Holder<ChunkStore> section2 = sections[j];
                final FluidSection fluidSection3 = section2.getComponent(FluidSection.getComponentType());
                if (fluidSection3 != null) {
                    if (!fluidSection3.isEmpty()) {
                        final BlockSection blockSection2 = section2.ensureAndGetComponent(BlockSection.getComponentType());
                        fluidSection3.preload(wc.getX(), j, wc.getZ());
                        final PreprocesorAccessor preprocesorAccessor;
                        (accessor.blockSection = blockSection2).forEachTicking(accessor, fluidSection3, j, (preprocesorAccessor, fluidSection1, x, y, z, block) -> {
                            final int fluidId2 = fluidSection1.getFluidId(x, y, z);
                            if (fluidId2 == 0) {
                                return BlockTickStrategy.IGNORED;
                            }
                            else {
                                final Fluid fluid2 = Fluid.getAssetMap().getAsset(fluidId2);
                                final int blockX = fluidSection1.getX() << 5 | x;
                                final int blockY = y;
                                final int blockZ = fluidSection1.getZ() << 5 | z;
                                return fluid2.getTicker().process(preprocesorAccessor.worldChunk.getWorld(), preprocesorAccessor.tick, preprocesorAccessor, fluidSection1, accessor.blockSection, fluid2, fluidId2, blockX, blockY, blockZ);
                            }
                        });
                    }
                }
            }
            tickingBlocks = blockChunk.getTickingBlocksCount();
            final PreprocesorAccessor preprocesorAccessor = accessor;
            ++preprocesorAccessor.tick;
        } while (tickingBlocks != 0 && accessor.tick <= 100L);
        blockChunk.mergeTickingBlocks();
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
    }
    
    public static class PreprocesorAccessor implements FluidTicker.Accessor
    {
        private final WorldChunk worldChunk;
        private final BlockChunk blockChunk;
        private final Holder<ChunkStore>[] sections;
        public long tick;
        public BlockSection blockSection;
        
        public PreprocesorAccessor(final WorldChunk worldChunk, final BlockChunk blockChunk, final Holder<ChunkStore>[] sections) {
            this.worldChunk = worldChunk;
            this.blockChunk = blockChunk;
            this.sections = sections;
        }
        
        @Nullable
        @Override
        public FluidSection getFluidSection(final int cx, final int cy, final int cz) {
            if (this.blockChunk.getX() == cx && this.blockChunk.getZ() == cz && cy >= 0 && cy < this.sections.length) {
                return this.sections[cy].getComponent(FluidSection.getComponentType());
            }
            return null;
        }
        
        @Nullable
        @Override
        public BlockSection getBlockSection(final int cx, final int cy, final int cz) {
            if (cy < 0 || cy >= 10) {
                return null;
            }
            if (this.blockChunk.getX() == cx && this.blockChunk.getZ() == cz) {
                return this.blockChunk.getSectionAtIndex(cy);
            }
            return null;
        }
        
        @Override
        public void setBlock(final int x, final int y, final int z, final int blockId) {
            if (this.worldChunk.getX() != ChunkUtil.chunkCoordinate(x) && this.worldChunk.getZ() != ChunkUtil.chunkCoordinate(z)) {
                return;
            }
            this.worldChunk.setBlock(x, y, z, blockId, 157);
        }
    }
}
