<commit_before>package org.cyclops.integrateddynamics.core.network;

import com.google.common.collect.Lists;
import org.apache.commons.lang3.tuple.Pair;
import org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
import org.cyclops.cyclopscore.datastructure.Wrapper;
import org.cyclops.cyclopscore.ingredient.collection.IIngredientMapMutable;
import org.cyclops.cyclopscore.ingredient.collection.IngredientHashMap;
import org.cyclops.integrateddynamics.api.network.IPartPosIteratorHandler;
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;
import org.cyclops.integrateddynamics.api.part.PartPos;

import javax.annotation.Nonnull;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
 * An abstract {@link IIngredientComponentStorage} that wraps over a {@link IPositionedAddonsNetworkIngredients}.
 *
 * @param <T> The instance type.
 * @param <M> The matching condition parameter.
 */
public abstract class IngredientChannelAdapter<T, M> implements IIngredientComponentStorage<T, M> {

    private final IPositionedAddonsNetworkIngredients<T, M> network;
    private final int channel;

    public IngredientChannelAdapter(PositionedAddonsNetworkIngredients<T, M> network, int channel) {
        this.network = network;
        this.channel = channel;
    }

    public IPositionedAddonsNetworkIngredients<T, M> getNetwork() {
        return network;
    }

    public int getChannel() {
        return channel;
    }

    @Override
    public IngredientComponent<T, M> getComponent() {
        return network.getComponent();
    }

    protected abstract Iterator<PartPos> getNonFullPositions();
    protected abstract Iterator<PartPos> getAllPositions();
    protected abstract Iterator<PartPos> getNonEmptyPositions();
    protected abstract Iterator<PartPos> getMatchingPositions(@Nonnull T prototype, M matchFlags);

    @Override
    public long getMaxQuantity() {
        long sum = 0;
        Iterator<PartPos> it = getAllPositions();
        while (it.hasNext()) {
            sum = Math.addExact(sum, this.network.getPositionedStorage(it.next()).getMaxQuantity());
        }
        return sum;
    }

    protected Pair<IPartPosIteratorHandler, Iterator<PartPos>> getPartPosIteratorData(Supplier<Iterator<PartPos>> iteratorSupplier, int channel) {
        IPartPosIteratorHandler handler = network.getPartPosIteratorHandler();
        if (handler == null) {
            handler = PartPosIteratorHandlerDummy.INSTANCE;
        } else {
            handler = handler.clone();
        }
        return Pair.of(handler, handler.handleIterator(iteratorSupplier, channel));
    }

    protected void savePartPosIteratorHandler(IPartPosIteratorHandler partPosIteratorHandler) {
        network.setPartPosIteratorHandler(partPosIteratorHandler);
    }

    @Override
    public T insert(@Nonnull T ingredient, boolean simulate) {
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();

        // Quickly return if the to-be-inserted ingredient was already empty
        if (matcher.isEmpty(ingredient)) {
            return ingredient;
        }

        // Limit rate
        long limit = network.getRateLimit();
        long currentQuantity = matcher.getQuantity(ingredient);
        long skippedQuantity = 0;
        if (currentQuantity > limit) {
            ingredient = matcher.withQuantity(ingredient, limit);
            skippedQuantity = currentQuantity - limit;
        }

        // Try inserting the ingredient at all positions that are not full,
        // until the ingredient becomes completely empty.
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(this::getNonFullPositions, channel);
        Iterator<PartPos> it = partPosIteratorData.getRight();
        while (it.hasNext()) {
            PartPos pos = it.next();
            this.network.disablePosition(pos);
            long quantityBefore = matcher.getQuantity(ingredient);
            ingredient = this.network.getPositionedStorage(pos).insert(ingredient, simulate);
            long quantityAfter = matcher.getQuantity(ingredient);
            this.network.enablePosition(pos);
            if (!simulate && quantityBefore != quantityAfter) {
                this.network.scheduleObservationForced(channel, pos); // Mark the position as 'changed'
            }
            if (matcher.isEmpty(ingredient)) {
                break;
            }
        }

        // Re-add skipped quantity to response if applicable
        if (skippedQuantity > 0) {
            ingredient = matcher.withQuantity(ingredient, skippedQuantity + matcher.getQuantity(ingredient));
        }

        if (!simulate) {
            savePartPosIteratorHandler(partPosIteratorData.getLeft());
        }

        return ingredient;
    }

