<commit_before>package com.mcjty.rftools.dimension.description;

import com.mcjty.rftools.RFTools;
import com.mcjty.rftools.blocks.teleporter.TeleportConfiguration;
import com.mcjty.rftools.items.dimlets.*;
import net.minecraft.nbt.NBTTagCompound;
import org.apache.commons.lang3.tuple.Pair;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * A unique descriptor of a dimension.
 */
public class DimensionDescriptor {
    private final String descriptionString;
    private final int rfCreateCost;
    private final int rfMaintainCost;
    private final int tickCost;
    private final long forcedSeed;

    public DimensionDescriptor(List<DimletDescriptor> descriptors, long forcedSeed) {
        this.forcedSeed = forcedSeed;

        StringBuilder s = new StringBuilder();

        // List of all non-modifier dimlets with all associated modifiers.
        List<Pair<DimletKey,List<DimletDescriptor>>> dimlets = new ArrayList<Pair<DimletKey, List<DimletDescriptor>>>();

        // A list of all current modifier that haven't been fitted into a type yet.
        List<DimletDescriptor> currentModifiers = new ArrayList<DimletDescriptor>();

        groupDimletsAndModifiers(descriptors, dimlets, currentModifiers);
        constructDescriptionStringNew(s, dimlets, currentModifiers);

        descriptionString = s.toString();

        tickCost = calculateTickCost(dimlets);
        rfCreateCost = calculateCreationRfCost(dimlets, currentModifiers, tickCost);
        rfMaintainCost = calculateMaintenanceRfCost(dimlets);
    }

    private void constructDescriptionStringNew(StringBuilder s, List<Pair<DimletKey,List<DimletDescriptor>>> dimlets, List<DimletDescriptor> currentModifiers) {
        s.append('@');
        boolean first = true;
        for (Pair<DimletKey, List<DimletDescriptor>> dimletWithModifiers : dimlets) {
            DimletKey key = dimletWithModifiers.getLeft();
            List<DimletDescriptor> mods = dimletWithModifiers.getRight();
            if (mods != null) {
                for (DimletDescriptor modifier : mods) {
                    if (!first) {
                        s.append(',');
                    }
                    first = false;
                    s.append('#').append(modifier.getKey());
                }
            }
            if (!first) {
                s.append(',');
            }
            first = false;
            s.append(key);
        }

        // Now add all unused modifiers to the end.
        for (DimletDescriptor modifier : currentModifiers) {
            if (s.length() > 0) {
                s.append(',');
            }
            s.append('?').append(modifier.getKey());
        }
    }

    private void groupDimletsAndModifiers(List<DimletDescriptor> descriptors, List<Pair<DimletKey,List<DimletDescriptor>>> dimlets, List<DimletDescriptor> currentModifiers) {
        for (DimletDescriptor descriptor : descriptors) {
            DimletType type = descriptor.getType();
            DimletKey key = descriptor.getKey();
            if (type.isModifier()) {
                // Keep the modifier here until we find a dimlet for which it fits.
                currentModifiers.add(descriptor);
            } else {
                List<DimletDescriptor> modifiers = new ArrayList<DimletDescriptor>();
                if (!currentModifiers.isEmpty()) {
                    // Check if we collected modifiers that fit with this type.
                    List<DimletDescriptor> copy = new ArrayList<DimletDescriptor>(currentModifiers);
                    // Iterate over a copy so that we can delete from original list.
                    for (DimletDescriptor modifier : copy) {
                        if (type.isModifiedBy(modifier.getType())) {
                            modifiers.add(modifier);
                            currentModifiers.remove(modifier);
                        }
                    }
                }
                dimlets.add(Pair.of(key, modifiers));
            }
        }
    }

    public DimensionDescriptor(NBTTagCompound tagCompound) {
        String ds = tagCompound.getString("descriptionString");
        if (ds.startsWith("@")) {
            // New style already.
            descriptionString = ds;
        } else {
            // We need to convert.
            List<DimletDescriptor> dimletDescriptors = parseOldDescriptionString(ds);

            List<Pair<DimletKey,List<DimletDescriptor>>> dimlets = new ArrayList<Pair<DimletKey, List<DimletDescriptor>>>();

            // A list of all current modifier that haven't been fitted into a type yet.
            List<DimletDescriptor> currentModifiers = new ArrayList<DimletDescriptor>();
            groupDimletsAndModifiers(dimletDescriptors, dimlets, currentModifiers);

            StringBuilder s = new StringBuilder();
            constructDescriptionStringNew(s, dimlets, currentModifiers);
            RFTools.log("Converting dimension descriptor from: " + ds + " to: " + s);
            descriptionString = s.toString();
        }

        rfCreateCost = tagCompound.getInteger("rfCreateCost");
        rfMaintainCost = tagCompound.getInteger("rfMaintainCost");
        tickCost = tagCompound.getInteger("tickCost");
        forcedSeed = tagCompound.getLong("forcedSeed");
    }

