package net.minecraft.tags;

import java.util.function.Function;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.concurrent.CompletionStage;
import com.mojang.datafixers.util.Pair;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.core.Registry;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.server.packs.resources.PreparableReloadListener;

public class TagManager implements PreparableReloadListener {
    private final SynchronizableTagCollection<Block> blocks;
    private final SynchronizableTagCollection<Item> items;
    private final SynchronizableTagCollection<Fluid> fluids;
    private final SynchronizableTagCollection<EntityType<?>> entityTypes;
    
    public TagManager() {
        this.blocks = new SynchronizableTagCollection<Block>(Registry.BLOCK, "tags/blocks", "block");
        this.items = new SynchronizableTagCollection<Item>(Registry.ITEM, "tags/items", "item");
        this.fluids = new SynchronizableTagCollection<Fluid>(Registry.FLUID, "tags/fluids", "fluid");
        this.entityTypes = new SynchronizableTagCollection<EntityType<?>>(Registry.ENTITY_TYPE, "tags/entity_types", "entity_type");
    }
    
    public SynchronizableTagCollection<Block> getBlocks() {
        return this.blocks;
    }
    
    public SynchronizableTagCollection<Item> getItems() {
        return this.items;
    }
    
    public SynchronizableTagCollection<Fluid> getFluids() {
        return this.fluids;
    }
    
    public SynchronizableTagCollection<EntityType<?>> getEntityTypes() {
        return this.entityTypes;
    }
    
    public void serializeToNetwork(final FriendlyByteBuf kv) {
        this.blocks.serializeToNetwork(kv);
        this.items.serializeToNetwork(kv);
        this.fluids.serializeToNetwork(kv);
        this.entityTypes.serializeToNetwork(kv);
    }
    
    public static TagManager deserializeFromNetwork(final FriendlyByteBuf kv) {
        final TagManager abb2 = new TagManager();
        abb2.getBlocks().loadFromNetwork(kv);
        abb2.getItems().loadFromNetwork(kv);
        abb2.getFluids().loadFromNetwork(kv);
        abb2.getEntityTypes().loadFromNetwork(kv);
        return abb2;
    }
    
    @Override
    public CompletableFuture<Void> reload(final PreparationBarrier a, final ResourceManager zb, final ProfilerFiller aio3, final ProfilerFiller aio4, final Executor executor5, final Executor executor6) {
        final CompletableFuture<Map<ResourceLocation, Tag.Builder<Block>>> completableFuture8 = this.blocks.prepare(zb, executor5);
        final CompletableFuture<Map<ResourceLocation, Tag.Builder<Item>>> completableFuture9 = this.items.prepare(zb, executor5);
        final CompletableFuture<Map<ResourceLocation, Tag.Builder<Fluid>>> completableFuture10 = this.fluids.prepare(zb, executor5);
        final CompletableFuture<Map<ResourceLocation, Tag.Builder<EntityType<?>>>> completableFuture11 = this.entityTypes.prepare(zb, executor5);
        return completableFuture8.thenCombine(completableFuture9, Pair::of).thenCombine(completableFuture10.thenCombine(completableFuture11, Pair::of), (pair1, pair2) -> new Preparations((Map<ResourceLocation, Tag.Builder<Block>>)pair1.getFirst(), (Map<ResourceLocation, Tag.Builder<Item>>)pair1.getSecond(), (Map<ResourceLocation, Tag.Builder<Fluid>>)pair2.getFirst(), (Map<ResourceLocation, Tag.Builder<EntityType<?>>>)pair2.getSecond())).thenCompose(a::wait).thenAcceptAsync(a -> {
            this.blocks.load(a.blocks);
            this.items.load(a.items);
            this.fluids.load(a.fluids);
            this.entityTypes.load(a.entityTypes);
            BlockTags.reset(this.blocks);
            ItemTags.reset(this.items);
            FluidTags.reset(this.fluids);
            EntityTypeTags.reset(this.entityTypes);
        }, executor6);
    }
    
    public static class Preparations {
        final Map<ResourceLocation, Tag.Builder<Block>> blocks;
        final Map<ResourceLocation, Tag.Builder<Item>> items;
        final Map<ResourceLocation, Tag.Builder<Fluid>> fluids;
        final Map<ResourceLocation, Tag.Builder<EntityType<?>>> entityTypes;
        
        public Preparations(final Map<ResourceLocation, Tag.Builder<Block>> map1, final Map<ResourceLocation, Tag.Builder<Item>> map2, final Map<ResourceLocation, Tag.Builder<Fluid>> map3, final Map<ResourceLocation, Tag.Builder<EntityType<?>>> map4) {
            this.blocks = map1;
            this.items = map2;
            this.fluids = map3;
            this.entityTypes = map4;
        }
    }
}
