package net.minecraft.world.level.levelgen.structure.templatesystem;

import java.util.AbstractList;
import net.minecraft.core.IdMapper;
import net.minecraft.nbt.IntTag;
import java.util.Comparator;
import net.minecraft.SharedConstants;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.world.entity.EntityType;
import java.util.Optional;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.LiquidBlockContainer;
import com.mojang.datafixers.util.Pair;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.Clearable;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.entity.decoration.Painting;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import java.util.Iterator;
import java.util.Collection;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.Vec3i;
import javax.annotation.Nullable;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.Level;
import com.google.common.collect.Lists;
import net.minecraft.core.BlockPos;
import java.util.List;

public class StructureTemplate {
    private final List<List<StructureBlockInfo>> palettes;
    private final List<StructureEntityInfo> entityInfoList;
    private BlockPos size;
    private String author;
    
    public StructureTemplate() {
        this.palettes = Lists.newArrayList();
        this.entityInfoList = Lists.newArrayList();
        this.size = BlockPos.ZERO;
        this.author = "?";
    }
    
    public BlockPos getSize() {
        return this.size;
    }
    
    public void setAuthor(final String string) {
        this.author = string;
    }
    
    public String getAuthor() {
        return this.author;
    }
    
    public void fillFromWorld(final Level bjw, final BlockPos fk2, final BlockPos fk3, final boolean boolean4, @Nullable final Block bph) {
        if (fk3.getX() < 1 || fk3.getY() < 1 || fk3.getZ() < 1) {
            return;
        }
        final BlockPos fk4 = fk2.offset(fk3).offset(-1, -1, -1);
        final List<StructureBlockInfo> list8 = Lists.newArrayList();
        final List<StructureBlockInfo> list9 = Lists.newArrayList();
        final List<StructureBlockInfo> list10 = Lists.newArrayList();
        final BlockPos fk5 = new BlockPos(Math.min(fk2.getX(), fk4.getX()), Math.min(fk2.getY(), fk4.getY()), Math.min(fk2.getZ(), fk4.getZ()));
        final BlockPos fk6 = new BlockPos(Math.max(fk2.getX(), fk4.getX()), Math.max(fk2.getY(), fk4.getY()), Math.max(fk2.getZ(), fk4.getZ()));
        this.size = fk3;
        for (final BlockPos fk7 : BlockPos.betweenClosed(fk5, fk6)) {
            final BlockPos fk8 = fk7.subtract(fk5);
            final BlockState byj16 = bjw.getBlockState(fk7);
            if (bph != null && bph == byj16.getBlock()) {
                continue;
            }
            final BlockEntity bwl17 = bjw.getBlockEntity(fk7);
            if (bwl17 != null) {
                final CompoundTag jt18 = bwl17.save(new CompoundTag());
                jt18.remove("x");
                jt18.remove("y");
                jt18.remove("z");
                list9.add(new StructureBlockInfo(fk8, byj16, jt18));
            }
            else if (byj16.isSolidRender(bjw, fk7) || byj16.isCollisionShapeFullBlock(bjw, fk7)) {
                list8.add(new StructureBlockInfo(fk8, byj16, null));
            }
            else {
                list10.add(new StructureBlockInfo(fk8, byj16, null));
            }
        }
        final List<StructureBlockInfo> list11 = Lists.newArrayList();
        list11.addAll(list8);
        list11.addAll(list9);
        list11.addAll(list10);
        this.palettes.clear();
        this.palettes.add(list11);
        if (boolean4) {
            this.fillEntityList(bjw, fk5, fk6.offset(1, 1, 1));
        }
        else {
            this.entityInfoList.clear();
        }
    }
    
