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

package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace;

import java.util.Objects;
import java.util.function.Predicate;
import com.hypixel.hytale.math.vector.Vector3i;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;

public class BooleanVoxelSpace implements VoxelSpace<Boolean>
{
    protected final int sizeX;
    protected final int sizeY;
    protected final int sizeZ;
    @Nonnull
    protected final int[][] cells;
    protected VoxelCoordinate origin;
    private boolean alignedOriginZ;
    private int originZOffset;
    
    public BooleanVoxelSpace(final int sizeX, final int sizeY, final int sizeZ, final int originX, final int originY, final int originZ, final boolean alignedOriginZ) {
        if (sizeX < 1 || sizeY < 1 || sizeZ < 1) {
            throw new IllegalArgumentException("invalid size " + sizeX + " " + sizeY + " " + sizeZ);
        }
        if (alignedOriginZ && !isAlignedOriginZ(originZ)) {
            throw new IllegalArgumentException("unaligned originZ: " + originZ);
        }
        this.sizeX = sizeX;
        this.sizeY = sizeY;
        this.sizeZ = sizeZ;
        this.alignedOriginZ = alignedOriginZ;
        final int primaryDepth = sizeX * sizeY;
        int secondaryDepth = (sizeZ - 1 >> 5) + 1;
        if (!alignedOriginZ) {
            ++secondaryDepth;
        }
        this.cells = new int[primaryDepth][secondaryDepth];
        this.origin = new VoxelCoordinate(originX, originY, originZ);
        this.setOrigin(originX, originY, originZ);
    }
    
    public BooleanVoxelSpace(final int sizeX, final int sizeY, final int sizeZ, final int originX, final int originY, final int originZ) {
        this(sizeX, sizeY, sizeZ, originX, originY, originZ, false);
    }
    
    public BooleanVoxelSpace(final int sizeX, final int sizeY, final int sizeZ) {
        this(sizeX, sizeY, sizeZ, 0, 0, 0);
    }
    
    public BooleanVoxelSpace(final int sizeX, final int sizeY, final int sizeZ, final boolean forceAlignOriginZ) {
        this(sizeX, sizeY, sizeZ, 0, 0, 0, forceAlignOriginZ);
    }
    
    @Override
    public int sizeX() {
        return this.sizeX;
    }
    
    @Override
    public int sizeY() {
        return this.sizeY;
    }
    
    @Override
    public int sizeZ() {
        return this.sizeZ;
    }
    
    @Override
    public void pasteFrom(@Nonnull final VoxelSpace<Boolean> source) {
        if (source == null) {
            throw new NullPointerException();
        }
        for (int x = source.minX(); x < source.maxX(); ++x) {
            for (int y = source.minY(); y < source.maxY(); ++y) {
                for (int z = source.minZ(); z < source.maxZ(); ++z) {
                    this.set(Boolean.valueOf(source.getContent(x, y, z)), x, y, z);
                }
            }
        }
    }
    
    private int primaryAddressIndex(final int x, final int y) {
        return x * this.sizeY + y;
    }
    
    private int secondaryAddressIndex(int z) {
        z += this.originZOffset;
        return z >> 5;
    }
    
    private static int setBit(int bits, final int index, final boolean value) {
        final int mask = 1 << index;
        if (!value) {
            bits &= ~mask;
        }
        else {
            bits |= mask;
        }
        return bits;
    }
    
    private static boolean getBit(final int bits, final int index) {
        return (bits >> index & 0x1) == 0x1;
    }
    
    @Override
    public boolean set(@Nullable Boolean value, final int x, final int y, final int z) {
        if (!this.isInsideSpace(x, y, z)) {
            return false;
        }
        if (value == null) {
            value = false;
        }
        final int localX = x + this.origin.x;
        final int localY = y + this.origin.y;
        final int localZ = z + this.origin.z;
        final int i = this.primaryAddressIndex(localX, localY);
        final int j = this.secondaryAddressIndex(localZ);
        final int bitIndex = localZ - j * 32 + this.originZOffset;
        final int cell = setBit(this.cells[i][j], bitIndex, value);
        this.cells[i][j] = cell;
        return true;
    }
    
    @Override
    public boolean set(final Boolean content, @Nonnull final Vector3i position) {
        return this.set(content, position.x, position.y, position.z);
    }
    
    @Nonnull
    @Override
    public Boolean getContent(final int x, final int y, final int z) {
        if (!this.isInsideSpace(x, y, z)) {
            throw new IndexOutOfBoundsException("Coordinates outside VoxelSpace: " + x + " " + y + " " + z + " constraints " + this.minX() + " -> " + this.maxX() + " " + this.minY() + " -> " + this.maxY() + " " + this.minZ() + " -> " + this.maxZ() + "\n" + this.toString());
        }
        final int localX = x + this.origin.x;
        final int localY = y + this.origin.y;
        final int localZ = z + this.origin.z;
        final int i = this.primaryAddressIndex(localX, localY);
        final int j = this.secondaryAddressIndex(localZ);
        final int bitIndex = localZ - j * 32 + this.originZOffset;
        return getBit(this.cells[i][j], bitIndex);
    }
    
    @Nonnull
    @Override
    public Boolean getContent(@Nonnull final Vector3i position) {
        return this.getContent(position.x, position.y, position.z);
    }
    
    private int globalJ(final int globalZ) {
        return globalZ >> 5;
    }
    
    private int localJ(final int globalJ) {
        return globalJ - this.globalJ(-this.origin.z);
    }
    
