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

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

import com.hypixel.hytale.server.core.util.FillerBlockUtil;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import java.util.function.Function;
import com.hypixel.hytale.codec.function.FunctionCodec;
import javax.annotation.Nonnull;
import java.util.List;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import javax.annotation.Nullable;
import java.util.Random;
import java.util.Iterator;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.asset.type.buildertool.config.BlockTypeListAsset;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.common.map.WeightedMap;
import com.hypixel.hytale.common.util.ArrayUtil;
import com.hypixel.hytale.common.map.IWeightedMap;
import java.util.regex.Pattern;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.logger.HytaleLogger;

public class BlockPattern
{
    private static final HytaleLogger LOGGER;
    public static final Codec<BlockPattern> CODEC;
    public static final BlockPattern EMPTY;
    public static final BlockPattern[] EMPTY_ARRAY;
    private static final Pattern FILLER_TEMP_REMOVER_PATTERN;
    private static final String BLOCK_SEPARATOR = ",";
    private static final String ALT_BLOCK_SEPARATOR = ";";
    private static final String CHANCE_SUFFIX = "%";
    private static final double DEFAULT_CHANCE = 100.0;
    private final IWeightedMap<String> weightedMap;
    private final transient String toString0;
    private IWeightedMap<Integer> resolvedWeightedMap;
    private IWeightedMap<BlockEntry> resolvedWeightedMapBtk;
    
    public BlockPattern(final IWeightedMap<String> weightedMap) {
        this.weightedMap = weightedMap;
        this.toString0 = this.toString0();
    }
    
    public Integer[] getResolvedKeys() {
        this.resolve();
        return this.resolvedWeightedMap.internalKeys();
    }
    
    public void resolve() {
        if (this.resolvedWeightedMap != null) {
            return;
        }
        final WeightedMap.Builder<Integer> mapBuilder = WeightedMap.builder(ArrayUtil.EMPTY_INTEGER_ARRAY);
        final WeightedMap.Builder<BlockEntry> mapBuilderKey = WeightedMap.builder(new BlockEntry[0]);
        this.weightedMap.forEachEntry((blockName, weight) -> {
            final int blockId = parseBlock(blockName);
            final BlockEntry key = tryParseBlockTypeKey(blockName);
            final BlockType blockType = BlockType.getAssetMap().getAsset(blockId);
            if (blockType != null && blockType.getBlockListAssetId() != null) {
                final BlockTypeListAsset blockTypeListAsset = BlockTypeListAsset.getAssetMap().getAsset(blockType.getBlockListAssetId());
                if (blockTypeListAsset != null && blockTypeListAsset.getBlockPattern() != null) {
                    for (final String resolvedKey : blockTypeListAsset.getBlockTypeKeys()) {
                        final int resolvedId = BlockType.getAssetMap().getIndex(resolvedKey);
                        if (resolvedId == Integer.MIN_VALUE) {
                            BlockPattern.LOGGER.at(Level.WARNING).log("BlockTypeList '%s' contains invalid block '%s' - skipping", blockType.getBlockListAssetId(), resolvedKey);
                        }
                        else {
                            mapBuilder.put(resolvedId, weight / blockTypeListAsset.getBlockTypeKeys().size());
                        }
                    }
                    return;
                }
            }
            mapBuilder.put(blockId, weight);
            if (key != null) {
                mapBuilderKey.put(key, weight);
            }
            return;
        });
        this.resolvedWeightedMap = mapBuilder.build();
        this.resolvedWeightedMapBtk = mapBuilderKey.build();
    }
    
    public boolean isEmpty() {
        return this.weightedMap.size() == 0;
    }
    
    public int nextBlock(final Random random) {
        this.resolve();
        return this.resolvedWeightedMap.get(random);
    }
    
    @Nullable
    public BlockEntry nextBlockTypeKey(final Random random) {
        this.resolve();
        return this.resolvedWeightedMapBtk.get(random);
    }
    
    @Deprecated
    public int firstBlock() {
        this.resolve();
        return (this.resolvedWeightedMap.size() > 0) ? this.resolvedWeightedMap.internalKeys()[0] : 0;
    }
    
    @Override
    public String toString() {
        return this.toString0;
    }
    
    private String toString0() {
        if (this.weightedMap.size() == 1) {
            return this.weightedMap.internalKeys()[0];
        }
        final List<String> blocks = new ObjectArrayList<String>();
        this.weightedMap.forEachEntry((k, v) -> blocks.add(v + "%" + k));
        return String.join(",", blocks);
    }
    
    public static BlockPattern parse(@Nonnull String str) {
        if (str.isEmpty() || str.equals("Empty")) {
            return BlockPattern.EMPTY;
        }
        if (str.toLowerCase().contains("filler=")) {
            str = BlockPattern.FILLER_TEMP_REMOVER_PATTERN.matcher(str).replaceAll("$1;$2;$3");
        }
        str = str.replace(";", ",");
        return new BlockPattern(parseBlockPattern(str.split(",")));
    }
    
    @Nonnull
    private static IWeightedMap<String> parseBlockPattern(@Nonnull final String... blocksArgs) {
        final WeightedMap.Builder<String> builder = WeightedMap.builder(ArrayUtil.EMPTY_STRING_ARRAY);
        for (String blockArg : blocksArgs) {
            if (!blockArg.isEmpty()) {
                double chance = 100.0;
                final String[] blockArr = blockArg.split("%");
                if (blockArr.length > 1) {
                    try {
                        chance = Double.parseDouble(blockArr[0]);
                    }
                    catch (final NumberFormatException e) {
                        throw new IllegalArgumentException("Invalid Chance Value: " + blockArr[0], (Throwable)e);
                    }
                    blockArg = blockArr[1];
                }
                builder.put(blockArg, chance);
            }
        }
        return builder.build();
    }
    