    private void fillEntityList(final Level bjw, final BlockPos fk2, final BlockPos fk3) {
        final List<Entity> list5 = bjw.<Entity>getEntitiesOfClass(Entity.class, new AABB(fk2, fk3), akq -> !(akq instanceof Player));
        this.entityInfoList.clear();
        for (final Entity akq2 : list5) {
            final Vec3 cvl8 = new Vec3(akq2.getX() - fk2.getX(), akq2.getY() - fk2.getY(), akq2.getZ() - fk2.getZ());
            final CompoundTag jt9 = new CompoundTag();
            akq2.save(jt9);
            BlockPos fk4;
            if (akq2 instanceof Painting) {
                fk4 = ((Painting)akq2).getPos().subtract(fk2);
            }
            else {
                fk4 = new BlockPos(cvl8);
            }
            this.entityInfoList.add(new StructureEntityInfo(cvl8, fk4, jt9));
        }
    }
    
    public List<StructureBlockInfo> filterBlocks(final BlockPos fk, final StructurePlaceSettings cmp, final Block bph) {
        return this.filterBlocks(fk, cmp, bph, true);
    }
    
    public List<StructureBlockInfo> filterBlocks(final BlockPos fk, final StructurePlaceSettings cmp, final Block bph, final boolean boolean4) {
        final List<StructureBlockInfo> list6 = Lists.newArrayList();
        final BoundingBox clb7 = cmp.getBoundingBox();
        for (final StructureBlockInfo b9 : cmp.getRandomPalette(this.palettes, fk)) {
            final BlockPos fk2 = boolean4 ? calculateRelativePosition(cmp, b9.pos).offset(fk) : b9.pos;
            if (clb7 != null && !clb7.isInside(fk2)) {
                continue;
            }
            final BlockState byj11 = b9.state;
            if (byj11.getBlock() != bph) {
                continue;
            }
            list6.add(new StructureBlockInfo(fk2, byj11.rotate(cmp.getRotation()), b9.nbt));
        }
        return list6;
    }
    
    public BlockPos calculateConnectedPosition(final StructurePlaceSettings cmp1, final BlockPos fk2, final StructurePlaceSettings cmp3, final BlockPos fk4) {
        final BlockPos fk5 = calculateRelativePosition(cmp1, fk2);
        final BlockPos fk6 = calculateRelativePosition(cmp3, fk4);
        return fk5.subtract(fk6);
    }
    
    public static BlockPos calculateRelativePosition(final StructurePlaceSettings cmp, final BlockPos fk) {
        return transform(fk, cmp.getMirror(), cmp.getRotation(), cmp.getRotationPivot());
    }
    
    public void placeInWorldChunk(final LevelAccessor bjx, final BlockPos fk, final StructurePlaceSettings cmp) {
        cmp.updateBoundingBoxFromChunkPos();
        this.placeInWorld(bjx, fk, cmp);
    }
    
    public void placeInWorld(final LevelAccessor bjx, final BlockPos fk, final StructurePlaceSettings cmp) {
        this.placeInWorld(bjx, fk, cmp, 2);
    }
    
