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

import java.util.AbstractList;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import com.google.common.collect.Maps;
import java.util.Map;
import net.minecraft.core.IdMapper;
import net.minecraft.nbt.IntTag;
import net.minecraft.SharedConstants;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.world.entity.EntityType;
import java.util.Optional;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.Mob;
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 net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
import com.mojang.datafixers.util.Pair;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.Clearable;
import java.util.Random;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import java.util.Collections;
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 java.util.Collection;
import java.util.Comparator;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import java.util.Iterator;
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<Palette> 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 bqb, final BlockPos fu2, final BlockPos fu3, final boolean boolean4, @Nullable final Block bvr) {
        if (fu3.getX() < 1 || fu3.getY() < 1 || fu3.getZ() < 1) {
            return;
        }
        final BlockPos fu4 = fu2.offset(fu3).offset(-1, -1, -1);
        final List<StructureBlockInfo> list8 = Lists.newArrayList();
        final List<StructureBlockInfo> list9 = Lists.newArrayList();
        final List<StructureBlockInfo> list10 = Lists.newArrayList();
        final BlockPos fu5 = new BlockPos(Math.min(fu2.getX(), fu4.getX()), Math.min(fu2.getY(), fu4.getY()), Math.min(fu2.getZ(), fu4.getZ()));
        final BlockPos fu6 = new BlockPos(Math.max(fu2.getX(), fu4.getX()), Math.max(fu2.getY(), fu4.getY()), Math.max(fu2.getZ(), fu4.getZ()));
        this.size = fu3;
        for (final BlockPos fu7 : BlockPos.betweenClosed(fu5, fu6)) {
            final BlockPos fu8 = fu7.subtract(fu5);
            final BlockState cfj16 = bqb.getBlockState(fu7);
            if (bvr != null && bvr == cfj16.getBlock()) {
                continue;
            }
            final BlockEntity cdl17 = bqb.getBlockEntity(fu7);
            StructureBlockInfo c18;
            if (cdl17 != null) {
                final CompoundTag le19 = cdl17.save(new CompoundTag());
                le19.remove("x");
                le19.remove("y");
                le19.remove("z");
                c18 = new StructureBlockInfo(fu8, cfj16, le19.copy());
            }
            else {
                c18 = new StructureBlockInfo(fu8, cfj16, null);
            }
            addToLists(c18, list8, list9, list10);
        }
        final List<StructureBlockInfo> list11 = buildInfoList(list8, list9, list10);
        this.palettes.clear();
        this.palettes.add(new Palette((List)list11));
        if (boolean4) {
            this.fillEntityList(bqb, fu5, fu6.offset(1, 1, 1));
        }
        else {
            this.entityInfoList.clear();
        }
    }
    
    private static void addToLists(final StructureBlockInfo c, final List<StructureBlockInfo> list2, final List<StructureBlockInfo> list3, final List<StructureBlockInfo> list4) {
        if (c.nbt != null) {
            list3.add(c);
        }
        else if (!c.state.getBlock().hasDynamicShape() && c.state.isCollisionShapeFullBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) {
            list2.add(c);
        }
        else {
            list4.add(c);
        }
    }
    
    private static List<StructureBlockInfo> buildInfoList(final List<StructureBlockInfo> list1, final List<StructureBlockInfo> list2, final List<StructureBlockInfo> list3) {
        final Comparator<StructureBlockInfo> comparator4 = Comparator.<StructureBlockInfo>comparingInt(c -> c.pos.getY()).thenComparingInt(c -> c.pos.getX()).thenComparingInt(c -> c.pos.getZ());
        list1.sort(comparator4);
        list3.sort(comparator4);
        list2.sort(comparator4);
        final List<StructureBlockInfo> list4 = Lists.newArrayList();
        list4.addAll(list1);
        list4.addAll(list3);
        list4.addAll(list2);
        return list4;
    }
    
    private void fillEntityList(final Level bqb, final BlockPos fu2, final BlockPos fu3) {
        final List<Entity> list5 = bqb.<Entity>getEntitiesOfClass(Entity.class, new AABB(fu2, fu3), aom -> !(aom instanceof Player));
        this.entityInfoList.clear();
        for (final Entity aom2 : list5) {
            final Vec3 dem8 = new Vec3(aom2.getX() - fu2.getX(), aom2.getY() - fu2.getY(), aom2.getZ() - fu2.getZ());
            final CompoundTag le9 = new CompoundTag();
            aom2.save(le9);
            BlockPos fu4;
            if (aom2 instanceof Painting) {
                fu4 = ((Painting)aom2).getPos().subtract(fu2);
            }
            else {
                fu4 = new BlockPos(dem8);
            }
            this.entityInfoList.add(new StructureEntityInfo(dem8, fu4, le9.copy()));
        }
    }
    
    public List<StructureBlockInfo> filterBlocks(final BlockPos fu, final StructurePlaceSettings cvb, final Block bvr) {
        return this.filterBlocks(fu, cvb, bvr, true);
    }
    
    public List<StructureBlockInfo> filterBlocks(final BlockPos fu, final StructurePlaceSettings cvb, final Block bvr, final boolean boolean4) {
        final List<StructureBlockInfo> list6 = Lists.newArrayList();
        final BoundingBox ctd7 = cvb.getBoundingBox();
        if (this.palettes.isEmpty()) {
            return Collections.<StructureBlockInfo>emptyList();
        }
        for (final StructureBlockInfo c9 : cvb.getRandomPalette(this.palettes, fu).blocks(bvr)) {
            final BlockPos fu2 = boolean4 ? calculateRelativePosition(cvb, c9.pos).offset(fu) : c9.pos;
            if (ctd7 != null && !ctd7.isInside(fu2)) {
                continue;
            }
            list6.add(new StructureBlockInfo(fu2, c9.state.rotate(cvb.getRotation()), c9.nbt));
        }
        return list6;
    }
    
    public BlockPos calculateConnectedPosition(final StructurePlaceSettings cvb1, final BlockPos fu2, final StructurePlaceSettings cvb3, final BlockPos fu4) {
        final BlockPos fu5 = calculateRelativePosition(cvb1, fu2);
        final BlockPos fu6 = calculateRelativePosition(cvb3, fu4);
        return fu5.subtract(fu6);
    }
    
    public static BlockPos calculateRelativePosition(final StructurePlaceSettings cvb, final BlockPos fu) {
        return transform(fu, cvb.getMirror(), cvb.getRotation(), cvb.getRotationPivot());
    }
    
    public void placeInWorldChunk(final LevelAccessor bqc, final BlockPos fu, final StructurePlaceSettings cvb, final Random random) {
        cvb.updateBoundingBoxFromChunkPos();
        this.placeInWorld(bqc, fu, cvb, random);
    }
    
    public void placeInWorld(final LevelAccessor bqc, final BlockPos fu, final StructurePlaceSettings cvb, final Random random) {
        this.placeInWorld(bqc, fu, fu, cvb, random, 2);
    }
    
    public boolean placeInWorld(final LevelAccessor bqc, final BlockPos fu2, final BlockPos fu3, final StructurePlaceSettings cvb, final Random random, final int integer) {
        if (this.palettes.isEmpty()) {
            return false;
        }
        final List<StructureBlockInfo> list8 = cvb.getRandomPalette(this.palettes, fu2).blocks();
        if ((list8.isEmpty() && (cvb.isIgnoreEntities() || this.entityInfoList.isEmpty())) || this.size.getX() < 1 || this.size.getY() < 1 || this.size.getZ() < 1) {
            return false;
        }
        final BoundingBox ctd9 = cvb.getBoundingBox();
        final List<BlockPos> list9 = Lists.newArrayListWithCapacity(cvb.shouldKeepLiquids() ? list8.size() : 0);
        final List<Pair<BlockPos, CompoundTag>> list10 = Lists.newArrayListWithCapacity(list8.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> list11 = processBlockInfos(bqc, fu2, fu3, cvb, list8);
        for (final StructureBlockInfo c20 : list11) {
            final BlockPos fu4 = c20.pos;
            if (ctd9 != null && !ctd9.isInside(fu4)) {
                continue;
            }
            final FluidState cxa22 = cvb.shouldKeepLiquids() ? bqc.getFluidState(fu4) : null;
            final BlockState cfj23 = c20.state.mirror(cvb.getMirror()).rotate(cvb.getRotation());
            if (c20.nbt != null) {
                final BlockEntity cdl24 = bqc.getBlockEntity(fu4);
                Clearable.tryClear(cdl24);
                bqc.setBlock(fu4, Blocks.BARRIER.defaultBlockState(), 20);
            }
            if (!bqc.setBlock(fu4, cfj23, integer)) {
                continue;
            }
            integer2 = Math.min(integer2, fu4.getX());
            integer3 = Math.min(integer3, fu4.getY());
            integer4 = Math.min(integer4, fu4.getZ());
            integer5 = Math.max(integer5, fu4.getX());
            integer6 = Math.max(integer6, fu4.getY());
            integer7 = Math.max(integer7, fu4.getZ());
            list10.add((Pair<BlockPos, CompoundTag>)Pair.of(fu4, c20.nbt));
            if (c20.nbt != null) {
                final BlockEntity cdl24 = bqc.getBlockEntity(fu4);
                if (cdl24 != null) {
                    c20.nbt.putInt("x", fu4.getX());
                    c20.nbt.putInt("y", fu4.getY());
                    c20.nbt.putInt("z", fu4.getZ());
                    if (cdl24 instanceof RandomizableContainerBlockEntity) {
                        c20.nbt.putLong("LootTableSeed", random.nextLong());
                    }
                    cdl24.load(c20.state, c20.nbt);
                    cdl24.mirror(cvb.getMirror());
                    cdl24.rotate(cvb.getRotation());
                }
            }
            if (cxa22 == null || !(cfj23.getBlock() instanceof LiquidBlockContainer)) {
                continue;
            }
            ((LiquidBlockContainer)cfj23.getBlock()).placeLiquid(bqc, fu4, cfj23, cxa22);
            if (cxa22.isSource()) {
                continue;
            }
            list9.add(fu4);
        }
        boolean boolean19 = true;
        final Direction[] arr20 = { Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST };
        while (boolean19 && !list9.isEmpty()) {
            boolean19 = false;
            final Iterator<BlockPos> iterator21 = list9.iterator();
            while (iterator21.hasNext()) {
                BlockPos fu6;
                final BlockPos fu5 = fu6 = iterator21.next();
                FluidState cxa23 = bqc.getFluidState(fu6);
                for (int integer8 = 0; integer8 < arr20.length && !cxa23.isSource(); ++integer8) {
                    final BlockPos fu7 = fu6.relative(arr20[integer8]);
                    final FluidState cxa24 = bqc.getFluidState(fu7);
                    if (cxa24.getHeight(bqc, fu7) > cxa23.getHeight(bqc, fu6) || (cxa24.isSource() && !cxa23.isSource())) {
                        cxa23 = cxa24;
                        fu6 = fu7;
                    }
                }
                if (cxa23.isSource()) {
                    final BlockState cfj24 = bqc.getBlockState(fu5);
                    final Block bvr26 = cfj24.getBlock();
                    if (!(bvr26 instanceof LiquidBlockContainer)) {
                        continue;
                    }
                    ((LiquidBlockContainer)bvr26).placeLiquid(bqc, fu5, cfj24, cxa23);
                    boolean19 = true;
                    iterator21.remove();
                }
            }
        }
        if (integer2 <= integer5) {
            if (!cvb.getKnownShape()) {
                final DiscreteVoxelShape dev21 = 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> pair26 : list10) {
                    final BlockPos fu8 = (BlockPos)pair26.getFirst();
                    dev21.setFull(fu8.getX() - integer9, fu8.getY() - integer10, fu8.getZ() - integer11, true, true);
                }
                updateShapeAtEdge(bqc, integer, dev21, integer9, integer10, integer11);
            }
            for (final Pair<BlockPos, CompoundTag> pair27 : list10) {
                final BlockPos fu6 = (BlockPos)pair27.getFirst();
                if (!cvb.getKnownShape()) {
                    final BlockState cfj25 = bqc.getBlockState(fu6);
                    final BlockState cfj24 = Block.updateFromNeighbourShapes(cfj25, bqc, fu6);
                    if (cfj25 != cfj24) {
                        bqc.setBlock(fu6, cfj24, (integer & 0xFFFFFFFE) | 0x10);
                    }
                    bqc.blockUpdated(fu6, cfj24.getBlock());
                }
                if (pair27.getSecond() != null) {
                    final BlockEntity cdl24 = bqc.getBlockEntity(fu6);
                    if (cdl24 == null) {
                        continue;
                    }
                    cdl24.setChanged();
                }
            }
        }
        if (!cvb.isIgnoreEntities()) {
            this.placeEntities(bqc, fu2, cvb.getMirror(), cvb.getRotation(), cvb.getRotationPivot(), ctd9, cvb.shouldFinalizeEntities());
        }
        return true;
    }
    
    public static void updateShapeAtEdge(final LevelAccessor bqc, final int integer2, final DiscreteVoxelShape dev, final int integer4, final int integer5, final int integer6) {
        final BlockPos fu10;
        final BlockPos fu11;
        final BlockState cfj12;
        final BlockState cfj13;
        final BlockState cfj14;
        final BlockState cfj15;
        dev.forAllFaces((fz, integer7, integer8, integer9) -> {
            fu10 = new BlockPos(integer4 + integer7, integer5 + integer8, integer6 + integer9);
            fu11 = fu10.relative(fz);
            cfj12 = bqc.getBlockState(fu10);
            cfj13 = bqc.getBlockState(fu11);
            cfj14 = cfj12.updateShape(fz, cfj13, bqc, fu10, fu11);
            if (cfj12 != cfj14) {
                bqc.setBlock(fu10, cfj14, integer2 & 0xFFFFFFFE);
            }
            cfj15 = cfj13.updateShape(fz.getOpposite(), cfj14, bqc, fu11, fu10);
            if (cfj13 != cfj15) {
                bqc.setBlock(fu11, cfj15, integer2 & 0xFFFFFFFE);
            }
        });
    }
    
    public static List<StructureBlockInfo> processBlockInfos(final LevelAccessor bqc, final BlockPos fu2, final BlockPos fu3, final StructurePlaceSettings cvb, final List<StructureBlockInfo> list) {
        final List<StructureBlockInfo> list2 = Lists.newArrayList();
        for (final StructureBlockInfo c8 : list) {
            final BlockPos fu4 = calculateRelativePosition(cvb, c8.pos).offset(fu2);
            StructureBlockInfo c9 = new StructureBlockInfo(fu4, c8.state, (c8.nbt != null) ? c8.nbt.copy() : null);
            for (Iterator<StructureProcessor> iterator11 = cvb.getProcessors().iterator(); c9 != null && iterator11.hasNext(); c9 = iterator11.next().processBlock(bqc, fu2, fu3, c8, c9, cvb)) {}
            if (c9 != null) {
                list2.add(c9);
            }
        }
        return list2;
    }
    
    private void placeEntities(final LevelAccessor bqc, final BlockPos fu2, final Mirror bzj, final Rotation cap, final BlockPos fu5, @Nullable final BoundingBox ctd, final boolean boolean7) {
        for (final StructureEntityInfo d10 : this.entityInfoList) {
            final BlockPos fu6 = transform(d10.blockPos, bzj, cap, fu5).offset(fu2);
            if (ctd != null && !ctd.isInside(fu6)) {
                continue;
            }
            final CompoundTag le12 = d10.nbt.copy();
            final Vec3 dem13 = transform(d10.pos, bzj, cap, fu5);
            final Vec3 dem14 = dem13.add(fu2.getX(), fu2.getY(), fu2.getZ());
            final ListTag lk15 = new ListTag();
            ((AbstractList<DoubleTag>)lk15).add(DoubleTag.valueOf(dem14.x));
            ((AbstractList<DoubleTag>)lk15).add(DoubleTag.valueOf(dem14.y));
            ((AbstractList<DoubleTag>)lk15).add(DoubleTag.valueOf(dem14.z));
            le12.put("Pos", lk15);
            le12.remove("UUID");
            final float float8;
            final float float9;
            final Vec3 dem15;
            final CompoundTag le13;
            createEntityIgnoreException(bqc, le12).ifPresent(aom -> {
                float8 = aom.mirror(bzj);
                float9 = float8 + (aom.yRot - aom.rotate(cap));
                aom.moveTo(dem15.x, dem15.y, dem15.z, float9, aom.xRot);
                if (boolean7 && aom instanceof Mob) {
                    aom.finalizeSpawn(bqc, bqc.getCurrentDifficultyAt(new BlockPos(dem15)), MobSpawnType.STRUCTURE, null, le13);
                }
                bqc.addFreshEntity(aom);
            });
        }
    }
    
    private static Optional<Entity> createEntityIgnoreException(final LevelAccessor bqc, final CompoundTag le) {
        try {
            return EntityType.create(le, bqc.getLevel());
        }
        catch (Exception exception3) {
            return Optional.<Entity>empty();
        }
    }
    
    public BlockPos getSize(final Rotation cap) {
        switch (cap) {
            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 fu1, final Mirror bzj, final Rotation cap, final BlockPos fu4) {
        int integer5 = fu1.getX();
        final int integer6 = fu1.getY();
        int integer7 = fu1.getZ();
        boolean boolean8 = true;
        switch (bzj) {
            case LEFT_RIGHT: {
                integer7 = -integer7;
                break;
            }
            case FRONT_BACK: {
                integer5 = -integer5;
                break;
            }
            default: {
                boolean8 = false;
                break;
            }
        }
        final int integer8 = fu4.getX();
        final int integer9 = fu4.getZ();
        switch (cap) {
            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) : fu1;
            }
        }
    }
    
    public static Vec3 transform(final Vec3 dem, final Mirror bzj, final Rotation cap, final BlockPos fu) {
        double double5 = dem.x;
        final double double6 = dem.y;
        double double7 = dem.z;
        boolean boolean11 = true;
        switch (bzj) {
            case LEFT_RIGHT: {
                double7 = 1.0 - double7;
                break;
            }
            case FRONT_BACK: {
                double5 = 1.0 - double5;
                break;
            }
            default: {
                boolean11 = false;
                break;
            }
        }
        final int integer12 = fu.getX();
        final int integer13 = fu.getZ();
        switch (cap) {
            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) : dem;
            }
        }
    }
    
    public BlockPos getZeroPositionWithTransform(final BlockPos fu, final Mirror bzj, final Rotation cap) {
        return getZeroPositionWithTransform(fu, bzj, cap, this.getSize().getX(), this.getSize().getZ());
    }
    
    public static BlockPos getZeroPositionWithTransform(final BlockPos fu, final Mirror bzj, final Rotation cap, int integer4, int integer5) {
        --integer4;
        --integer5;
        final int integer6 = (bzj == Mirror.FRONT_BACK) ? integer4 : 0;
        final int integer7 = (bzj == Mirror.LEFT_RIGHT) ? integer5 : 0;
        BlockPos fu2 = fu;
        switch (cap) {
            case NONE: {
                fu2 = fu.offset(integer6, 0, integer7);
                break;
            }
            case CLOCKWISE_90: {
                fu2 = fu.offset(integer5 - integer7, 0, integer6);
                break;
            }
            case CLOCKWISE_180: {
                fu2 = fu.offset(integer4 - integer6, 0, integer5 - integer7);
                break;
            }
            case COUNTERCLOCKWISE_90: {
                fu2 = fu.offset(integer7, 0, integer4 - integer6);
                break;
            }
        }
        return fu2;
    }
    
    public BoundingBox getBoundingBox(final StructurePlaceSettings cvb, final BlockPos fu) {
        return this.getBoundingBox(fu, cvb.getRotation(), cvb.getRotationPivot(), cvb.getMirror());
    }
    
    public BoundingBox getBoundingBox(final BlockPos fu1, final Rotation cap, final BlockPos fu3, final Mirror bzj) {
        final BlockPos fu4 = this.getSize(cap);
        final int integer7 = fu3.getX();
        final int integer8 = fu3.getZ();
        final int integer9 = fu4.getX() - 1;
        final int integer10 = fu4.getY() - 1;
        final int integer11 = fu4.getZ() - 1;
        BoundingBox ctd12 = new BoundingBox(0, 0, 0, 0, 0, 0);
        switch (cap) {
            case NONE: {
                ctd12 = new BoundingBox(0, 0, 0, integer9, integer10, integer11);
                break;
            }
            case CLOCKWISE_180: {
                ctd12 = new BoundingBox(integer7 + integer7 - integer9, 0, integer8 + integer8 - integer11, integer7 + integer7, integer10, integer8 + integer8);
                break;
            }
            case COUNTERCLOCKWISE_90: {
                ctd12 = new BoundingBox(integer7 - integer8, 0, integer7 + integer8 - integer11, integer7 - integer8 + integer9, integer10, integer7 + integer8);
                break;
            }
            case CLOCKWISE_90: {
                ctd12 = new BoundingBox(integer7 + integer8 - integer9, 0, integer8 - integer7, integer7 + integer8, integer10, integer8 - integer7 + integer11);
                break;
            }
        }
        switch (bzj) {
            case FRONT_BACK: {
                this.mirrorAABB(cap, integer9, integer11, ctd12, Direction.WEST, Direction.EAST);
                break;
            }
            case LEFT_RIGHT: {
                this.mirrorAABB(cap, integer11, integer9, ctd12, Direction.NORTH, Direction.SOUTH);
                break;
            }
        }
        ctd12.move(fu1.getX(), fu1.getY(), fu1.getZ());
        return ctd12;
    }
    
    private void mirrorAABB(final Rotation cap, final int integer2, final int integer3, final BoundingBox ctd, final Direction fz5, final Direction fz6) {
        BlockPos fu8 = BlockPos.ZERO;
        if (cap == Rotation.CLOCKWISE_90 || cap == Rotation.COUNTERCLOCKWISE_90) {
            fu8 = fu8.relative(cap.rotate(fz5), integer3);
        }
        else if (cap == Rotation.CLOCKWISE_180) {
            fu8 = fu8.relative(fz6, integer2);
        }
        else {
            fu8 = fu8.relative(fz5, integer2);
        }
        ctd.move(fu8.getX(), 0, fu8.getZ());
    }
    
    public CompoundTag save(final CompoundTag le) {
        if (this.palettes.isEmpty()) {
            le.put("blocks", new ListTag());
            le.put("palette", new ListTag());
        }
        else {
            final List<SimplePalette> list3 = Lists.newArrayList();
            final SimplePalette b4 = new SimplePalette();
            list3.add(b4);
            for (int integer5 = 1; integer5 < this.palettes.size(); ++integer5) {
                list3.add(new SimplePalette());
            }
            final ListTag lk5 = new ListTag();
            final List<StructureBlockInfo> list4 = this.palettes.get(0).blocks();
            for (int integer6 = 0; integer6 < list4.size(); ++integer6) {
                final StructureBlockInfo c8 = list4.get(integer6);
                final CompoundTag le2 = new CompoundTag();
                le2.put("pos", this.newIntegerList(c8.pos.getX(), c8.pos.getY(), c8.pos.getZ()));
                final int integer7 = b4.idFor(c8.state);
                le2.putInt("state", integer7);
                if (c8.nbt != null) {
                    le2.put("nbt", c8.nbt);
                }
                ((AbstractList<CompoundTag>)lk5).add(le2);
                for (int integer8 = 1; integer8 < this.palettes.size(); ++integer8) {
                    final SimplePalette b5 = list3.get(integer8);
                    b5.addMapping(this.palettes.get(integer8).blocks().get(integer6).state, integer7);
                }
            }
            le.put("blocks", lk5);
            if (list3.size() == 1) {
                final ListTag lk6 = new ListTag();
                for (final BlockState cfj9 : b4) {
                    ((AbstractList<CompoundTag>)lk6).add(NbtUtils.writeBlockState(cfj9));
                }
                le.put("palette", lk6);
            }
            else {
                final ListTag lk6 = new ListTag();
                for (final SimplePalette b6 : list3) {
                    final ListTag lk7 = new ListTag();
                    for (final BlockState cfj10 : b6) {
                        ((AbstractList<CompoundTag>)lk7).add(NbtUtils.writeBlockState(cfj10));
                    }
                    ((AbstractList<ListTag>)lk6).add(lk7);
                }
                le.put("palettes", lk6);
            }
        }
        final ListTag lk8 = new ListTag();
        for (final StructureEntityInfo d5 : this.entityInfoList) {
            final CompoundTag le3 = new CompoundTag();
            le3.put("pos", this.newDoubleList(d5.pos.x, d5.pos.y, d5.pos.z));
            le3.put("blockPos", this.newIntegerList(d5.blockPos.getX(), d5.blockPos.getY(), d5.blockPos.getZ()));
            if (d5.nbt != null) {
                le3.put("nbt", d5.nbt);
            }
            ((AbstractList<CompoundTag>)lk8).add(le3);
        }
        le.put("entities", lk8);
        le.put("size", this.newIntegerList(this.size.getX(), this.size.getY(), this.size.getZ()));
        le.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
        return le;
    }
    
    public void load(final CompoundTag le) {
        this.palettes.clear();
        this.entityInfoList.clear();
        final ListTag lk3 = le.getList("size", 3);
        this.size = new BlockPos(lk3.getInt(0), lk3.getInt(1), lk3.getInt(2));
        final ListTag lk4 = le.getList("blocks", 10);
        if (le.contains("palettes", 9)) {
            final ListTag lk5 = le.getList("palettes", 9);
            for (int integer6 = 0; integer6 < lk5.size(); ++integer6) {
                this.loadPalette(lk5.getList(integer6), lk4);
            }
        }
        else {
            this.loadPalette(le.getList("palette", 10), lk4);
        }
        final ListTag lk5 = le.getList("entities", 10);
        for (int integer6 = 0; integer6 < lk5.size(); ++integer6) {
            final CompoundTag le2 = lk5.getCompound(integer6);
            final ListTag lk6 = le2.getList("pos", 6);
            final Vec3 dem9 = new Vec3(lk6.getDouble(0), lk6.getDouble(1), lk6.getDouble(2));
            final ListTag lk7 = le2.getList("blockPos", 3);
            final BlockPos fu11 = new BlockPos(lk7.getInt(0), lk7.getInt(1), lk7.getInt(2));
            if (le2.contains("nbt")) {
                final CompoundTag le3 = le2.getCompound("nbt");
                this.entityInfoList.add(new StructureEntityInfo(dem9, fu11, le3));
            }
        }
    }
    
    private void loadPalette(final ListTag lk1, final ListTag lk2) {
        final SimplePalette b4 = new SimplePalette();
        for (int integer5 = 0; integer5 < lk1.size(); ++integer5) {
            b4.addMapping(NbtUtils.readBlockState(lk1.getCompound(integer5)), integer5);
        }
        final List<StructureBlockInfo> list5 = Lists.newArrayList();
        final List<StructureBlockInfo> list6 = Lists.newArrayList();
        final List<StructureBlockInfo> list7 = Lists.newArrayList();
        for (int integer6 = 0; integer6 < lk2.size(); ++integer6) {
            final CompoundTag le9 = lk2.getCompound(integer6);
            final ListTag lk3 = le9.getList("pos", 3);
            final BlockPos fu11 = new BlockPos(lk3.getInt(0), lk3.getInt(1), lk3.getInt(2));
            final BlockState cfj12 = b4.stateFor(le9.getInt("state"));
            CompoundTag le10;
            if (le9.contains("nbt")) {
                le10 = le9.getCompound("nbt");
            }
            else {
                le10 = null;
            }
            final StructureBlockInfo c14 = new StructureBlockInfo(fu11, cfj12, le10);
            addToLists(c14, list5, list6, list7);
        }
        final List<StructureBlockInfo> list8 = buildInfoList(list5, list6, list7);
        this.palettes.add(new Palette((List)list8));
    }
    
    private ListTag newIntegerList(final int... arr) {
        final ListTag lk3 = new ListTag();
        for (final int integer7 : arr) {
            ((AbstractList<IntTag>)lk3).add(IntTag.valueOf(integer7));
        }
        return lk3;
    }
    
    private ListTag newDoubleList(final double... arr) {
        final ListTag lk3 = new ListTag();
        for (final double double7 : arr) {
            ((AbstractList<DoubleTag>)lk3).add(DoubleTag.valueOf(double7));
        }
        return lk3;
    }
    
    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 cfj) {
            int integer3 = this.ids.getId(cfj);
            if (integer3 == -1) {
                integer3 = this.lastId++;
                this.ids.addMapping(cfj, integer3);
            }
            return integer3;
        }
        
        @Nullable
        public BlockState stateFor(final int integer) {
            final BlockState cfj3 = this.ids.byId(integer);
            return (cfj3 == null) ? SimplePalette.DEFAULT_BLOCK_STATE : cfj3;
        }
        
        @Override
        public Iterator<BlockState> iterator() {
            return this.ids.iterator();
        }
        
        public void addMapping(final BlockState cfj, final int integer) {
            this.ids.addMapping(cfj, 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 fu, final BlockState cfj, @Nullable final CompoundTag le) {
            this.pos = fu;
            this.state = cfj;
            this.nbt = le;
        }
        
        @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 dem, final BlockPos fu, final CompoundTag le) {
            this.pos = dem;
            this.blockPos = fu;
            this.nbt = le;
        }
    }
    
    public static final class Palette {
        private final List<StructureBlockInfo> blocks;
        private final Map<Block, List<StructureBlockInfo>> cache;
        
        private Palette(final List<StructureBlockInfo> list) {
            this.cache = Maps.newHashMap();
            this.blocks = list;
        }
        
        public List<StructureBlockInfo> blocks() {
            return this.blocks;
        }
        
        public List<StructureBlockInfo> blocks(final Block bvr) {
            return this.cache.computeIfAbsent(bvr, bvr -> this.blocks.stream().filter(c -> c.state.is(bvr)).collect(Collectors.toList()));
        }
    }
}
