// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.builtin.adventure.farming.states;

import java.util.Arrays;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
import java.util.UUID;
import java.time.LocalDateTime;
import java.time.temporal.TemporalUnit;
import java.time.temporal.ChronoUnit;
import com.hypixel.hytale.math.range.IntRange;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import it.unimi.dsi.fastutil.Pair;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import com.hypixel.hytale.function.consumer.TriConsumer;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.spawning.SpawnTestResult;
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
import com.hypixel.hytale.server.spawning.SpawningContext;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.asset.type.item.config.container.ItemDropContainer;
import java.util.Iterator;
import java.util.Map;
import java.time.Instant;
import java.util.Random;
import com.hypixel.hytale.logger.HytaleLogger;
import java.util.Objects;
import com.hypixel.hytale.server.core.asset.type.item.config.ItemDrop;
import com.hypixel.hytale.math.util.MathUtil;
import java.time.temporal.Temporal;
import java.time.Duration;
import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList;
import com.hypixel.hytale.server.npc.NPCPlugin;
import java.util.concurrent.ThreadLocalRandom;
import com.hypixel.hytale.builtin.tagset.TagSetPlugin;
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.builtin.adventure.farming.FarmingUtil;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.builtin.adventure.farming.component.CoopResidentComponent;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.entity.reference.PersistentRef;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.server.npc.metadata.CapturedNPCMetadata;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.Collection;
import javax.annotation.Nullable;
import com.hypixel.hytale.builtin.adventure.farming.config.FarmingCoopAsset;
import com.hypixel.fastutil.shorts.Short2ObjectConcurrentHashMap;
import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.inventory.container.EmptyItemContainer;
import java.util.ArrayList;
import com.hypixel.hytale.builtin.adventure.farming.FarmingPlugin;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import java.util.List;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.Component;

public class CoopBlock implements Component<ChunkStore>
{
    public static final String STATE_PRODUCE = "Produce_Ready";
    public static final BuilderCodec<CoopBlock> CODEC;
    protected String coopAssetId;
    protected List<CoopResident> residents;
    protected ItemContainer itemContainer;
    
    public static ComponentType<ChunkStore, CoopBlock> getComponentType() {
        return FarmingPlugin.get().getCoopBlockStateComponentType();
    }
    
    public CoopBlock() {
        this.residents = new ArrayList<CoopResident>();
        this.itemContainer = EmptyItemContainer.INSTANCE;
        final ArrayList<ItemStack> remainder = new ArrayList<ItemStack>();
        this.itemContainer = ItemContainer.ensureContainerCapacity(this.itemContainer, (short)5, (Short2ObjectConcurrentHashMap.ShortFunction<ItemContainer>)SimpleItemContainer::new, remainder);
    }
    
    @Nullable
    public FarmingCoopAsset getCoopAsset() {
        return FarmingCoopAsset.getAssetMap().getAsset(this.coopAssetId);
    }
    
    public CoopBlock(final String farmingCoopId, final List<CoopResident> residents, final ItemContainer itemContainer) {
        this.residents = new ArrayList<CoopResident>();
        this.itemContainer = EmptyItemContainer.INSTANCE;
        this.coopAssetId = farmingCoopId;
        this.residents.addAll(residents);
        this.itemContainer = itemContainer.clone();
        final List<ItemStack> remainder = new ObjectArrayList<ItemStack>();
        this.itemContainer = ItemContainer.ensureContainerCapacity(this.itemContainer, (short)5, (Short2ObjectConcurrentHashMap.ShortFunction<ItemContainer>)SimpleItemContainer::new, remainder);
    }
    
    public boolean tryPutResident(final CapturedNPCMetadata metadata, final WorldTimeResource worldTimeResource) {
        final FarmingCoopAsset coopAsset = this.getCoopAsset();
        if (coopAsset == null) {
            return false;
        }
        if (this.residents.size() >= coopAsset.getMaxResidents()) {
            return false;
        }
        if (!this.getCoopAcceptsNPCGroup(metadata.getRoleIndex())) {
            return false;
        }
        this.residents.add(new CoopResident(metadata, null, worldTimeResource.getGameTime()));
        return true;
    }
    