    public boolean placeInWorld(final LevelAccessor bjx, final BlockPos fk, final StructurePlaceSettings cmp, final int integer) {
        if (this.palettes.isEmpty()) {
            return false;
        }
        final List<StructureBlockInfo> list6 = cmp.getRandomPalette(this.palettes, fk);
        if ((list6.isEmpty() && (cmp.isIgnoreEntities() || this.entityInfoList.isEmpty())) || this.size.getX() < 1 || this.size.getY() < 1 || this.size.getZ() < 1) {
            return false;
        }
        final BoundingBox clb7 = cmp.getBoundingBox();
        final List<BlockPos> list7 = Lists.newArrayListWithCapacity(cmp.shouldKeepLiquids() ? list6.size() : 0);
        final List<Pair<BlockPos, CompoundTag>> list8 = Lists.newArrayListWithCapacity(list6.size());
        int integer2 = Integer.MAX_VALUE;
        int integer3 = Integer.MAX_VALUE;
        int integer4 = Integer.MAX_VALUE;
        int integer5 = Integer.MIN_VALUE;
        int integer6 = Integer.MIN_VALUE;
        int integer7 = Integer.MIN_VALUE;
        final List<StructureBlockInfo> list9 = processBlockInfos(bjx, fk, cmp, list6);
        for (final StructureBlockInfo b18 : list9) {
            final BlockPos fk2 = b18.pos;
            if (clb7 != null && !clb7.isInside(fk2)) {
                continue;
            }
            final FluidState coj20 = cmp.shouldKeepLiquids() ? bjx.getFluidState(fk2) : null;
            final BlockState byj21 = b18.state.mirror(cmp.getMirror()).rotate(cmp.getRotation());
            if (b18.nbt != null) {
                final BlockEntity bwl22 = bjx.getBlockEntity(fk2);
                Clearable.tryClear(bwl22);
                bjx.setBlock(fk2, Blocks.BARRIER.defaultBlockState(), 20);
            }
            if (!bjx.setBlock(fk2, byj21, integer)) {
                continue;
            }
            integer2 = Math.min(integer2, fk2.getX());
            integer3 = Math.min(integer3, fk2.getY());
            integer4 = Math.min(integer4, fk2.getZ());
            integer5 = Math.max(integer5, fk2.getX());
            integer6 = Math.max(integer6, fk2.getY());
            integer7 = Math.max(integer7, fk2.getZ());
            list8.add((Pair<BlockPos, CompoundTag>)Pair.of(fk2, b18.nbt));
            if (b18.nbt != null) {
                final BlockEntity bwl22 = bjx.getBlockEntity(fk2);
                if (bwl22 != null) {
                    b18.nbt.putInt("x", fk2.getX());
                    b18.nbt.putInt("y", fk2.getY());
                    b18.nbt.putInt("z", fk2.getZ());
                    bwl22.load(b18.nbt);
                    bwl22.mirror(cmp.getMirror());
                    bwl22.rotate(cmp.getRotation());
                }
            }
            if (coj20 == null || !(byj21.getBlock() instanceof LiquidBlockContainer)) {
                continue;
            }
            ((LiquidBlockContainer)byj21.getBlock()).placeLiquid(bjx, fk2, byj21, coj20);
            if (coj20.isSource()) {
                continue;
            }
            list7.add(fk2);
        }
        boolean boolean17 = true;
        final Direction[] arr18 = { Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST };
        while (boolean17 && !list7.isEmpty()) {
            boolean17 = false;
            final Iterator<BlockPos> iterator19 = list7.iterator();
            while (iterator19.hasNext()) {
                BlockPos fk4;
                final BlockPos fk3 = fk4 = iterator19.next();
                FluidState coj21 = bjx.getFluidState(fk4);
                for (int integer8 = 0; integer8 < arr18.length && !coj21.isSource(); ++integer8) {
                    final BlockPos fk5 = fk4.relative(arr18[integer8]);
                    final FluidState coj22 = bjx.getFluidState(fk5);
                    if (coj22.getHeight(bjx, fk5) > coj21.getHeight(bjx, fk4) || (coj22.isSource() && !coj21.isSource())) {
                        coj21 = coj22;
                        fk4 = fk5;
                    }
                }
                if (coj21.isSource()) {
                    final BlockState byj22 = bjx.getBlockState(fk3);
                    final Block bph24 = byj22.getBlock();
                    if (!(bph24 instanceof LiquidBlockContainer)) {
                        continue;
                    }
                    ((LiquidBlockContainer)bph24).placeLiquid(bjx, fk3, byj22, coj21);
                    boolean17 = true;
                    iterator19.remove();
                }
            }
        }
        if (integer2 <= integer5) {
            if (!cmp.getKnownShape()) {
                final DiscreteVoxelShape cvu19 = new BitSetDiscreteVoxelShape(integer5 - integer2 + 1, integer6 - integer3 + 1, integer7 - integer4 + 1);
                final int integer9 = integer2;
                final int integer10 = integer3;
                final int integer11 = integer4;
                for (final Pair<BlockPos, CompoundTag> pair24 : list8) {
                    final BlockPos fk6 = (BlockPos)pair24.getFirst();
                    cvu19.setFull(fk6.getX() - integer9, fk6.getY() - integer10, fk6.getZ() - integer11, true, true);
                }
                updateShapeAtEdge(bjx, integer, cvu19, integer9, integer10, integer11);
            }
            for (final Pair<BlockPos, CompoundTag> pair25 : list8) {
                final BlockPos fk4 = (BlockPos)pair25.getFirst();
                if (!cmp.getKnownShape()) {
                    final BlockState byj23 = bjx.getBlockState(fk4);
                    final BlockState byj22 = Block.updateFromNeighbourShapes(byj23, bjx, fk4);
                    if (byj23 != byj22) {
                        bjx.setBlock(fk4, byj22, (integer & 0xFFFFFFFE) | 0x10);
                    }
                    bjx.blockUpdated(fk4, byj22.getBlock());
                }
                if (pair25.getSecond() != null) {
                    final BlockEntity bwl22 = bjx.getBlockEntity(fk4);
                    if (bwl22 == null) {
                        continue;
                    }
                    bwl22.setChanged();
                }
            }
        }
        if (!cmp.isIgnoreEntities()) {
            this.placeEntities(bjx, fk, cmp.getMirror(), cmp.getRotation(), cmp.getRotationPivot(), clb7);
        }
        return true;
    }
    
