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

package com.hypixel.hytale.server.core.prefab.selection.mask;

import java.util.function.Function;
import com.hypixel.hytale.codec.function.FunctionCodec;
import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.ArrayList;
import java.util.List;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import javax.annotation.Nullable;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor;
import javax.annotation.Nonnull;
import com.hypixel.hytale.codec.Codec;

public class BlockMask
{
    public static final BlockMask EMPTY;
    public static final Codec<BlockMask> CODEC;
    public static final String MASK_SEPARATOR = ",";
    public static final String ALT_MASK_SEPARATOR = ";";
    public static final String EMPTY_MASK_CHARACTER = "-";
    private final BlockFilter[] filters;
    private boolean inverted;
    
    public BlockMask(final BlockFilter[] filters) {
        this.filters = filters;
    }
    
    @Nonnull
    public BlockMask withOptions(@Nonnull final BlockFilter.FilterType filterType, final boolean inverted) {
        if (this == BlockMask.EMPTY) {
            return this;
        }
        final BlockFilter[] filters2;
        BlockFilter[] filters = filters2 = this.filters;
        for (final BlockFilter filter : filters2) {
            if (filter.getBlockFilterType() != filterType || filter.isInverted() != inverted) {
                filters = new BlockFilter[filters.length];
                break;
            }
        }
        if (filters == this.filters) {
            return this;
        }
        for (int i = 0; i < filters.length; ++i) {
            BlockFilter filter2 = this.filters[i];
            if (filter2.getBlockFilterType() != filterType || filter2.isInverted() != inverted) {
                filter2 = new BlockFilter(filterType, filter2.getBlocks(), inverted);
            }
            filters[i] = filter2;
        }
        return new BlockMask(filters);
    }
    
    public BlockFilter[] getFilters() {
        return this.filters;
    }
    
    public void setInverted(final boolean inverted) {
        this.inverted = inverted;
    }
    
    public boolean isInverted() {
        return this.inverted;
    }
    
    public boolean isExcluded(@Nonnull final ChunkAccessor accessor, final int x, final int y, final int z, final Vector3i min, final Vector3i max, final int blockId) {
        return this.isExcluded(accessor, x, y, z, min, max, blockId, -1);
    }
    
    public boolean isExcluded(@Nonnull final ChunkAccessor accessor, final int x, final int y, final int z, final Vector3i min, final Vector3i max, final int blockId, final int fluidId) {
        boolean excluded = false;
        for (final BlockFilter filter : this.filters) {
            if (filter.isExcluded(accessor, x, y, z, min, max, blockId, fluidId)) {
                excluded = true;
                break;
            }
        }
        return this.inverted != excluded;
    }
    
    @Nonnull
    @Override
    public String toString() {
        if (this.filters.length == 0) {
            return "-";
        }
        final String base = joinElements(",", this.filters);
        return this.inverted ? ("!" + base) : base;
    }
    
    @Nonnull
    public String informativeToString() {
        if (this.filters.length == 0) {
            return "-";
        }
        final StringBuilder builder = new StringBuilder();
        if (this.inverted) {
            builder.append("NOT(");
        }
        if (this.filters.length > 1) {
            builder.append("(");
        }
        for (int i = 0; i < this.filters.length; ++i) {
            builder.append(this.filters[i].informativeToString());
            if (i != this.filters.length - 1) {
                builder.append(" AND ");
            }
        }
        if (this.filters.length > 1) {
            builder.append(")");
        }
        if (this.inverted) {
            builder.append(")");
        }
        return builder.toString();
    }
    
    @Nonnull
    protected static String joinElements(final String separator, @Nonnull final Object[] elements) {
        final StringBuilder sb = new StringBuilder();
        for (final Object o : elements) {
            if (!sb.isEmpty()) {
                sb.append(separator);
            }
            sb.append(o);
        }
        return sb.toString();
    }
    
    public static BlockMask parse(@Nonnull String masks) {
        if (masks.isEmpty() || masks.equals("-")) {
            return BlockMask.EMPTY;
        }
        masks = masks.replace(";", ",");
        return parse(masks.split(","));
    }
    
    public static BlockMask parse(@Nonnull final String[] masks) {
        if (masks.length == 0) {
            return BlockMask.EMPTY;
        }
        if (masks.length == 1) {
            return new BlockMask(new BlockFilter[] { BlockFilter.parse(masks[0]) });
        }
        final BlockFilter[] parsedFilters = new BlockFilter[masks.length];
        for (int i = 0; i < masks.length; ++i) {
            parsedFilters[i] = BlockFilter.parse(masks[i]);
        }
        return groupFilters(parsedFilters);
    }
    
    public static BlockMask combine(@Nullable final BlockMask... masks) {
        if (masks == null || masks.length == 0) {
            return BlockMask.EMPTY;
        }
        int totalFilters = 0;
        for (final BlockMask mask : masks) {
            if (mask != null && mask != BlockMask.EMPTY) {
                totalFilters += mask.getFilters().length;
            }
        }
        if (totalFilters == 0) {
            return BlockMask.EMPTY;
        }
        final BlockFilter[] allFilters = new BlockFilter[totalFilters];
        int idx = 0;
        for (final BlockMask mask2 : masks) {
            if (mask2 != null) {
                if (mask2 != BlockMask.EMPTY) {
                    for (final BlockFilter filter : mask2.getFilters()) {
                        allFilters[idx++] = filter;
                    }
                }
            }
        }
        return groupFilters(allFilters);
    }
    
    private static BlockMask groupFilters(@Nonnull final BlockFilter[] inputFilters) {
        if (inputFilters.length == 0) {
            return BlockMask.EMPTY;
        }
        if (inputFilters.length == 1) {
            return new BlockMask(inputFilters);
        }
        final Int2ObjectLinkedOpenHashMap<List<String>> groups = new Int2ObjectLinkedOpenHashMap<List<String>>();
        for (final BlockFilter filter : inputFilters) {
            final int key = filter.getBlockFilterType().ordinal() << 1 | (filter.isInverted() ? 1 : 0);
            final List<String> list = groups.computeIfAbsent(key, k -> new ArrayList());
            for (final String block : filter.getBlocks()) {
                list.add(block);
            }
        }
        if (groups.size() == inputFilters.length) {
            return new BlockMask(inputFilters);
        }
        final BlockFilter[] filters = new BlockFilter[groups.size()];
        int i = 0;
        for (final Int2ObjectMap.Entry<List<String>> entry : groups.int2ObjectEntrySet()) {
            final int key = entry.getIntKey();
            final BlockFilter.FilterType filterType = BlockFilter.FilterType.values()[key >> 1];
            final boolean inverted = (key & 0x1) != 0x0;
            final String[] blocks = (String[])entry.getValue().toArray(new String[0]);
            filters[i++] = new BlockFilter(filterType, blocks, inverted);
        }
        return new BlockMask(filters);
    }
    
    static {
        EMPTY = new BlockMask(BlockFilter.EMPTY_ARRAY);
        CODEC = new FunctionCodec<Object, BlockMask>(Codec.STRING, BlockMask::parse, BlockMask::toString);
    }
}