    @Override
    public T extract(long maxQuantity, boolean simulate) {
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();

        // Limit rate
        maxQuantity = (int) Math.min(maxQuantity, network.getRateLimit());

        // Try extracting from all non-empty positions
        // until one succeeds.
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(this::getNonEmptyPositions, channel);
        Iterator<PartPos> it = partPosIteratorData.getRight();
        while (it.hasNext()) {
            PartPos pos = it.next();
            this.network.disablePosition(pos);
            T extracted = this.network.getPositionedStorage(pos).extract(maxQuantity, simulate);
            if (!this.canExtract(extracted)) {
                extracted = matcher.getEmptyInstance();
            }
            this.network.enablePosition(pos);
            if (!matcher.isEmpty(extracted)) {
                if (!simulate) {
                    this.network.scheduleObservationForced(channel, pos); // Mark the position as 'changed'
                    savePartPosIteratorHandler(partPosIteratorData.getLeft());
                }
                return extracted;
            }
        }

        if (!simulate) {
            savePartPosIteratorHandler(partPosIteratorData.getLeft());
        }

        return matcher.getEmptyInstance();
    }

    @Override
    public T extract(@Nonnull T prototype, M matchFlags, boolean simulate) {
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
        boolean checkQuantity = matcher.hasCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());

        // Limit rate
        long limit = network.getRateLimit();
        if (matcher.getQuantity(prototype) > limit) {
            // Fail immediately if we require more than the limit
            if (checkQuantity) {
                return matcher.getEmptyInstance();
            }

            // Otherwise, we reduce our requested quantity
            prototype = matcher.withQuantity(prototype, limit);
        }
        final T prototypeFinal = prototype;
        long requiredQuantity = matcher.getQuantity(prototypeFinal);