    public static void updateShapeAtEdge(final LevelAccessor bjx, final int integer2, final DiscreteVoxelShape cvu, final int integer4, final int integer5, final int integer6) {
        final BlockPos fk10;
        final BlockPos fk11;
        final BlockState byj12;
        final BlockState byj13;
        final BlockState byj14;
        final BlockState byj15;
        cvu.forAllFaces((fp, integer7, integer8, integer9) -> {
            fk10 = new BlockPos(integer4 + integer7, integer5 + integer8, integer6 + integer9);
            fk11 = fk10.relative(fp);
            byj12 = bjx.getBlockState(fk10);
            byj13 = bjx.getBlockState(fk11);
            byj14 = byj12.updateShape(fp, byj13, bjx, fk10, fk11);
            if (byj12 != byj14) {
                bjx.setBlock(fk10, byj14, (integer2 & 0xFFFFFFFE) | 0x10);
            }
            byj15 = byj13.updateShape(fp.getOpposite(), byj14, bjx, fk11, fk10);
            if (byj13 != byj15) {
                bjx.setBlock(fk11, byj15, (integer2 & 0xFFFFFFFE) | 0x10);
            }
        });
    }
    
    public static List<StructureBlockInfo> processBlockInfos(final LevelAccessor bjx, final BlockPos fk, final StructurePlaceSettings cmp, final List<StructureBlockInfo> list) {
        final List<StructureBlockInfo> list2 = Lists.newArrayList();
        for (final StructureBlockInfo b7 : list) {
            final BlockPos fk2 = calculateRelativePosition(cmp, b7.pos).offset(fk);
            StructureBlockInfo b8 = new StructureBlockInfo(fk2, b7.state, b7.nbt);
            for (Iterator<StructureProcessor> iterator10 = cmp.getProcessors().iterator(); b8 != null && iterator10.hasNext(); b8 = iterator10.next().processBlock(bjx, fk, b7, b8, cmp)) {}
            if (b8 != null) {
                list2.add(b8);
            }
        }
        return list2;
    }
    