    public boolean tryPutWildResidentFromWild(final Store<EntityStore> store, final Ref<EntityStore> entityRef, final WorldTimeResource worldTimeResource, final Vector3i coopLocation) {
        final FarmingCoopAsset coopAsset = this.getCoopAsset();
        if (coopAsset == null) {
            return false;
        }
        final NPCEntity npcComponent = store.getComponent(entityRef, NPCEntity.getComponentType());
        if (npcComponent == null) {
            return false;
        }
        CoopResidentComponent coopResidentComponent = store.getComponent(entityRef, CoopResidentComponent.getComponentType());
        if (coopResidentComponent != null) {
            return false;
        }
        if (!this.getCoopAcceptsNPCGroup(npcComponent.getRoleIndex())) {
            return false;
        }
        if (this.residents.size() >= coopAsset.getMaxResidents()) {
            return false;
        }
        coopResidentComponent = store.ensureAndGetComponent(entityRef, CoopResidentComponent.getComponentType());
        coopResidentComponent.setCoopLocation(coopLocation);
        final UUIDComponent uuidComponent = store.getComponent(entityRef, UUIDComponent.getComponentType());
        if (uuidComponent == null) {
            return false;
        }
        final PersistentRef persistentRef = new PersistentRef();
        persistentRef.setEntity(entityRef, uuidComponent.getUuid());
        final CapturedNPCMetadata metadata = FarmingUtil.generateCapturedNPCMetadata(store, entityRef, npcComponent.getRoleIndex());
        final CoopResident residentRecord = new CoopResident(metadata, persistentRef, worldTimeResource.getGameTime());
        residentRecord.deployedToWorld = true;
        this.residents.add(residentRecord);
        return true;
    }
    
    public boolean getCoopAcceptsNPCGroup(final int npcRoleIndex) {
        final TagSetPlugin.TagSetLookup tagSetPlugin = TagSetPlugin.get(NPCGroup.class);
        final FarmingCoopAsset coopAsset = this.getCoopAsset();
        if (coopAsset == null) {
            return false;
        }
        final int[] acceptedNpcGroupIndexes = coopAsset.getAcceptedNpcGroupIndexes();
        if (acceptedNpcGroupIndexes == null) {
            return true;
        }
        for (final int group : acceptedNpcGroupIndexes) {
            if (tagSetPlugin.tagInSet(group, npcRoleIndex)) {
                return true;
            }
        }
        return false;
    }
    
    public void generateProduceToInventory(final WorldTimeResource worldTimeResource) {
        final Instant currentTime = worldTimeResource.getGameTime();
        final FarmingCoopAsset coopAsset = this.getCoopAsset();
        if (coopAsset == null) {
            return;
        }
        final Map<String, String> produceDropsMap = coopAsset.getProduceDrops();
        if (produceDropsMap.isEmpty()) {
            return;
        }
        final ThreadLocalRandom random = ThreadLocalRandom.current();
        final List<ItemStack> generatedItemDrops = new ArrayList<ItemStack>();
        for (final CoopResident resident : this.residents) {
            final Instant lastProduced = resident.getLastProduced();
            if (lastProduced == null) {
                resident.setLastProduced(currentTime);
            }
            else {
                final CapturedNPCMetadata residentMeta = resident.getMetadata();
                final int npcRoleIndex = residentMeta.getRoleIndex();
                final String npcName = NPCPlugin.get().getName(npcRoleIndex);
                final String npcDropListName = produceDropsMap.get(npcName);
                if (npcDropListName == null) {
                    continue;
                }
                final ItemDropList dropListAsset = ItemDropList.getAssetMap().getAsset(npcDropListName);
                if (dropListAsset == null) {
                    continue;
                }
                final Duration harvestDiff = Duration.between(lastProduced, currentTime);
                final long hoursSinceLastHarvest = harvestDiff.toHours();
                final int produceCount = MathUtil.ceil(hoursSinceLastHarvest / (float)WorldTimeResource.HOURS_PER_DAY);
                final List<ItemDrop> configuredItemDrops = new ArrayList<ItemDrop>();
                for (int i = 0; i < produceCount; ++i) {
                    final ItemDropContainer container = dropListAsset.getContainer();
                    final List<ItemDrop> drops = configuredItemDrops;
                    final ThreadLocalRandom obj = random;
                    Objects.requireNonNull(obj);
                    container.populateDrops(drops, obj::nextDouble, npcDropListName);
                    for (final ItemDrop drop : configuredItemDrops) {
                        if (drop == null || drop.getItemId() == null) {
                            HytaleLogger.forEnclosingClass().atWarning().log("Tried to create ItemDrop for non-existent item in drop list id '%s'", npcDropListName);
                        }
                        else {
                            final int amount = drop.getRandomQuantity(random);
                            if (amount <= 0) {
                                continue;
                            }
                            generatedItemDrops.add(new ItemStack(drop.getItemId(), amount, drop.getMetadata()));
                        }
                    }
                    configuredItemDrops.clear();
                }
                resident.setLastProduced(currentTime);
            }
        }
        this.itemContainer.addItemStacks(generatedItemDrops);
    }
    