    public void deepCopyFrom(@Nonnull final BooleanVoxelSpace other) {
        if (other.cells.length == 0) {
            return;
        }
        if (other.cells[0].length == 0) {
            return;
        }
        if (this.cells.length == 0) {
            return;
        }
        if (this.cells[0].length == 0) {
            return;
        }
        final int thisGlobalJ = this.globalJ(-this.origin.z);
        final int otherGlobalJ = other.globalJ(-other.origin.z);
        final int minGlobalJ = Math.max(otherGlobalJ, thisGlobalJ);
        final int minThisJ = this.localJ(minGlobalJ);
        final int minOtherJ = other.localJ(minGlobalJ);
        final int maxIterations = Math.min(other.cells[0].length - minOtherJ, this.cells[0].length - minThisJ);
        final int minX = Math.max(this.minX(), other.minX());
        final int minY = Math.max(this.minY(), other.minY());
        final int maxX = Math.min(this.maxX(), other.maxX());
        final int maxY = Math.min(this.maxY(), other.maxY());
        for (int x = minX; x < maxX; ++x) {
            for (int y = minY; y < maxY; ++y) {
                final int thisLocalX = x + this.origin.x;
                final int thisLocalY = y + this.origin.y;
                final int otherLocalX = x + other.origin.x;
                final int otherLocalY = y + other.origin.y;
                final int thisI = this.primaryAddressIndex(thisLocalX, thisLocalY);
                final int otherI = other.primaryAddressIndex(otherLocalX, otherLocalY);
                int thisJ = minThisJ;
                int otherJ = minOtherJ;
                for (int c = 0; c < maxIterations; ++c) {
                    this.cells[thisI][thisJ] = other.cells[otherI][otherJ];
                    ++otherJ;
                    ++thisJ;
                }
            }
        }
    }
    
    @Override
    public void set(final Boolean content) {
        for (int x = this.minX(); x < this.maxX(); ++x) {
            for (int y = this.minY(); y < this.maxY(); ++y) {
                for (int z = this.minZ(); z < this.maxZ(); ++z) {
                    this.set(content, x, y, z);
                }
            }
        }
    }
    
    @Override
    public void setOrigin(final int x, final int y, final int z) {
        if (this.alignedOriginZ && z % 32 != 0) {
            throw new IllegalArgumentException("z isn't aligned to 32 bit integer grid: " + z);
        }
        this.origin.x = x;
        this.origin.y = y;
        this.origin.z = z;
        this.originZOffset = -this.origin.z - getAlignedZ(-this.origin.z);
    }
    
    @Override
    public boolean replace(final Boolean replacement, final int x, final int y, final int z, @Nonnull final Predicate<Boolean> mask) {
        if (!this.isInsideSpace(x, y, z)) {
            throw new IllegalArgumentException("outside schematic");
        }
        if (!mask.test(this.getContent(x, y, z))) {
            return false;
        }
        this.set(replacement, x, y, z);
        return true;
    }
    
    @Nonnull
    VoxelCoordinate getOrigin() {
        return this.origin.clone();
    }
    
    @Override
    public int getOriginX() {
        return this.origin.x;
    }
    
    @Override
    public int getOriginY() {
        return this.origin.y;
    }
    
    @Override
    public int getOriginZ() {
        return this.origin.z;
    }
    
    @Nonnull
    @Override
    public String getName() {
        return "";
    }
    
    @Override
    public boolean isInsideSpace(final int x, final int y, final int z) {
        return x + this.origin.x >= 0 && x + this.origin.x < this.sizeX && y + this.origin.y >= 0 && y + this.origin.y < this.sizeY && z + this.origin.z >= 0 && z + this.origin.z < this.sizeZ;
    }
    
    @Override
    public boolean isInsideSpace(@Nonnull final Vector3i position) {
        return this.isInsideSpace(position.x, position.y, position.z);
    }
    
    @Override
    public void forEach(@Nonnull final VoxelConsumer<? super Boolean> action) {
        if (action == null) {
            throw new NullPointerException();
        }
        for (int x = this.minX(); x < this.maxX(); ++x) {
            for (int y = this.minY(); y < this.maxY(); ++y) {
                for (int z = this.minZ(); z < this.maxZ(); ++z) {
                    action.accept(this.getContent(x, y, z), x, y, z);
                }
            }
        }
    }
    
    @Override
    public int minX() {
        return -this.origin.x;
    }
    
    @Override
    public int maxX() {
        return this.sizeX - this.origin.x;
    }
    
    @Override
    public int minY() {
        return -this.origin.y;
    }
    
    @Override
    public int maxY() {
        return this.sizeY - this.origin.y;
    }
    
    @Override
    public int minZ() {
        return -this.origin.z;
    }
    
    @Override
    public int maxZ() {
        return this.sizeZ - this.origin.z;
    }
    
    @Nonnull
    public BooleanVoxelSpace clone() {
        final BooleanVoxelSpace obj;
        final BooleanVoxelSpace clone = obj = new BooleanVoxelSpace(this.sizeX, this.sizeY, this.sizeZ, this.origin.x, this.origin.y, this.origin.z);
        Objects.requireNonNull(obj);
        this.forEach(obj::set);
        return clone;
    }
    
    private int arrayIndex(final int x, final int y, final int z) {
        return y + x * this.sizeY + z * this.sizeY * this.sizeX;
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "ArrayVoxelSpace{sizeX=" + this.sizeX + ", sizeY=" + this.sizeY + ", sizeZ=" + this.sizeZ + ", minX=" + this.minX() + ", minY=" + this.minY() + ", minZ=" + this.minZ() + ", maxX=" + this.maxX() + ", maxY=" + this.maxY() + ", maxZ=" + this.maxZ() + ", origin=" + String.valueOf(this.origin);
    }
    
    public static boolean isAlignedOriginZ(final int z) {
        return z % 32 == 0;
    }
    
    public static int getAlignedZ(final int z) {
        return z >> 5 << 5;
    }
}