    private void placeEntities(final LevelAccessor bjx, final BlockPos fk2, final Mirror bsu, final Rotation btu, final BlockPos fk5, @Nullable final BoundingBox clb) {
        for (final StructureEntityInfo c9 : this.entityInfoList) {
            final BlockPos fk6 = transform(c9.blockPos, bsu, btu, fk5).offset(fk2);
            if (clb != null && !clb.isInside(fk6)) {
                continue;
            }
            final CompoundTag jt11 = c9.nbt;
            final Vec3 cvl12 = transform(c9.pos, bsu, btu, fk5);
            final Vec3 cvl13 = cvl12.add(fk2.getX(), fk2.getY(), fk2.getZ());
            final ListTag jz14 = new ListTag();
            ((AbstractList<DoubleTag>)jz14).add(DoubleTag.valueOf(cvl13.x));
            ((AbstractList<DoubleTag>)jz14).add(DoubleTag.valueOf(cvl13.y));
            ((AbstractList<DoubleTag>)jz14).add(DoubleTag.valueOf(cvl13.z));
            jt11.put("Pos", jz14);
            jt11.remove("UUIDMost");
            jt11.remove("UUIDLeast");
            final float float6;
            final float float7;
            final Vec3 vec3;
            createEntityIgnoreException(bjx, jt11).ifPresent(akq -> {
                float6 = akq.mirror(bsu);
                float7 = float6 + (akq.yRot - akq.rotate(btu));
                akq.moveTo(vec3.x, vec3.y, vec3.z, float7, akq.xRot);
                bjx.addFreshEntity(akq);
            });
        }
    }
    
    private static Optional<Entity> createEntityIgnoreException(final LevelAccessor bjx, final CompoundTag jt) {
        try {
            return EntityType.create(jt, bjx.getLevel());
        }
        catch (Exception exception3) {
            return Optional.<Entity>empty();
        }
    }
    
    public BlockPos getSize(final Rotation btu) {
        switch (btu) {
            case COUNTERCLOCKWISE_90:
            case CLOCKWISE_90: {
                return new BlockPos(this.size.getZ(), this.size.getY(), this.size.getX());
            }
            default: {
                return this.size;
            }
        }
    }
    
    public static BlockPos transform(final BlockPos fk1, final Mirror bsu, final Rotation btu, final BlockPos fk4) {
        int integer5 = fk1.getX();
        final int integer6 = fk1.getY();
        int integer7 = fk1.getZ();
        boolean boolean8 = true;
        switch (bsu) {
            case LEFT_RIGHT: {
                integer7 = -integer7;
                break;
            }
            case FRONT_BACK: {
                integer5 = -integer5;
                break;
            }
            default: {
                boolean8 = false;
                break;
            }
        }
        final int integer8 = fk4.getX();
        final int integer9 = fk4.getZ();
        switch (btu) {
            case CLOCKWISE_180: {
                return new BlockPos(integer8 + integer8 - integer5, integer6, integer9 + integer9 - integer7);
            }
            case COUNTERCLOCKWISE_90: {
                return new BlockPos(integer8 - integer9 + integer7, integer6, integer8 + integer9 - integer5);
            }
            case CLOCKWISE_90: {
                return new BlockPos(integer8 + integer9 - integer7, integer6, integer9 - integer8 + integer5);
            }
            default: {
                return boolean8 ? new BlockPos(integer5, integer6, integer7) : fk1;
            }
        }
    }
    
    private static Vec3 transform(final Vec3 cvl, final Mirror bsu, final Rotation btu, final BlockPos fk) {
        double double5 = cvl.x;
        final double double6 = cvl.y;
        double double7 = cvl.z;
        boolean boolean11 = true;
        switch (bsu) {
            case LEFT_RIGHT: {
                double7 = 1.0 - double7;
                break;
            }
            case FRONT_BACK: {
                double5 = 1.0 - double5;
                break;
            }
            default: {
                boolean11 = false;
                break;
            }
        }
        final int integer12 = fk.getX();
        final int integer13 = fk.getZ();
        switch (btu) {
            case CLOCKWISE_180: {
                return new Vec3(integer12 + integer12 + 1 - double5, double6, integer13 + integer13 + 1 - double7);
            }
            case COUNTERCLOCKWISE_90: {
                return new Vec3(integer12 - integer13 + double7, double6, integer12 + integer13 + 1 - double5);
            }
            case CLOCKWISE_90: {
                return new Vec3(integer12 + integer13 + 1 - double7, double6, integer13 - integer12 + double5);
            }
            default: {
                return boolean11 ? new Vec3(double5, double6, double7) : cvl;
            }
        }
    }
    