    public void gatherProduceFromInventory(final ItemContainer playerInventory) {
        for (final ItemStack item : this.itemContainer.removeAllItemStacks()) {
            playerInventory.addItemStack(item);
        }
    }
    
    public void ensureSpawnResidentsInWorld(final World world, final Store<EntityStore> store, final Vector3d coopLocation, final Vector3d spawnOffset) {
        final NPCPlugin npcModule = NPCPlugin.get();
        final FarmingCoopAsset coopAsset = this.getCoopAsset();
        if (coopAsset == null) {
            return;
        }
        final float radiansPerSpawn = 6.2831855f / coopAsset.getMaxResidents();
        Vector3d spawnOffsetIteration = spawnOffset;
        final SpawningContext spawningContext = new SpawningContext();
        for (final CoopResident resident : this.residents) {
            final CapturedNPCMetadata residentMeta = resident.getMetadata();
            final int npcRoleIndex = residentMeta.getRoleIndex();
            final boolean residentDeployed = resident.getDeployedToWorld();
            final PersistentRef residentEntityId = resident.getPersistentRef();
            if (!residentDeployed) {
                if (residentEntityId != null) {
                    continue;
                }
                final Vector3d residentSpawnLocation = new Vector3d().assign(coopLocation).add(spawnOffsetIteration);
                final Builder<Role> roleBuilder = NPCPlugin.get().tryGetCachedValidRole(npcRoleIndex);
                if (roleBuilder == null) {
                    continue;
                }
                spawningContext.setSpawnable((ISpawnableWithModel)roleBuilder);
                if (!spawningContext.set(world, residentSpawnLocation.x, residentSpawnLocation.y, residentSpawnLocation.z)) {
                    continue;
                }
                if (spawningContext.canSpawn() != SpawnTestResult.TEST_OK) {
                    continue;
                }
                final Pair<Ref<EntityStore>, NPCEntity> npcPair = npcModule.spawnEntity(store, npcRoleIndex, spawningContext.newPosition(), Vector3f.ZERO, null, null);
                if (npcPair == null) {
                    resident.setPersistentRef(null);
                    resident.setDeployedToWorld(false);
                }
                else {
                    final Ref<EntityStore> npcRef = npcPair.first();
                    final NPCEntity npcComponent = npcPair.second();
                    npcComponent.getLeashPoint().assign(coopLocation);
                    if (npcRef == null || !npcRef.isValid()) {
                        resident.setPersistentRef(null);
                        resident.setDeployedToWorld(false);
                    }
                    else {
                        final UUIDComponent uuidComponent = store.getComponent(npcRef, UUIDComponent.getComponentType());
                        if (uuidComponent == null) {
                            resident.setPersistentRef(null);
                            resident.setDeployedToWorld(false);
                        }
                        else {
                            final CoopResidentComponent coopResidentComponent = new CoopResidentComponent();
                            coopResidentComponent.setCoopLocation(coopLocation.toVector3i());
                            store.addComponent(npcRef, CoopResidentComponent.getComponentType(), coopResidentComponent);
                            final PersistentRef persistentRef = new PersistentRef();
                            persistentRef.setEntity(npcRef, uuidComponent.getUuid());
                            resident.setPersistentRef(persistentRef);
                            resident.setDeployedToWorld(true);
                            spawnOffsetIteration = spawnOffsetIteration.rotateY(radiansPerSpawn);
                        }
                    }
                }
            }
        }
    }
    