    public List<Pair<DimletDescriptor,List<DimletDescriptor>>> getDimletsWithModifiers() {
        List<Pair<DimletDescriptor,List<DimletDescriptor>>> result = new ArrayList<Pair<DimletDescriptor, List<DimletDescriptor>>>();

        String ds = descriptionString;
        if (ds.startsWith("@")) {
            ds = ds.substring(1);
        }

        if (!ds.isEmpty()) {
            List<DimletDescriptor> modifiers = new ArrayList<DimletDescriptor>();

            String[] opcodes = ds.split(",");
            for (String oc : opcodes) {
                DimletKey key;
                if (oc.startsWith("#")) {
                    // First comes '#', then the type of the actual dimlet.
                    key = DimletKey.parseKey(oc.substring(1));
                    modifiers.add(new DimletDescriptor(key.getType(), key));
                } else if (oc.startsWith("?")) {
                } else {
                    key = DimletKey.parseKey(oc);
                    result.add(Pair.of(new DimletDescriptor(key.getType(), key), modifiers));
                    modifiers = new ArrayList<DimletDescriptor>();
                }
            }
        }
        return result;
    }

    public static List<DimletDescriptor> parseDescriptionString(String descriptionString) {
        if (descriptionString.startsWith("@")) {
            return parseNewDescriptionString(descriptionString);
        } else {
            return parseOldDescriptionString(descriptionString);
        }
    }