    public static int parseBlock(@Nonnull String blockText) {
        int blockId;
        try {
            blockId = Integer.parseInt(blockText);
            if (BlockType.getAssetMap().getAsset(blockId) == null) {
                throw new IllegalArgumentException("Block with id '" + blockText + "' doesn't exist!");
            }
        }
        catch (final NumberFormatException ignored) {
            blockText = blockText.replace(";", ",");
            final int oldData = blockText.indexOf(124);
            if (oldData != -1) {
                blockText = blockText.substring(0, oldData);
            }
            blockId = BlockType.getAssetMap().getIndex(blockText);
            if (blockId == Integer.MIN_VALUE) {
                BlockPattern.LOGGER.at(Level.WARNING).log("Invalid block name '%s' - using empty block", blockText);
                return 0;
            }
        }
        return blockId;
    }
    
    @Nullable
    public static BlockEntry tryParseBlockTypeKey(String blockText) {
        try {
            blockText = blockText.replace(";", ",");
            return BlockEntry.decode(blockText);
        }
        catch (final Exception e) {
            return null;
        }
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
        CODEC = new FunctionCodec<Object, BlockPattern>(Codec.STRING, BlockPattern::parse, BlockPattern::toString);
        EMPTY = new BlockPattern(parseBlockPattern("Empty"));
        EMPTY_ARRAY = new BlockPattern[0];
        FILLER_TEMP_REMOVER_PATTERN = Pattern.compile("(Filler=-?\\d+),(-?\\d+),(-?\\d+)");
    }
    
    record BlockEntry(String blockTypeKey, int rotation, int filler) {
        @Deprecated(forRemoval = true)
        public static Codec<BlockEntry> CODEC;
        
        @Deprecated(forRemoval = true)
        private String encode() {
            if (this.filler == 0 && this.rotation == 0) {
                return this.blockTypeKey;
            }
            final StringBuilder out = new StringBuilder(this.blockTypeKey);
            final RotationTuple rot = RotationTuple.get(this.rotation);
            if (rot.yaw() != Rotation.None) {
                out.append("|Yaw=").append(rot.yaw().getDegrees());
            }
            if (rot.pitch() != Rotation.None) {
                out.append("|Pitch=").append(rot.pitch().getDegrees());
            }
            if (rot.roll() != Rotation.None) {
                out.append("|Roll=").append(rot.roll().getDegrees());
            }
            if (this.filler != 0) {
                final int fillerX = FillerBlockUtil.unpackX(this.filler);
                final int fillerY = FillerBlockUtil.unpackY(this.filler);
                final int fillerZ = FillerBlockUtil.unpackZ(this.filler);
                out.append("|Filler=").append(fillerX).append(",").append(fillerY).append(",").append(fillerZ);
            }
            return out.toString();
        }
        
        @Deprecated(forRemoval = true)
        public static BlockEntry decode(final String key) {
            int filler = 0;
            if (key.contains("|Filler=")) {
                final int start = key.indexOf("|Filler=") + "|Filler=".length();
                final int firstComma = key.indexOf(44, start);
                if (firstComma == -1) {
                    throw new IllegalArgumentException("Invalid filler metadata! Missing comma");
                }
                final int secondComma = key.indexOf(44, firstComma + 1);
                if (secondComma == -1) {
                    throw new IllegalArgumentException("Invalid filler metadata! Missing second comma");
                }
                int end = key.indexOf(124, start);
                if (end == -1) {
                    end = key.length();
                }
                final int fillerX = Integer.parseInt(key, start, firstComma, 10);
                final int fillerY = Integer.parseInt(key, firstComma + 1, secondComma, 10);
                final int fillerZ = Integer.parseInt(key, secondComma + 1, end, 10);
                filler = FillerBlockUtil.pack(fillerX, fillerY, fillerZ);
            }
            Rotation rotationYaw = Rotation.None;
            Rotation rotationPitch = Rotation.None;
            Rotation rotationRoll = Rotation.None;
            if (key.contains("|Yaw=")) {
                final int start2 = key.indexOf("|Yaw=") + "|Yaw=".length();
                int end2 = key.indexOf(124, start2);
                if (end2 == -1) {
                    end2 = key.length();
                }
                rotationYaw = Rotation.ofDegrees(Integer.parseInt(key, start2, end2, 10));
            }
            if (key.contains("|Pitch=")) {
                final int start2 = key.indexOf("|Pitch=") + "|Pitch=".length();
                int end2 = key.indexOf(124, start2);
                if (end2 == -1) {
                    end2 = key.length();
                }
                rotationPitch = Rotation.ofDegrees(Integer.parseInt(key, start2, end2, 10));
            }
            if (key.contains("|Roll=")) {
                final int start2 = key.indexOf("|Roll=") + "|Roll=".length();
                int end2 = key.indexOf(124, start2);
                if (end2 == -1) {
                    end2 = key.length();
                }
                rotationRoll = Rotation.ofDegrees(Integer.parseInt(key, start2, end2, 10));
            }
            int end = key.indexOf(124);
            if (end == -1) {
                end = key.length();
            }
            final String name = key.substring(0, end);
            return new BlockEntry(name, RotationTuple.of(rotationYaw, rotationPitch, rotationRoll).index(), filler);
        }
        
        static {
            BlockEntry.CODEC = new FunctionCodec<Object, BlockEntry>(Codec.STRING, BlockEntry::decode, BlockEntry::encode);
        }
    }
}