    public void ensureNoResidentsInWorld(final Store<EntityStore> store) {
        final ArrayList<CoopResident> residentsToRemove = new ArrayList<CoopResident>();
        for (final CoopResident resident : this.residents) {
            final boolean deployed = resident.getDeployedToWorld();
            final PersistentRef entityUuid = resident.getPersistentRef();
            if (!deployed && entityUuid == null) {
                continue;
            }
            final Ref<EntityStore> entityRef = entityUuid.getEntity(store);
            if (entityRef == null) {
                residentsToRemove.add(resident);
            }
            else {
                final CoopResidentComponent coopResidentComponent = store.getComponent(entityRef, CoopResidentComponent.getComponentType());
                if (coopResidentComponent == null) {
                    residentsToRemove.add(resident);
                }
                else {
                    final DeathComponent deathComponent = store.getComponent(entityRef, DeathComponent.getComponentType());
                    if (deathComponent != null) {
                        residentsToRemove.add(resident);
                    }
                    else {
                        coopResidentComponent.setMarkedForDespawn(true);
                        resident.setPersistentRef(null);
                        resident.setDeployedToWorld(false);
                    }
                }
            }
        }
        for (final CoopResident resident : residentsToRemove) {
            this.residents.remove(resident);
        }
    }
    
    public boolean shouldResidentsBeInCoop(final WorldTimeResource worldTimeResource) {
        final FarmingCoopAsset coopAsset = this.getCoopAsset();
        if (coopAsset == null) {
            return true;
        }
        final IntRange roamTimeRange = coopAsset.getResidentRoamTime();
        if (roamTimeRange == null) {
            return true;
        }
        final int gameHour = worldTimeResource.getCurrentHour();
        return !roamTimeRange.includes(gameHour);
    }
    
    @Nullable
    public Instant getNextScheduledTick(final WorldTimeResource worldTimeResource) {
        final Instant gameTime = worldTimeResource.getGameTime();
        final LocalDateTime gameDateTime = worldTimeResource.getGameDateTime();
        final int gameHour = worldTimeResource.getCurrentHour();
        final int minutes = gameDateTime.getMinute();
        final FarmingCoopAsset coopAsset = this.getCoopAsset();
        if (coopAsset == null) {
            return null;
        }
        final IntRange roamTimeRange = coopAsset.getResidentRoamTime();
        if (roamTimeRange == null) {
            return null;
        }
        int nextScheduledHour = 0;
        final int minTime = roamTimeRange.getInclusiveMin();
        final int maxTime = roamTimeRange.getInclusiveMax();
        if (coopAsset.getResidentRoamTime().includes(gameHour)) {
            nextScheduledHour = coopAsset.getResidentRoamTime().getInclusiveMax() + 1 - gameHour;
        }
        else if (gameHour > maxTime) {
            nextScheduledHour = WorldTimeResource.HOURS_PER_DAY - gameHour + minTime;
        }
        else {
            nextScheduledHour = minTime - gameHour;
        }
        return gameTime.plus(nextScheduledHour * 60L - minutes, (TemporalUnit)ChronoUnit.MINUTES);
    }
    
    public void handleResidentDespawn(final UUID entityUuid) {
        CoopResident removedResident = null;
        for (final CoopResident resident : this.residents) {
            if (resident.persistentRef == null) {
                continue;
            }
            if (resident.persistentRef.getUuid() == entityUuid) {
                removedResident = resident;
                break;
            }
        }
        if (removedResident == null) {
            return;
        }
        this.residents.remove(removedResident);
    }
    