    public BlockPos getZeroPositionWithTransform(final BlockPos fk, final Mirror bsu, final Rotation btu) {
        return getZeroPositionWithTransform(fk, bsu, btu, this.getSize().getX(), this.getSize().getZ());
    }
    
    public static BlockPos getZeroPositionWithTransform(final BlockPos fk, final Mirror bsu, final Rotation btu, int integer4, int integer5) {
        --integer4;
        --integer5;
        final int integer6 = (bsu == Mirror.FRONT_BACK) ? integer4 : 0;
        final int integer7 = (bsu == Mirror.LEFT_RIGHT) ? integer5 : 0;
        BlockPos fk2 = fk;
        switch (btu) {
            case NONE: {
                fk2 = fk.offset(integer6, 0, integer7);
                break;
            }
            case CLOCKWISE_90: {
                fk2 = fk.offset(integer5 - integer7, 0, integer6);
                break;
            }
            case CLOCKWISE_180: {
                fk2 = fk.offset(integer4 - integer6, 0, integer5 - integer7);
                break;
            }
            case COUNTERCLOCKWISE_90: {
                fk2 = fk.offset(integer7, 0, integer4 - integer6);
                break;
            }
        }
        return fk2;
    }
    
    public BoundingBox getBoundingBox(final StructurePlaceSettings cmp, final BlockPos fk) {
        final Rotation btu4 = cmp.getRotation();
        final BlockPos fk2 = cmp.getRotationPivot();
        final BlockPos fk3 = this.getSize(btu4);
        final Mirror bsu7 = cmp.getMirror();
        final int integer8 = fk2.getX();
        final int integer9 = fk2.getZ();
        final int integer10 = fk3.getX() - 1;
        final int integer11 = fk3.getY() - 1;
        final int integer12 = fk3.getZ() - 1;
        BoundingBox clb13 = new BoundingBox(0, 0, 0, 0, 0, 0);
        switch (btu4) {
            case NONE: {
                clb13 = new BoundingBox(0, 0, 0, integer10, integer11, integer12);
                break;
            }
            case CLOCKWISE_180: {
                clb13 = new BoundingBox(integer8 + integer8 - integer10, 0, integer9 + integer9 - integer12, integer8 + integer8, integer11, integer9 + integer9);
                break;
            }
            case COUNTERCLOCKWISE_90: {
                clb13 = new BoundingBox(integer8 - integer9, 0, integer8 + integer9 - integer12, integer8 - integer9 + integer10, integer11, integer8 + integer9);
                break;
            }
            case CLOCKWISE_90: {
                clb13 = new BoundingBox(integer8 + integer9 - integer10, 0, integer9 - integer8, integer8 + integer9, integer11, integer9 - integer8 + integer12);
                break;
            }
        }
        switch (bsu7) {
            case FRONT_BACK: {
                this.mirrorAABB(btu4, integer10, integer12, clb13, Direction.WEST, Direction.EAST);
                break;
            }
            case LEFT_RIGHT: {
                this.mirrorAABB(btu4, integer12, integer10, clb13, Direction.NORTH, Direction.SOUTH);
                break;
            }
        }
        clb13.move(fk.getX(), fk.getY(), fk.getZ());
        return clb13;
    }
    
    private void mirrorAABB(final Rotation btu, final int integer2, final int integer3, final BoundingBox clb, final Direction fp5, final Direction fp6) {
        BlockPos fk8 = BlockPos.ZERO;
        if (btu == Rotation.CLOCKWISE_90 || btu == Rotation.COUNTERCLOCKWISE_90) {
            fk8 = fk8.relative(btu.rotate(fp5), integer3);
        }
        else if (btu == Rotation.CLOCKWISE_180) {
            fk8 = fk8.relative(fp6, integer2);
        }
        else {
            fk8 = fk8.relative(fp5, integer2);
        }
        clb.move(fk8.getX(), 0, fk8.getZ());
    }
    
