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

package com.hypixel.hytale.server.core.util;

import com.hypixel.hytale.server.core.prefab.PrefabWeights;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.server.core.prefab.PrefabCopyableComponent;
import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab;
import com.hypixel.hytale.server.core.prefab.event.PrefabPlaceEntityEvent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.common.util.CompletableFutureUtil;
import java.util.concurrent.Executor;
import java.util.concurrent.CompletableFuture;
import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.prefab.event.PrefabPasteEvent;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.ints.IntSet;
import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall;
import com.hypixel.hytale.server.core.prefab.PrefabRotation;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor;
import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor;
import com.hypixel.hytale.math.util.MathUtil;
import java.util.Random;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.universe.world.World;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer;
import java.util.concurrent.atomic.AtomicInteger;

public class PrefabUtil
{
    protected static final String EDITOR_BLOCK = "Editor_Block";
    protected static final String EDITOR_BLOCK_PREFAB_AIR = "Editor_Empty";
    protected static final String EDITOR_BLOCK_PREFAB_ANCHOR = "Editor_Anchor";
    private static final AtomicInteger PREFAB_ID_SOURCE;
    
    public static boolean prefabMatchesAtPosition(@Nonnull final IPrefabBuffer prefabBuffer, final World world, @Nonnull final Vector3i position, @Nonnull final Rotation yaw, final Random random) {
        final double xLength = prefabBuffer.getMaxX() - prefabBuffer.getMinX();
        final double zLength = prefabBuffer.getMaxZ() - prefabBuffer.getMinZ();
        final int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength));
        final LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.getX(), position.getZ(), prefabRadius);
        return prefabBuffer.compare((x, y, z, blockId, rotation, holder, prefabBufferCall) -> {
            final int bx = position.x + x;
            final int by = position.y + y;
            final int bz = position.z + z;
            final WorldChunk chunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
            final int blockIdAtPos = chunk.getBlock(bx, by, bz);
            return blockIdAtPos == blockId;
        }, new PrefabBufferCall(random, PrefabRotation.fromRotation(yaw)));
    }
    
    public static boolean canPlacePrefab(@Nonnull final IPrefabBuffer prefabBuffer, final World world, @Nonnull final Vector3i position, @Nonnull final Rotation yaw, @Nullable final IntSet mask, final Random random, final boolean ignoreOrigin) {
        final double xLength = prefabBuffer.getMaxX() - prefabBuffer.getMinX();
        final double zLength = prefabBuffer.getMaxZ() - prefabBuffer.getMinZ();
        final int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength));
        final LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.getX(), position.getZ(), prefabRadius);
        return prefabBuffer.compare((x, y, z, blockId, rotation, holder, prefabBufferCall) -> {
            if (ignoreOrigin && x == 0 && y == 0 && z == 0) {
                return true;
            }
            else {
                final int bx = position.x + x;
                final int by = position.y + y;
                final int bz = position.z + z;
                final WorldChunk chunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
                return chunk.testPlaceBlock(bx, by, bz, BlockType.getAssetMap().getAsset(blockId), rotation, (x1, y1, z1, blockType, _rotation, filler) -> mask != null && mask.contains(BlockType.getAssetMap().getIndex(blockType.getId())));
            }
        }, new PrefabBufferCall(random, PrefabRotation.fromRotation(yaw)));
    }
    
    public static void paste(@Nonnull final IPrefabBuffer buffer, @Nonnull final World world, @Nonnull final Vector3i position, @Nonnull final Rotation yaw, final boolean force, final Random random, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        paste(buffer, world, position, yaw, force, random, 0, componentAccessor);
    }
    
    public static void paste(@Nonnull final IPrefabBuffer buffer, @Nonnull final World world, @Nonnull final Vector3i position, @Nonnull final Rotation yaw, final boolean force, final Random random, final int setBlockSettings, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        paste(buffer, world, position, yaw, force, random, setBlockSettings, false, false, false, componentAccessor);
    }
    
    public static int getNextPrefabId() {
        return PrefabUtil.PREFAB_ID_SOURCE.getAndIncrement();
    }
    
    public static void paste(@Nonnull final IPrefabBuffer buffer, @Nonnull final World world, @Nonnull final Vector3i position, @Nonnull final Rotation yaw, final boolean force, final Random random, final int setBlockSettings, final boolean technicalPaste, final boolean pasteAnchorAsBlock, final boolean loadEntities, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final double xLength = buffer.getMaxX() - buffer.getMinX();
        final double zLength = buffer.getMaxZ() - buffer.getMinZ();
        final int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength));
        final LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.getX(), position.getZ(), prefabRadius);
        final BlockTypeAssetMap<String, BlockType> blockTypeMap = BlockType.getAssetMap();
        final int editorBlock = blockTypeMap.getIndex("Editor_Block");
        if (editorBlock == Integer.MIN_VALUE) {
            throw new IllegalArgumentException("Unknown key! Editor_Block");
        }
        final PrefabRotation rotation = PrefabRotation.fromRotation(yaw);
        final int prefabId = getNextPrefabId();
        final PrefabPasteEvent startEvent = new PrefabPasteEvent(prefabId, true);
        componentAccessor.invoke(startEvent);
        if (startEvent.isCancelled()) {
            return;
        }
        buffer.forEach(IPrefabBuffer.iterateAllColumns(), (x, y, z, blockId, holder, supportValue, blockRotation, filler, call, fluidId, fluidLevel) -> {
            final int bx = position.x + x;
            final int by = position.y + y;
            final int bz = position.z + z;
            final WorldChunk chunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
            final Store<ChunkStore> fluidStore = world.getChunkStore().getStore();
            final ChunkColumn fluidColumn = fluidStore.getComponent(chunk.getReference(), ChunkColumn.getComponentType());
            final Ref<ChunkStore> section = fluidColumn.getSection(ChunkUtil.chunkCoordinate(by));
            final FluidSection fluidSection = fluidStore.ensureAndGetComponent(section, FluidSection.getComponentType());
            fluidSection.setFluid(bx, by, bz, fluidId, (byte)fluidLevel);
            BlockType block;
            if (technicalPaste) {
                if (blockId == 0 && fluidId == 0) {
                    block = (BlockType)blockTypeMap.getAsset("Editor_Empty");
                }
                else {
                    block = blockTypeMap.getAsset(blockId);
                }
            }
            else {
                block = blockTypeMap.getAsset(blockId);
            }
            final String blockKey = block.getId();
            if (filler != 0) {
                if (holder != null) {
                    chunk.setState(bx, by, bz, holder.clone());
                }
                return;
            }
            else {
                if (pasteAnchorAsBlock && technicalPaste && x == buffer.getAnchorX() && y == buffer.getAnchorY() && z == buffer.getAnchorZ()) {
                    final int index = blockTypeMap.getIndex("Editor_Anchor");
                    final BlockType type = blockTypeMap.getAsset(index);
                    chunk.setBlock(bx, by, bz, index, type, blockRotation, filler, setBlockSettings);
                }
                else if (!force) {
                    final RotationTuple rot = RotationTuple.get(blockRotation);
                    chunk.placeBlock(bx, by, bz, blockKey, rot.yaw(), rot.pitch(), rot.roll(), setBlockSettings);
                }
                else {
                    final int index2 = blockTypeMap.getIndex(blockKey);
                    final BlockType type2 = blockTypeMap.getAsset(index2);
                    chunk.setBlock(bx, by, bz, index2, type2, blockRotation, filler, setBlockSettings);
                }
                if (supportValue != 0) {
                    if (!world.isInThread()) {
                        CompletableFutureUtil._catch(CompletableFuture.runAsync(() -> {
                            final Ref<ChunkStore> ref2 = chunk.getReference();
                            final Store<ChunkStore> store2 = ref2.getStore();
                            final ChunkColumn column2 = store2.getComponent(ref2, ChunkColumn.getComponentType());
                            BlockPhysics.setSupportValue(store2, column2.getSection(ChunkUtil.chunkCoordinate(by)), bx, by, bz, supportValue);
                            return;
                        }, world));
                    }
                    else {
                        final Ref<ChunkStore> ref = chunk.getReference();
                        final Store<ChunkStore> store = ref.getStore();
                        final ChunkColumn column = store.getComponent(ref, ChunkColumn.getComponentType());
                        BlockPhysics.setSupportValue(store, column.getSection(ChunkUtil.chunkCoordinate(by)), bx, by, bz, supportValue);
                    }
                }
                if (holder != null) {
                    chunk.setState(bx, by, bz, holder.clone());
                }
                return;
            }
        }, (x, z, entityWrappers, t) -> {
            if (!loadEntities) {
                return;
            }
            else if (entityWrappers == null || entityWrappers.length == 0) {
                return;
            }
            else {
                for (int i = 0; i < entityWrappers.length; ++i) {
                    final Holder<EntityStore> entityToAdd = entityWrappers[i].clone();
                    final TransformComponent transformComp = entityToAdd.getComponent(TransformComponent.getComponentType());
                    if (transformComp != null) {
                        final Vector3d entityPosition = transformComp.getPosition().clone();
                        rotation.rotate(entityPosition);
                        final Vector3d entityWorldPosition = entityPosition.add(position);
                        final TransformComponent transformComp2 = entityToAdd.getComponent(TransformComponent.getComponentType());
                        if (transformComp2 != null) {
                            final Vector3d entityPosition2 = transformComp2.getPosition();
                            entityPosition2.x = entityWorldPosition.x;
                            entityPosition2.y = entityWorldPosition.y;
                            entityPosition2.z = entityWorldPosition.z;
                            final PrefabPlaceEntityEvent prefabPlaceEntityEvent = new PrefabPlaceEntityEvent(prefabId, entityToAdd);
                            componentAccessor.invoke(prefabPlaceEntityEvent);
                            entityToAdd.ensureComponent(FromPrefab.getComponentType());
                            if (technicalPaste) {
                                entityToAdd.ensureComponent(PrefabCopyableComponent.getComponentType());
                            }
                            componentAccessor.addEntity(entityToAdd, AddReason.LOAD);
                        }
                    }
                }
                return;
            }
        }, (x, y, z, path, fitHeightmap, inheritSeed, inheritHeightCondition, weights, rot, t) -> {}, new PrefabBufferCall(random, rotation));
        final PrefabPasteEvent endEvent = new PrefabPasteEvent(prefabId, false);
        componentAccessor.invoke(endEvent);
    }
    
    public static void remove(@Nonnull final IPrefabBuffer prefabBuffer, @Nonnull final World world, @Nonnull final Vector3i position, final boolean force, @Nonnull final Random random, final int setBlockSettings) {
        remove(prefabBuffer, world, position, force, random, setBlockSettings, 1.0);
    }
    
    public static void remove(@Nonnull final IPrefabBuffer prefabBuffer, @Nonnull final World world, @Nonnull final Vector3i position, final boolean force, @Nonnull final Random random, final int setBlockSettings, final double brokenParticlesRate) {
    }
    
    public static void remove(@Nonnull final IPrefabBuffer prefabBuffer, @Nonnull final World world, @Nonnull final Vector3i position, final Rotation prefabRotation, final boolean force, @Nonnull final Random random, final int setBlockSettings, final double brokenParticlesRate) {
        final double xLength = prefabBuffer.getMaxX() - prefabBuffer.getMinX();
        final double zLength = prefabBuffer.getMaxZ() - prefabBuffer.getMinZ();
        final int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength));
        final LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.getX(), position.getZ(), prefabRadius);
        final BlockTypeAssetMap<String, BlockType> blockTypeMap = BlockType.getAssetMap();
        prefabBuffer.forEach(IPrefabBuffer.iterateAllColumns(), (x, y, z, blockId, state, support, rotation, filler, call, fluidId, fluidLevel) -> {
            final int bx = position.x + x;
            final int by = position.y + y;
            final int bz = position.z + z;
            final WorldChunk chunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
            final Store<ChunkStore> store = world.getChunkStore().getStore();
            if (fluidId != 0) {
                final ChunkColumn column = store.getComponent(chunk.getReference(), ChunkColumn.getComponentType());
                final Ref<ChunkStore> section = column.getSection(ChunkUtil.chunkCoordinate(by));
                final FluidSection fluidSection = store.ensureAndGetComponent(section, FluidSection.getComponentType());
                fluidSection.setFluid(bx, by, bz, 0, (byte)0);
            }
            if (blockId != 0) {
                if (filler == 0) {
                    int updatedSetBlockSettings = setBlockSettings;
                    if ((setBlockSettings & 0x4) != 0x4 && random.nextDouble() > brokenParticlesRate) {
                        updatedSetBlockSettings |= 0x4;
                    }
                    if (!force) {
                        chunk.breakBlock(bx, by, bz, updatedSetBlockSettings);
                    }
                    else {
                        chunk.setBlock(bx, by, bz, "Empty", updatedSetBlockSettings);
                    }
                }
            }
        }, (x, z, entityWrappers, t) -> {}, (x, y, z, path, fitHeightmap, inheritSeed, inheritHeightCondition, weights, rotation, t) -> {}, new PrefabBufferCall(random, PrefabRotation.fromRotation(prefabRotation)));
    }
    
    static {
        PREFAB_ID_SOURCE = new AtomicInteger(0);
    }
}
