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

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

import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.util.FillerBlockUtil;
import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes;
import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.protocol.BlockMaterial;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;

public final class WorldUtil
{
    public static boolean isFluidOnlyBlock(@Nonnull final BlockType blockType, final int fluidId) {
        return blockType.getMaterial() == BlockMaterial.Empty && fluidId != 0;
    }
    
    public static boolean isSolidOnlyBlock(@Nonnull final BlockType blockType, final int fluidId) {
        return blockType.getMaterial() == BlockMaterial.Solid && fluidId == 0;
    }
    
    public static boolean isEmptyOnlyBlock(@Nonnull final BlockType blockType, final int fluidId) {
        return blockType.getMaterial() == BlockMaterial.Empty && fluidId == 0;
    }
    
    public static int getFluidIdAtPosition(@Nonnull final ComponentAccessor<ChunkStore> chunkStore, @Nonnull final ChunkColumn chunkColumnComponent, final int x, final int y, final int z) {
        if (y < 0 || y >= 320) {
            return 0;
        }
        final Ref<ChunkStore> sectionRef = chunkColumnComponent.getSection(ChunkUtil.chunkCoordinate(y));
        if (sectionRef == null || !sectionRef.isValid()) {
            return 0;
        }
        final FluidSection fluidSectionComponent = chunkStore.getComponent(sectionRef, FluidSection.getComponentType());
        if (fluidSectionComponent == null) {
            return 0;
        }
        return fluidSectionComponent.getFluidId(x, y, z);
    }
    