    private static List<DimletDescriptor> parseNewDescriptionString(String descriptionString) {
        List<DimletDescriptor> result = new ArrayList<DimletDescriptor>();
        if (!descriptionString.isEmpty()) {
            String[] opcodes = descriptionString.substring(1).split(",");
            for (String oc : opcodes) {
                DimletKey key;
                if (oc.startsWith("#")) {
                    // First comes '#', then the type of the actual dimlet.
                    key = DimletKey.parseKey(oc.substring(1));
                } else if (oc.startsWith("?")) {
                    // First comes '?', then the type of the actual dimlet
                    key = DimletKey.parseKey(oc.substring(1));<commit_msg>Fixed a problem when creating random empty worlds. It would crash then.<commit_after>package com.mcjty.rftools.dimension.description;

import com.mcjty.rftools.RFTools;
import com.mcjty.rftools.blocks.teleporter.TeleportConfiguration;
import com.mcjty.rftools.items.dimlets.*;
import net.minecraft.nbt.NBTTagCompound;
import org.apache.commons.lang3.tuple.Pair;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * A unique descriptor of a dimension.
 */
public class DimensionDescriptor {
    private final String descriptionString;
    private final int rfCreateCost;
    private final int rfMaintainCost;
    private final int tickCost;
    private final long forcedSeed;

    public DimensionDescriptor(List<DimletDescriptor> descriptors, long forcedSeed) {
        this.forcedSeed = forcedSeed;

        StringBuilder s = new StringBuilder();

        // List of all non-modifier dimlets with all associated modifiers.
        List<Pair<DimletKey,List<DimletDescriptor>>> dimlets = new ArrayList<Pair<DimletKey, List<DimletDescriptor>>>();

        // A list of all current modifier that haven't been fitted into a type yet.
        List<DimletDescriptor> currentModifiers = new ArrayList<DimletDescriptor>();

        groupDimletsAndModifiers(descriptors, dimlets, currentModifiers);
        constructDescriptionStringNew(s, dimlets, currentModifiers);

        descriptionString = s.toString();

        tickCost = calculateTickCost(dimlets);
        rfCreateCost = calculateCreationRfCost(dimlets, currentModifiers, tickCost);
        rfMaintainCost = calculateMaintenanceRfCost(dimlets);
    }

    private void constructDescriptionStringNew(StringBuilder s, List<Pair<DimletKey,List<DimletDescriptor>>> dimlets, List<DimletDescriptor> currentModifiers) {
        s.append('@');
        boolean first = true;
        for (Pair<DimletKey, List<DimletDescriptor>> dimletWithModifiers : dimlets) {
            DimletKey key = dimletWithModifiers.getLeft();
            List<DimletDescriptor> mods = dimletWithModifiers.getRight();
            if (mods != null) {
                for (DimletDescriptor modifier : mods) {
                    if (!first) {
                        s.append(',');
                    }
                    first = false;
                    s.append('#').append(modifier.getKey());
                }
            }
            if (!first) {
                s.append(',');
            }
            first = false;
            s.append(key);
        }

        // Now add all unused modifiers to the end.
        for (DimletDescriptor modifier : currentModifiers) {
            if (s.length() > 0) {
                s.append(',');
            }
            s.append('?').append(modifier.getKey());
        }
    }

    private void groupDimletsAndModifiers(List<DimletDescriptor> descriptors, List<Pair<DimletKey,List<DimletDescriptor>>> dimlets, List<DimletDescriptor> currentModifiers) {
        for (DimletDescriptor descriptor : descriptors) {
            DimletType type = descriptor.getType();
            DimletKey key = descriptor.getKey();
            if (type.isModifier()) {
                // Keep the modifier here until we find a dimlet for which it fits.
                currentModifiers.add(descriptor);
            } else {
                List<DimletDescriptor> modifiers = new ArrayList<DimletDescriptor>();
                if (!currentModifiers.isEmpty()) {
                    // Check if we collected modifiers that fit with this type.
                    List<DimletDescriptor> copy = new ArrayList<DimletDescriptor>(currentModifiers);
                    // Iterate over a copy so that we can delete from original list.
                    for (DimletDescriptor modifier : copy) {
                        if (type.isModifiedBy(modifier.getType())) {
                            modifiers.add(modifier);
                            currentModifiers.remove(modifier);
                        }
                    }
                }
                dimlets.add(Pair.of(key, modifiers));
            }
        }
    }

    public DimensionDescriptor(NBTTagCompound tagCompound) {
        String ds = tagCompound.getString("descriptionString");
        if (ds.startsWith("@")) {
            // New style already.
            descriptionString = ds;
        } else {
            // We need to convert.
            List<DimletDescriptor> dimletDescriptors = parseOldDescriptionString(ds);

            List<Pair<DimletKey,List<DimletDescriptor>>> dimlets = new ArrayList<Pair<DimletKey, List<DimletDescriptor>>>();

            // A list of all current modifier that haven't been fitted into a type yet.
            List<DimletDescriptor> currentModifiers = new ArrayList<DimletDescriptor>();
            groupDimletsAndModifiers(dimletDescriptors, dimlets, currentModifiers);

            StringBuilder s = new StringBuilder();
            constructDescriptionStringNew(s, dimlets, currentModifiers);
            RFTools.log("Converting dimension descriptor from: " + ds + " to: " + s);
            descriptionString = s.toString();
        }

        rfCreateCost = tagCompound.getInteger("rfCreateCost");
        rfMaintainCost = tagCompound.getInteger("rfMaintainCost");
        tickCost = tagCompound.getInteger("tickCost");
        forcedSeed = tagCompound.getLong("forcedSeed");
    }

    public List<Pair<DimletDescriptor,List<DimletDescriptor>>> getDimletsWithModifiers() {
        List<Pair<DimletDescriptor,List<DimletDescriptor>>> result = new ArrayList<Pair<DimletDescriptor, List<DimletDescriptor>>>();

        String ds = descriptionString;
        if (ds.startsWith("@")) {
            ds = ds.substring(1);
        }

        if (!ds.isEmpty()) {
            List<DimletDescriptor> modifiers = new ArrayList<DimletDescriptor>();

            String[] opcodes = ds.split(",");
            for (String oc : opcodes) {
                DimletKey key;
                if (oc.startsWith("#")) {
                    // First comes '#', then the type of the actual dimlet.
                    key = DimletKey.parseKey(oc.substring(1));
                    modifiers.add(new DimletDescriptor(key.getType(), key));
                } else if (oc.startsWith("?")) {
                } else {
                    key = DimletKey.parseKey(oc);
                    result.add(Pair.of(new DimletDescriptor(key.getType(), key), modifiers));
                    modifiers = new ArrayList<DimletDescriptor>();
                }
            }
        }
        return result;
    }

    public static List<DimletDescriptor> parseDescriptionString(String descriptionString) {
        if (descriptionString.startsWith("@")) {
            return parseNewDescriptionString(descriptionString);
        } else {
            return parseOldDescriptionString(descriptionString);
        }
    }

    private static List<DimletDescriptor> parseNewDescriptionString(String descriptionString) {
        List<DimletDescriptor> result = new ArrayList<DimletDescriptor>();
        String ds = descriptionString.substring(1);
        if (!ds.isEmpty()) {
            String[] opcodes = ds.split(",");
            for (String oc : opcodes) {
                DimletKey key;
                if (oc.startsWith("#")) {
                    // First comes '#', then the type of the actual dimlet.
                    key = DimletKey.parseKey(oc.substring(1));
                } else if (oc.startsWith("?")) {
                    // First comes '?', then the type of the actual dimlet
                    key = DimletKey.parseKey(oc.substring(1));
{'fraction_non_alphanumeric': 0.10186959106824338, 'fraction_numerical': 0.0009248860408271124, 'mean_word_length': 2.182926829268293, 'pattern_counts': {'":': 0, '<': 103, '<?xml version=': 0, '>': 105, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5939169', 'n_tokens_mistral': 3901, 'n_tokens_neox': 3796, 'n_words': 879}