    public void handleBlockBroken(final World world, final WorldTimeResource worldTimeResource, final Store<EntityStore> store, final int blockX, final int blockY, final int blockZ) {
        final Vector3i location = new Vector3i(blockX, blockY, blockZ);
        world.execute(() -> this.ensureSpawnResidentsInWorld(world, store, location.toVector3d(), new Vector3d().assign(Vector3d.FORWARD)));
        this.generateProduceToInventory(worldTimeResource);
        final Vector3d dropPosition = new Vector3d(blockX + 0.5f, blockY, blockZ + 0.5f);
        final Holder<EntityStore>[] itemEntityHolders = ItemComponent.generateItemDrops(store, this.itemContainer.removeAllItemStacks(), dropPosition, Vector3f.ZERO);
        if (itemEntityHolders.length > 0) {
            world.execute(() -> store.addEntities(itemEntityHolders, AddReason.SPAWN));
        }
        world.execute(() -> {
            for (final CoopResident resident : this.residents) {
                final PersistentRef persistentRef = resident.getPersistentRef();
                if (persistentRef == null) {
                    continue;
                }
                else {
                    final Ref<EntityStore> ref = persistentRef.getEntity(store);
                    if (ref != null) {
                        store.tryRemoveComponent(ref, CoopResidentComponent.getComponentType());
                    }
                }
            }
        });
    }
    
    public boolean hasProduce() {
        return !this.itemContainer.isEmpty();
    }
    
    @Override
    public Component<ChunkStore> clone() {
        return new CoopBlock(this.coopAssetId, this.residents, this.itemContainer);
    }
    
