/*
 * Copyright (c) Forge Development LLC and contributors
 * SPDX-License-Identifier: LGPL-2.1-only
 */

package net.minecraftforge.common.loot;

import java.util.function.Predicate;

import com.mojang.datafixers.Products;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.levelgen.feature.trunkplacers.BendingTrunkPlacer;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemConditions;
import org.jetbrains.annotations.NotNull;

/**
 * A base implementation of a Global Loot Modifier for modders to extend.
 * Takes care of ILootCondition matching and comes with the base codec to extend.
 */
public abstract class LootModifier implements IGlobalLootModifier
{
    protected final LootItemCondition[] conditions;
    private final Predicate<LootContext> combinedConditions;

    /**
     * Simplifies codec creation, especially if no other fields are added:
     * <p>
     * {@code
     * public static final Codec<MyLootModifier> CODEC = RecordCodecBuilder.create(inst -> codecStart(inst).apply(inst, MyLootModifier::new));
     * }
     * </p>
     * Otherwise can follow this with #and() to add more fields.
     * Examples: Forge Test Subclasses or {@link BendingTrunkPlacer#CODEC}
     */
    protected static <T extends LootModifier> Products.P1<RecordCodecBuilder.Mu<T>, LootItemCondition[]> codecStart(RecordCodecBuilder.Instance<T> instance)
    {
        return instance.group(LOOT_CONDITIONS_CODEC.fieldOf("conditions").forGetter(lm -> lm.conditions));
    }

    /**
     * Constructs a LootModifier.
     * @param conditionsIn the ILootConditions that need to be matched before the loot is modified.
     */
    protected LootModifier(LootItemCondition[] conditionsIn)
    {
        this.conditions = conditionsIn;
        this.combinedConditions = LootItemConditions.andConditions(conditionsIn);
    }

    @NotNull
    @Override
    public final ObjectArrayList<ItemStack> apply(ObjectArrayList<ItemStack> generatedLoot, LootContext context)
    {
        return this.combinedConditions.test(context) ? this.doApply(generatedLoot, context) : generatedLoot;
    }

    /**
     * Applies the modifier to the generated loot (all loot conditions have already been checked
     * and have returned true).
     * @param generatedLoot the list of ItemStacks that will be dropped, generated by loot tables
     * @param context the LootContext, identical to what is passed to loot tables
     * @return modified loot drops
     */
    @NotNull
    protected abstract ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context);
}