    public static long getPackedMaterialAndFluidAtPosition(@Nonnull final Ref<ChunkStore> chunkRef, @Nonnull final ComponentAccessor<ChunkStore> chunkStore, final double x, final double y, final double z) {
        if (y < 0.0 || y >= 320.0) {
            return MathUtil.packLong(BlockMaterial.Empty.ordinal(), 0);
        }
        final int blockX = MathUtil.floor(x);
        final int blockY = MathUtil.floor(y);
        final int blockZ = MathUtil.floor(z);
        final ChunkColumn chunkColumnComponent = chunkStore.getComponent(chunkRef, ChunkColumn.getComponentType());
        if (chunkColumnComponent == null) {
            return MathUtil.packLong(BlockMaterial.Empty.ordinal(), 0);
        }
        final BlockChunk blockChunkComponent = chunkStore.getComponent(chunkRef, BlockChunk.getComponentType());
        if (blockChunkComponent == null) {
            return MathUtil.packLong(BlockMaterial.Empty.ordinal(), 0);
        }
        final BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockY);
        int fluidId = 0;
        final Ref<ChunkStore> sectionRef = chunkColumnComponent.getSection(ChunkUtil.chunkCoordinate(y));
        if (sectionRef != null && sectionRef.isValid()) {
            final FluidSection fluidSectionComponent = chunkStore.getComponent(sectionRef, FluidSection.getComponentType());
            if (fluidSectionComponent != null) {
                fluidId = fluidSectionComponent.getFluidId(blockX, blockY, blockZ);
                if (fluidId != 0) {
                    final Fluid fluid = Fluid.getAssetMap().getAsset(fluidId);
                    if (fluid != null) {
                        final double yTest = y - blockY;
                        if (yTest > fluidSectionComponent.getFluidLevel(blockX, blockY, blockZ) / (double)fluid.getMaxFluidLevel()) {
                            fluidId = 0;
                        }
                    }
                }
            }
        }
        final int blockId = blockSection.get(blockX, blockY, blockZ);
        if (blockId == 0) {
            return MathUtil.packLong(BlockMaterial.Empty.ordinal(), fluidId);
        }
        final BlockType blockType = BlockType.getAssetMap().getAsset(blockId);
        if (blockType == null || blockType.isUnknown()) {
            return MathUtil.packLong(BlockMaterial.Empty.ordinal(), fluidId);
        }
        final double relativeY = y - blockY;
        final String blockTypeKey = blockType.getId();
        final BlockType blockTypeAsset = BlockType.getAssetMap().getAsset(blockTypeKey);
        if (blockTypeAsset == null) {
            return MathUtil.packLong(BlockMaterial.Empty.ordinal(), fluidId);
        }
        final BlockMaterial blockTypeMaterial = blockType.getMaterial();
        final int filler = blockSection.getFiller(blockX, blockY, blockZ);
        final int rotation = blockSection.getRotationIndex(blockX, blockY, blockZ);
        if (filler != 0 && blockTypeAsset.getMaterial() == BlockMaterial.Solid) {
            final BlockBoundingBoxes boundingBoxes = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex());
            if (boundingBoxes == null) {
                return MathUtil.packLong(BlockMaterial.Empty.ordinal(), fluidId);
            }
            final BlockBoundingBoxes.RotatedVariantBoxes rotatedBoxes = boundingBoxes.get(rotation);
            final int fillerX = FillerBlockUtil.unpackX(filler);
            final int fillerY = FillerBlockUtil.unpackY(filler);
            final int fillerZ = FillerBlockUtil.unpackZ(filler);
            if (rotatedBoxes.containsPosition(x - blockX + fillerX, relativeY + fillerY, z - blockZ + fillerZ)) {
                return MathUtil.packLong(BlockMaterial.Solid.ordinal(), fluidId);
            }
        }
        else if (blockTypeMaterial == BlockMaterial.Solid) {
            final BlockBoundingBoxes boundingBoxes = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex());
            if (boundingBoxes == null) {
                return MathUtil.packLong(BlockMaterial.Empty.ordinal(), fluidId);
            }
            final BlockBoundingBoxes.RotatedVariantBoxes rotatedBoxes = boundingBoxes.get(rotation);
            if (rotatedBoxes.containsPosition(x - blockX, relativeY, z - blockZ)) {
                return MathUtil.packLong(BlockMaterial.Solid.ordinal(), fluidId);
            }
        }
        return MathUtil.packLong(BlockMaterial.Empty.ordinal(), fluidId);
    }
    
    public static int findFluidBlock(@Nonnull final ComponentAccessor<ChunkStore> chunkStore, @Nonnull final ChunkColumn chunkColumnComponent, @Nonnull final BlockChunk blockChunkComponent, final int x, int y, final int z, final boolean allowBubble) {
        if (y < 0 || y >= 320) {
            return -1;
        }
        if (getFluidIdAtPosition(chunkStore, chunkColumnComponent, x, y++, z) != 0) {
            return y;
        }
        if (y == 320 || !allowBubble) {
            return -1;
        }
        final BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(y);
        final int blockId = blockSection.get(x, y++, z);
        final BlockType blockType = BlockType.getAssetMap().getAsset(blockId);
        final BlockMaterial materialLowerBlock = (blockType != null) ? blockType.getMaterial() : BlockMaterial.Empty;
        if (getFluidIdAtPosition(chunkStore, chunkColumnComponent, x, y++, z) != 0) {
            return y;
        }
        if (materialLowerBlock != BlockMaterial.Solid || y == 320) {
            return -1;
        }
        return (getFluidIdAtPosition(chunkStore, chunkColumnComponent, x, y++, z) != 0) ? y : -1;
    }
    
    public static int getWaterLevel(@Nonnull final ComponentAccessor<ChunkStore> chunkStore, @Nonnull final ChunkColumn chunkColumnComponent, @Nonnull final BlockChunk blockChunkComponent, final int x, final int z, int startY) {
        startY = findFluidBlock(chunkStore, chunkColumnComponent, blockChunkComponent, x, startY, z, true);
        if (startY == -1) {
            return -1;
        }
        while (startY + 1 < 320) {
            final BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(startY + 1);
            final int blockId = blockSection.get(x, startY + 1, z);
            final BlockType blockType = BlockType.getAssetMap().getAsset(blockId);
            if (blockType == null) {
                break;
            }
            final int fluidId = getFluidIdAtPosition(chunkStore, chunkColumnComponent, x, startY + 1, z);
            if (!isFluidOnlyBlock(blockType, fluidId)) {
                break;
            }
            ++startY;
        }
        return startY;
    }
    
    public static int findFarthestEmptySpaceBelow(@Nonnull final ComponentAccessor<ChunkStore> chunkStore, @Nonnull final ChunkColumn chunkColumnComponent, @Nonnull final BlockChunk blockChunkComponent, final int x, int y, final int z, final int yFail) {
        if (y < 0) {
            return yFail;
        }
        if (y >= 320) {
            y = 319;
        }
        final BlockTypeAssetMap<String, BlockType> assetMap = BlockType.getAssetMap();
        for (int indexSection = ChunkUtil.indexSection(y); indexSection >= 0; --indexSection) {
            final Ref<ChunkStore> sectionRef = chunkColumnComponent.getSection(indexSection);
            final FluidSection fluidSectionComponent = chunkStore.getComponent(sectionRef, FluidSection.getComponentType());
            final BlockSection chunkSection = blockChunkComponent.getSectionAtIndex(indexSection);
            if (chunkSection.isSolidAir() && fluidSectionComponent != null && fluidSectionComponent.isEmpty()) {
                y = 32 * indexSection - 1;
                if (y <= 0) {
                    return 0;
                }
            }
            else {
                final int yBottom = 32 * indexSection--;
                while (y >= yBottom) {
                    final int blockId = chunkSection.get(x, y--, z);
                    final int fluidId = (fluidSectionComponent != null) ? fluidSectionComponent.getFluidId(x, y, z) : 0;
                    if (blockId == 0 && fluidId == 0) {
                        continue;
                    }
                    final BlockType blockType = assetMap.getAsset(blockId);
                    if (blockType == null || blockType.isUnknown()) {
                        return y + 2;
                    }
                    final int filler = chunkSection.getFiller(x, y, z);
                    if (filler != 0 || !isEmptyOnlyBlock(blockType, fluidId)) {
                        return y + 2;
                    }
                }
            }
        }
        return 0;
    }
    
    public static int findFarthestEmptySpaceAbove(@Nonnull final ComponentAccessor<ChunkStore> chunkStore, @Nonnull final ChunkColumn chunkColumnComponent, @Nonnull final BlockChunk blockChunkComponent, final int x, int y, final int z, final int yFail) {
        if (y >= 320) {
            return Integer.MAX_VALUE;
        }
        if (y < 0) {
            return yFail;
        }
        final BlockTypeAssetMap<String, BlockType> assetMap = BlockType.getAssetMap();
        final int sectionCount = blockChunkComponent.getSectionCount();
        int indexSection = ChunkUtil.indexSection(y);
        while (indexSection < sectionCount) {
            final Ref<ChunkStore> sectionRef = chunkColumnComponent.getSection(indexSection);
            final FluidSection fluidSectionComponent = chunkStore.getComponent(sectionRef, FluidSection.getComponentType());
            final BlockSection chunkSection = blockChunkComponent.getSectionAtIndex(indexSection);
            if (chunkSection.isSolidAir() && fluidSectionComponent != null && fluidSectionComponent.isEmpty()) {
                ++indexSection;
                y = 32 * indexSection;
                if (y >= 320) {
                    return 319;
                }
                continue;
            }
            else {
                final int yTop = 32 * ++indexSection;
                while (y < yTop) {
                    final int blockId = chunkSection.get(x, y++, z);
                    final int fluidId = (fluidSectionComponent != null) ? fluidSectionComponent.getFluidId(x, y, z) : 0;
                    if (blockId == 0 && fluidId == 0) {
                        continue;
                    }
                    final BlockType blockType = assetMap.getAsset(blockId);
                    if (blockType == null || blockType.isUnknown()) {
                        return y - 1;
                    }
                    final int filler = chunkSection.getFiller(x, y, z);
                    if (filler != 0 || !isEmptyOnlyBlock(blockType, fluidId)) {
                        return y - 1;
                    }
                }
            }
        }
        return Integer.MAX_VALUE;
    }
}