    public CompoundTag save(final CompoundTag jt) {
        if (this.palettes.isEmpty()) {
            jt.put("blocks", new ListTag());
            jt.put("palette", new ListTag());
        }
        else {
            final List<SimplePalette> list3 = Lists.newArrayList();
            final SimplePalette a4 = new SimplePalette();
            list3.add(a4);
            for (int integer5 = 1; integer5 < this.palettes.size(); ++integer5) {
                list3.add(new SimplePalette());
            }
            final ListTag jz5 = new ListTag();
            final List<StructureBlockInfo> list4 = this.palettes.get(0);
            for (int integer6 = 0; integer6 < list4.size(); ++integer6) {
                final StructureBlockInfo b8 = list4.get(integer6);
                final CompoundTag jt2 = new CompoundTag();
                jt2.put("pos", this.newIntegerList(b8.pos.getX(), b8.pos.getY(), b8.pos.getZ()));
                final int integer7 = a4.idFor(b8.state);
                jt2.putInt("state", integer7);
                if (b8.nbt != null) {
                    jt2.put("nbt", b8.nbt);
                }
                ((AbstractList<CompoundTag>)jz5).add(jt2);
                for (int integer8 = 1; integer8 < this.palettes.size(); ++integer8) {
                    final SimplePalette a5 = list3.get(integer8);
                    a5.addMapping(this.palettes.get(integer8).get(integer6).state, integer7);
                }
            }
            jt.put("blocks", jz5);
            if (list3.size() == 1) {
                final ListTag jz6 = new ListTag();
                for (final BlockState byj9 : a4) {
                    ((AbstractList<CompoundTag>)jz6).add(NbtUtils.writeBlockState(byj9));
                }
                jt.put("palette", jz6);
            }
            else {
                final ListTag jz6 = new ListTag();
                for (final SimplePalette a6 : list3) {
                    final ListTag jz7 = new ListTag();
                    for (final BlockState byj10 : a6) {
                        ((AbstractList<CompoundTag>)jz7).add(NbtUtils.writeBlockState(byj10));
                    }
                    ((AbstractList<ListTag>)jz6).add(jz7);
                }
                jt.put("palettes", jz6);
            }
        }
        final ListTag jz8 = new ListTag();
        for (final StructureEntityInfo c5 : this.entityInfoList) {
            final CompoundTag jt3 = new CompoundTag();
            jt3.put("pos", this.newDoubleList(c5.pos.x, c5.pos.y, c5.pos.z));
            jt3.put("blockPos", this.newIntegerList(c5.blockPos.getX(), c5.blockPos.getY(), c5.blockPos.getZ()));
            if (c5.nbt != null) {
                jt3.put("nbt", c5.nbt);
            }
            ((AbstractList<CompoundTag>)jz8).add(jt3);
        }
        jt.put("entities", jz8);
        jt.put("size", this.newIntegerList(this.size.getX(), this.size.getY(), this.size.getZ()));
        jt.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
        return jt;
    }
    
    public void load(final CompoundTag jt) {
        this.palettes.clear();
        this.entityInfoList.clear();
        final ListTag jz3 = jt.getList("size", 3);
        this.size = new BlockPos(jz3.getInt(0), jz3.getInt(1), jz3.getInt(2));
        final ListTag jz4 = jt.getList("blocks", 10);
        if (jt.contains("palettes", 9)) {
            final ListTag jz5 = jt.getList("palettes", 9);
            for (int integer6 = 0; integer6 < jz5.size(); ++integer6) {
                this.loadPalette(jz5.getList(integer6), jz4);
            }
        }
        else {
            this.loadPalette(jt.getList("palette", 10), jz4);
        }
        final ListTag jz5 = jt.getList("entities", 10);
        for (int integer6 = 0; integer6 < jz5.size(); ++integer6) {
            final CompoundTag jt2 = jz5.getCompound(integer6);
            final ListTag jz6 = jt2.getList("pos", 6);
            final Vec3 cvl9 = new Vec3(jz6.getDouble(0), jz6.getDouble(1), jz6.getDouble(2));
            final ListTag jz7 = jt2.getList("blockPos", 3);
            final BlockPos fk11 = new BlockPos(jz7.getInt(0), jz7.getInt(1), jz7.getInt(2));
            if (jt2.contains("nbt")) {
                final CompoundTag jt3 = jt2.getCompound("nbt");
                this.entityInfoList.add(new StructureEntityInfo(cvl9, fk11, jt3));
            }
        }
    }
    