        // Modify our match condition that will be used to test each separate interface
        if (checkQuantity) {
            matchFlags = matcher.withoutCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());
        }
        M finalMatchFlags = matchFlags;

        // Maintain a temporary mapping of prototype items to their total count over all positions,
        // plus the list of positions in which they are present.
        IIngredientMapMutable<T, M, Pair<Wrapper<Long>, List<PartPos>>> validInstancesCollapsed = new IngredientHashMap<>(getComponent());

        // Try extracting from all positions that match with the given conditions
        // until one succeeds.
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(() -> this.getMatchingPositions(prototypeFinal, finalMatchFlags), channel);
        Iterator<PartPos> it = partPosIteratorData.getRight();
        while (it.hasNext()) {
            PartPos pos = it.next();

            // Do a simulated extraction
            this.network.disablePosition(pos);
            T extractedSimulated = this.network.getPositionedStorage(pos).extract(prototypeFinal, finalMatchFlags, true);
            if (!this.canExtract(extractedSimulated)) {
                extractedSimulated = matcher.getEmptyInstance();
            }
            this.network.enablePosition(pos);
            T storagePrototype = getComponent().getMatcher().withQuantity(extractedSimulated, 1);

            // Get existing value from temporary mapping
            Pair<Wrapper<Long>, List<PartPos>> existingValue = validInstancesCollapsed.get(storagePrototype);<commit_msg>Add safety for network ingredient handling when positions are not loaded<commit_after>package org.cyclops.integrateddynamics.core.network;

import com.google.common.collect.Lists;
import org.apache.commons.lang3.tuple.Pair;
import org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
import org.cyclops.cyclopscore.datastructure.Wrapper;
import org.cyclops.cyclopscore.ingredient.collection.IIngredientMapMutable;
import org.cyclops.cyclopscore.ingredient.collection.IngredientHashMap;
import org.cyclops.integrateddynamics.api.network.IPartPosIteratorHandler;
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;
import org.cyclops.integrateddynamics.api.part.PartPos;

import javax.annotation.Nonnull;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
 * An abstract {@link IIngredientComponentStorage} that wraps over a {@link IPositionedAddonsNetworkIngredients}.
 *
 * @param <T> The instance type.
 * @param <M> The matching condition parameter.
 */
public abstract class IngredientChannelAdapter<T, M> implements IIngredientComponentStorage<T, M> {

    private final IPositionedAddonsNetworkIngredients<T, M> network;
    private final int channel;

    public IngredientChannelAdapter(PositionedAddonsNetworkIngredients<T, M> network, int channel) {
        this.network = network;
        this.channel = channel;
    }

    public IPositionedAddonsNetworkIngredients<T, M> getNetwork() {
        return network;
    }

    public int getChannel() {
        return channel;
    }

    @Override
    public IngredientComponent<T, M> getComponent() {
        return network.getComponent();
    }

    protected abstract Iterator<PartPos> getNonFullPositions();
    protected abstract Iterator<PartPos> getAllPositions();
    protected abstract Iterator<PartPos> getNonEmptyPositions();
    protected abstract Iterator<PartPos> getMatchingPositions(@Nonnull T prototype, M matchFlags);

    @Override
    public long getMaxQuantity() {
        long sum = 0;
        Iterator<PartPos> it = getAllPositions();
        while (it.hasNext()) {
            sum = Math.addExact(sum, this.network.getPositionedStorage(it.next()).getMaxQuantity());
        }
        return sum;
    }

    protected Pair<IPartPosIteratorHandler, Iterator<PartPos>> getPartPosIteratorData(Supplier<Iterator<PartPos>> iteratorSupplier, int channel) {
        IPartPosIteratorHandler handler = network.getPartPosIteratorHandler();
        if (handler == null) {
            handler = PartPosIteratorHandlerDummy.INSTANCE;
        } else {
            handler = handler.clone();
        }
        return Pair.of(handler, handler.handleIterator(iteratorSupplier, channel));
    }

    protected void savePartPosIteratorHandler(IPartPosIteratorHandler partPosIteratorHandler) {
        network.setPartPosIteratorHandler(partPosIteratorHandler);
    }

    @Override
    public T insert(@Nonnull T ingredient, boolean simulate) {
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();

        // Quickly return if the to-be-inserted ingredient was already empty
        if (matcher.isEmpty(ingredient)) {
            return ingredient;
        }

        // Limit rate
        long limit = network.getRateLimit();
        long currentQuantity = matcher.getQuantity(ingredient);
        long skippedQuantity = 0;
        if (currentQuantity > limit) {
            ingredient = matcher.withQuantity(ingredient, limit);
            skippedQuantity = currentQuantity - limit;
        }

        // Try inserting the ingredient at all positions that are not full,
        // until the ingredient becomes completely empty.
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(this::getNonFullPositions, channel);
        Iterator<PartPos> it = partPosIteratorData.getRight();
        while (it.hasNext()) {
            PartPos pos = it.next();
            // Skip if the position is not loaded
            if (!pos.getPos().isLoaded()) {
                continue;
            }
            this.network.disablePosition(pos);
            long quantityBefore = matcher.getQuantity(ingredient);
            ingredient = this.network.getPositionedStorage(pos).insert(ingredient, simulate);
            long quantityAfter = matcher.getQuantity(ingredient);
            this.network.enablePosition(pos);
            if (!simulate && quantityBefore != quantityAfter) {
                this.network.scheduleObservationForced(channel, pos); // Mark the position as 'changed'
            }
            if (matcher.isEmpty(ingredient)) {
                break;
            }
        }

        // Re-add skipped quantity to response if applicable
        if (skippedQuantity > 0) {
            ingredient = matcher.withQuantity(ingredient, skippedQuantity + matcher.getQuantity(ingredient));
        }

        if (!simulate) {
            savePartPosIteratorHandler(partPosIteratorData.getLeft());
        }

        return ingredient;
    }

    @Override
    public T extract(long maxQuantity, boolean simulate) {
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();

        // Limit rate
        maxQuantity = (int) Math.min(maxQuantity, network.getRateLimit());

        // Try extracting from all non-empty positions
        // until one succeeds.
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(this::getNonEmptyPositions, channel);
        Iterator<PartPos> it = partPosIteratorData.getRight();
        while (it.hasNext()) {
            PartPos pos = it.next();
            // Skip if the position is not loaded
            if (!pos.getPos().isLoaded()) {
                continue;
            }
            this.network.disablePosition(pos);
            T extracted = this.network.getPositionedStorage(pos).extract(maxQuantity, simulate);
            if (!this.canExtract(extracted)) {
                extracted = matcher.getEmptyInstance();
            }
            this.network.enablePosition(pos);
            if (!matcher.isEmpty(extracted)) {
                if (!simulate) {
                    this.network.scheduleObservationForced(channel, pos); // Mark the position as 'changed'
                    savePartPosIteratorHandler(partPosIteratorData.getLeft());
                }
                return extracted;
            }
        }

        if (!simulate) {
            savePartPosIteratorHandler(partPosIteratorData.getLeft());
        }

        return matcher.getEmptyInstance();
    }

    @Override
    public T extract(@Nonnull T prototype, M matchFlags, boolean simulate) {
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
        boolean checkQuantity = matcher.hasCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());

        // Limit rate
        long limit = network.getRateLimit();
        if (matcher.getQuantity(prototype) > limit) {
            // Fail immediately if we require more than the limit
            if (checkQuantity) {
                return matcher.getEmptyInstance();
            }

            // Otherwise, we reduce our requested quantity
            prototype = matcher.withQuantity(prototype, limit);
        }
        final T prototypeFinal = prototype;
        long requiredQuantity = matcher.getQuantity(prototypeFinal);

        // Modify our match condition that will be used to test each separate interface
        if (checkQuantity) {
            matchFlags = matcher.withoutCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());
        }
        M finalMatchFlags = matchFlags;

        // Maintain a temporary mapping of prototype items to their total count over all positions,
        // plus the list of positions in which they are present.
        IIngredientMapMutable<T, M, Pair<Wrapper<Long>, List<PartPos>>> validInstancesCollapsed = new IngredientHashMap<>(getComponent());

        // Try extracting from all positions that match with the given conditions
        // until one succeeds.
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(() -> this.getMatchingPositions(prototypeFinal, finalMatchFlags), channel);
        Iterator<PartPos> it = partPosIteratorData.getRight();
        while (it.hasNext()) {
            PartPos pos = it.next();

            // Skip if the position is not loaded
            if (!pos.getPos().isLoaded()) {
                continue;
            }

            // Do a simulated extraction
            this.network.disablePosition(pos);
            T extractedSimulated = this.network.getPositionedStorage(pos).extract(prototypeFinal, finalMatchFlags, true);
            if (!this.canExtract(extractedSimulated)) {
                extractedSimulated = matcher.getEmptyInstance();
            }
            this.network.enablePosition(pos);
            T storagePrototype = getComponent().getMatcher().withQuantity(extractedSimulated, 1);

            // Get existing value from temporary mapping
            Pair<Wrapper<Long>, List<PartPos>> existingValue = validInstancesCollapsed.get(storagePrototype);
{'fraction_non_alphanumeric': 0.09167120304844856, 'fraction_numerical': 0.0005443658138268917, 'mean_word_length': 2.868393345967572, 'pattern_counts': {'":': 0, '<': 77, '<?xml version=': 0, '>': 85, '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': '21756', 'n_tokens_mistral': 4868, 'n_tokens_neox': 4638, 'n_words': 1071}