package com.infinityraider.agricraft.content.core;

import com.infinityraider.agricraft.AgriCraft;
import com.infinityraider.agricraft.api.v1.AgriApi;
import com.infinityraider.agricraft.api.v1.crop.IAgriCrop;
import com.infinityraider.agricraft.api.v1.event.AgriCropEvent;
import com.infinityraider.agricraft.api.v1.genetics.IAgriGenome;
import com.infinityraider.agricraft.api.v1.items.IAgriSeedItem;
import com.infinityraider.agricraft.api.v1.plant.IAgriPlant;
import com.infinityraider.agricraft.api.v1.seed.AgriSeed;
import com.infinityraider.agricraft.impl.v1.plant.NoPlant;
import com.infinityraider.agricraft.content.AgriTabs;
import com.infinityraider.agricraft.reference.AgriToolTips;
import com.infinityraider.agricraft.reference.Names;
import com.infinityraider.infinitylib.item.ItemBase;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;

public class ItemDynamicAgriSeed extends ItemBase implements IAgriSeedItem {
    private static final IAgriPlant NO_PLANT = NoPlant.getInstance();

    public static ItemStack toStack(AgriSeed seed, int amount) {
        return toStack(seed.getGenome(), amount);
    }

    public static ItemStack toStack(IAgriPlant plant, int amount) {
        return toStack(AgriApi.getAgriGenomeBuilder(plant).build(), amount);
    }

    public static ItemStack toStack(IAgriGenome genome, int amount) {
        // Create the stack.
        ItemStack stack = new ItemStack(AgriCraft.instance.getModItemRegistry().seed, amount);
        // Create the tag.
        CompoundNBT tag = new CompoundNBT();
        genome.writeToNBT(tag);
        // Put the tag on stack
        stack.setTag(tag);
        // Return the stack
        return stack;
    }

    public ItemDynamicAgriSeed() {
        super(Names.Items.SEED, new Properties().group(AgriTabs.TAB_AGRICRAFT_SEED));
    }

    @Nonnull
    @Override
    public ActionResultType onItemUse(@Nonnull ItemUseContext context) {
        World world = context.getWorld();
        BlockPos pos = context.getPos();
        TileEntity tile = world.getTileEntity(pos);
        ItemStack stack = context.getItem();
        PlayerEntity player = context.getPlayer();
        // If crop sticks were clicked, attempt to plant the seed
        if (tile instanceof TileEntityCropSticks) {
            return this.attemptSeedPlant(
                    (TileEntityCropSticks) tile, stack, player == null || !player.isCreative()
            );
        }
        // If a soil was clicked, check the block on top of the soil and handle accordingly
        BlockState state = world.getBlockState(pos);
        return AgriApi.getSoilRegistry().valueOf(state).map(soil -> {
            BlockPos up = pos.up();
            TileEntity above = world.getTileEntity(up);
            // There are currently crop sticks on the soil, attempt to plant on the crop sticks
            if (above instanceof TileEntityCropSticks) {
                return this.attemptSeedPlant(
                        (TileEntityCropSticks) above, stack, player == null || !player.isCreative()
                );
            }
            // There are currently no crop sticks, check if the place is suitable and plant the plant directly
            if (above == null && AgriCraft.instance.getConfig().allowPlantingOutsideCropSticks()) {
                BlockState newState = AgriCraft.instance.getModBlockRegistry().crop_plant.getStateForPlacement(world, up);
                if (newState != null && world.setBlockState(up, newState, 11)) {
                    boolean success = AgriApi.getCrop(world, up).map(crop -> {
                        if (MinecraftForge.EVENT_BUS.post(new AgriCropEvent.Trowel.Pre(crop, stack, player))) {
                            return false;
                        }
                        return this.getGenome(context.getItem()).map(crop::setGenome).map(result -> {
                            if (result) {
                                if (player == null || !player.isCreative()) {
                                    stack.shrink(1);
                                }
                                MinecraftForge.EVENT_BUS.post(new AgriCropEvent.Trowel.Post(crop, stack, player));
                            }
                            return result;
                        }).orElse(false);
                    }).orElse(false);
                    if (success) {
                        return ActionResultType.SUCCESS;
                    } else {
                        world.setBlockState(up, Blocks.AIR.getDefaultState());
                    }
                }
            }
            // Neither alternative option was successful, delegate the call to the super method
            return super.onItemUse(context);
        }).orElse(super.onItemUse(context));
    }

    protected ActionResultType attemptSeedPlant(IAgriCrop crop, ItemStack stack, boolean consumeItem) {
        return AgriApi.getSeedAdapterizer().valueOf(stack)
                .map(seed -> {
                    if (crop.plantSeed(seed)) {
                        if (consumeItem) {
                            stack.shrink(1);
                        }
                        return ActionResultType.CONSUME;
                    } else {
                        return ActionResultType.PASS;
                    }
                })
                .orElse(ActionResultType.PASS);
    }

    @Nonnull
    public ITextComponent getDisplayName(@Nonnull ItemStack stack) {
        return this.getPlant(stack).getSeedName();
    }

    @Override
    public void fillItemGroup(@Nonnull ItemGroup group, @Nonnull NonNullList<ItemStack> items) {
        if(this.isInGroup(group)) {
            items.clear();
            AgriApi.getPlantRegistry()
                    .stream()
                    .map(IAgriPlant::toAgriSeed)
                    .map(seed -> toStack(seed, 1))
                    .forEach(items::add);
        }
    }

    @Override
    @OnlyIn(Dist.CLIENT)
    public void addInformation(@Nonnull ItemStack stack, @Nullable World world, @Nonnull List<ITextComponent> tooltip, @Nonnull ITooltipFlag advanced) {
        this.getStats(stack).map(stats -> {
            stats.addTooltips(tooltip::add);
            return true;
        }).orElseGet(() -> {
            tooltip.add(AgriToolTips.UNKNOWN);
            return false;
        });
    }

    @Nonnull
    @Override
    public Optional<IAgriGenome> getGenome(ItemStack stack) {
        CompoundNBT tag = stack.getTag();
        if(tag == null) {
            return Optional.empty();
        }
        IAgriGenome genome = AgriApi.getAgriGenomeBuilder(NO_PLANT).build();
        if(!genome.readFromNBT(tag)) {
            // Faulty NBT
            stack.setTag(null);
            return Optional.empty();
        }
        return Optional.of(genome);
    }

    @Override
    public boolean doesSneakBypassUse(ItemStack stack, IWorldReader world, BlockPos pos, PlayerEntity player) {
        return true;
    }
}

@@ -2591,16 +2591,33 @@
 erties()
+%0A                
 .group(A
@@ -2643,16 +2643,59 @@
 FT_SEED)
+%0A                .maxStackSize(64)%0A        
 );%0A    %7D

Allow stacking of seeds up to 64