    private void loadPalette(final ListTag jz1, final ListTag jz2) {
        final SimplePalette a4 = new SimplePalette();
        final List<StructureBlockInfo> list5 = Lists.newArrayList();
        for (int integer6 = 0; integer6 < jz1.size(); ++integer6) {
            a4.addMapping(NbtUtils.readBlockState(jz1.getCompound(integer6)), integer6);
        }
        for (int integer6 = 0; integer6 < jz2.size(); ++integer6) {
            final CompoundTag jt7 = jz2.getCompound(integer6);
            final ListTag jz3 = jt7.getList("pos", 3);
            final BlockPos fk9 = new BlockPos(jz3.getInt(0), jz3.getInt(1), jz3.getInt(2));
            final BlockState byj10 = a4.stateFor(jt7.getInt("state"));
            CompoundTag jt8;
            if (jt7.contains("nbt")) {
                jt8 = jt7.getCompound("nbt");
            }
            else {
                jt8 = null;
            }
            list5.add(new StructureBlockInfo(fk9, byj10, jt8));
        }
        list5.sort(Comparator.comparingInt(b -> b.pos.getY()));
        this.palettes.add(list5);
    }
    
    private ListTag newIntegerList(final int... arr) {
        final ListTag jz3 = new ListTag();
        for (final int integer7 : arr) {
            ((AbstractList<IntTag>)jz3).add(IntTag.valueOf(integer7));
        }
        return jz3;
    }
    
    private ListTag newDoubleList(final double... arr) {
        final ListTag jz3 = new ListTag();
        for (final double double7 : arr) {
            ((AbstractList<DoubleTag>)jz3).add(DoubleTag.valueOf(double7));
        }
        return jz3;
    }
    
    static class SimplePalette implements Iterable<BlockState> {
        public static final BlockState DEFAULT_BLOCK_STATE;
        private final IdMapper<BlockState> ids;
        private int lastId;
        
        private SimplePalette() {
            this.ids = new IdMapper<BlockState>(16);
        }
        
        public int idFor(final BlockState byj) {
            int integer3 = this.ids.getId(byj);
            if (integer3 == -1) {
                integer3 = this.lastId++;
                this.ids.addMapping(byj, integer3);
            }
            return integer3;
        }
        
        @Nullable
        public BlockState stateFor(final int integer) {
            final BlockState byj3 = this.ids.byId(integer);
            return (byj3 == null) ? SimplePalette.DEFAULT_BLOCK_STATE : byj3;
        }
        
        @Override
        public Iterator<BlockState> iterator() {
            return this.ids.iterator();
        }
        
        public void addMapping(final BlockState byj, final int integer) {
            this.ids.addMapping(byj, integer);
        }
        
        static {
            DEFAULT_BLOCK_STATE = Blocks.AIR.defaultBlockState();
        }
    }
    
    public static class StructureBlockInfo {
        public final BlockPos pos;
        public final BlockState state;
        public final CompoundTag nbt;
        
        public StructureBlockInfo(final BlockPos fk, final BlockState byj, @Nullable final CompoundTag jt) {
            this.pos = fk;
            this.state = byj;
            this.nbt = jt;
        }
        
        @Override
        public String toString() {
            return String.format("<StructureBlockInfo | %s | %s | %s>", this.pos, this.state, this.nbt);
        }
    }
    
    public static class StructureEntityInfo {
        public final Vec3 pos;
        public final BlockPos blockPos;
        public final CompoundTag nbt;
        
        public StructureEntityInfo(final Vec3 cvl, final BlockPos fk, final CompoundTag jt) {
            this.pos = cvl;
            this.blockPos = fk;
            this.nbt = jt;
        }
    }
}