    static {
        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     2: invokedynamic   BootstrapMethod #6, get:()Ljava/util/function/Supplier;
        //     7: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    10: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    13: dup            
        //    14: ldc_w           "FarmingCoopId"
        //    17: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
        //    20: iconst_1       
        //    21: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;Z)V
        //    24: invokedynamic   BootstrapMethod #7, accept:()Ljava/util/function/BiConsumer;
        //    29: invokedynamic   BootstrapMethod #8, apply:()Ljava/util/function/Function;
        //    34: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
        //    37: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //    40: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    43: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    46: dup            
        //    47: ldc_w           "Residents"
        //    50: new             Lcom/hypixel/hytale/codec/codecs/array/ArrayCodec;
        //    53: dup            
        //    54: getstatic       com/hypixel/hytale/builtin/adventure/farming/states/CoopBlock$CoopResident.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //    57: invokedynamic   BootstrapMethod #9, apply:()Ljava/util/function/IntFunction;
        //    62: invokespecial   com/hypixel/hytale/codec/codecs/array/ArrayCodec.<init>:(Lcom/hypixel/hytale/codec/Codec;Ljava/util/function/IntFunction;)V
        //    65: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //    68: invokedynamic   BootstrapMethod #10, accept:()Ljava/util/function/BiConsumer;
        //    73: invokedynamic   BootstrapMethod #11, apply:()Ljava/util/function/Function;
        //    78: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
        //    81: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //    84: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    87: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    90: dup            
        //    91: ldc_w           "Storage"
        //    94: getstatic       com/hypixel/hytale/server/core/inventory/container/ItemContainer.CODEC:Lcom/hypixel/hytale/codec/lookup/CodecMapCodec;
        //    97: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //   100: invokedynamic   BootstrapMethod #12, accept:()Ljava/util/function/BiConsumer;
        //   105: invokedynamic   BootstrapMethod #13, apply:()Ljava/util/function/Function;
        //   110: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
        //   113: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   116: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   119: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //   122: putstatic       com/hypixel/hytale/builtin/adventure/farming/states/CoopBlock.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //   125: return         
        // 
        // The error that occurred was:
        // 
        // java.lang.UnsupportedOperationException: The requested operation is not supported.
        //     at com.strobel.util.ContractUtils.unsupported(ContractUtils.java:27)
        //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:284)
        //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:279)
        //     at com.strobel.assembler.metadata.TypeReference.makeGenericType(TypeReference.java:154)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:225)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
        //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:211)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
        //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitMethod(TypeSubstitutionVisitor.java:314)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2611)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1083)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:684)
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:667)
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:373)
        //     at com.strobel.decompiler.ast.TypeAnalysis.run(TypeAnalysis.java:95)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:344)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:206)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:93)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:868)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:761)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:638)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:162)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:137)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
        //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
        //     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
        //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
        // 
        throw new IllegalStateException("An error occurred while decompiling this method.");
    }
    
    public static class CoopResident
    {
        public static final BuilderCodec<CoopResident> CODEC;
        protected CapturedNPCMetadata metadata;
        @Nullable
        protected PersistentRef persistentRef;
        protected boolean deployedToWorld;
        protected Instant lastProduced;
        
        public CoopResident() {
        }
        
        public CoopResident(final CapturedNPCMetadata metadata, final PersistentRef persistentRef, final Instant lastProduced) {
            this.metadata = metadata;
            this.persistentRef = persistentRef;
            this.lastProduced = lastProduced;
        }
        
        public CapturedNPCMetadata getMetadata() {
            return this.metadata;
        }
        
        @Nullable
        public PersistentRef getPersistentRef() {
            return this.persistentRef;
        }
        
        public void setPersistentRef(@Nullable final PersistentRef persistentRef) {
            this.persistentRef = persistentRef;
        }
        
        public boolean getDeployedToWorld() {
            return this.deployedToWorld;
        }
        
        public void setDeployedToWorld(final boolean deployedToWorld) {
            this.deployedToWorld = deployedToWorld;
        }
        
        public Instant getLastProduced() {
            return this.lastProduced;
        }
        
        public void setLastProduced(final Instant lastProduced) {
            this.lastProduced = lastProduced;
        }
        
        static {
            // 
            // This method could not be decompiled.
            // 
            // Original Bytecode:
            // 
            //     2: invokedynamic   BootstrapMethod #0, get:()Ljava/util/function/Supplier;
            //     7: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    10: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    13: dup            
            //    14: ldc             "Metadata"
            //    16: getstatic       com/hypixel/hytale/server/npc/metadata/CapturedNPCMetadata.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //    19: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    22: invokedynamic   BootstrapMethod #1, accept:()Ljava/util/function/BiConsumer;
            //    27: invokedynamic   BootstrapMethod #2, apply:()Ljava/util/function/Function;
            //    32: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //    35: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //    38: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    41: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    44: dup            
            //    45: ldc             "PersistentRef"
            //    47: getstatic       com/hypixel/hytale/server/core/entity/reference/PersistentRef.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //    50: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    53: invokedynamic   BootstrapMethod #3, accept:()Ljava/util/function/BiConsumer;
            //    58: invokedynamic   BootstrapMethod #4, apply:()Ljava/util/function/Function;
            //    63: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //    66: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //    69: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    72: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    75: dup            
            //    76: ldc             "DeployedToWorld"
            //    78: getstatic       com/hypixel/hytale/codec/Codec.BOOLEAN:Lcom/hypixel/hytale/codec/codecs/simple/BooleanCodec;
            //    81: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    84: invokedynamic   BootstrapMethod #5, accept:()Ljava/util/function/BiConsumer;
            //    89: invokedynamic   BootstrapMethod #6, apply:()Ljava/util/function/Function;
            //    94: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //    97: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   100: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   103: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //   106: dup            
            //   107: ldc             "LastHarvested"
            //   109: getstatic       com/hypixel/hytale/codec/Codec.INSTANT:Lcom/hypixel/hytale/codec/function/FunctionCodec;
            //   112: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //   115: invokedynamic   BootstrapMethod #7, accept:()Ljava/util/function/BiConsumer;
            //   120: invokedynamic   BootstrapMethod #8, apply:()Ljava/util/function/Function;
            //   125: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //   128: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   131: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   134: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //   137: putstatic       com/hypixel/hytale/builtin/adventure/farming/states/CoopBlock$CoopResident.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //   140: return         
            // 
            // The error that occurred was:
            // 
            // java.lang.UnsupportedOperationException: The requested operation is not supported.
            //     at com.strobel.util.ContractUtils.unsupported(ContractUtils.java:27)
            //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:284)
            //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:279)
            //     at com.strobel.assembler.metadata.TypeReference.makeGenericType(TypeReference.java:154)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:225)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
            //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:211)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
            //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitMethod(TypeSubstitutionVisitor.java:314)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2611)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1083)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:684)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:667)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:373)
            //     at com.strobel.decompiler.ast.TypeAnalysis.run(TypeAnalysis.java:95)
            //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:344)
            //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
            //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:206)
            //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:93)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:868)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:761)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:638)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:662)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:162)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:137)
            //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
            //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
            //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
            //     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
            //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
            // 
            throw new IllegalStateException("An error occurred while decompiling this method.");
        }
    }
}
