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

package com.hypixel.hytale.server.spawning.beacons;

import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.systems.NewSpawnStartTickingSystem;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import it.unimi.dsi.fastutil.Pair;
import com.hypixel.hytale.server.flock.config.FlockAsset;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.spawning.assets.spawns.config.RoleSpawnParameters;
import java.util.logging.Level;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.function.consumer.TriConsumer;
import com.hypixel.hytale.server.flock.FlockPlugin;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
import com.hypixel.hytale.server.npc.NPCPlugin;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import com.hypixel.hytale.math.random.RandomExtra;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.spawning.util.FloodFillPositionSelector;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.spawning.assets.spawns.config.BeaconNPCSpawn;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.world.World;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.spawning.SpawningContext;
import it.unimi.dsi.fastutil.ints.IntSet;
import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.server.core.entity.Entity;

public class SpawnBeacon extends Entity
{
    public static final BuilderCodec<SpawnBeacon> CODEC;
    private BeaconSpawnWrapper spawnWrapper;
    private String spawnConfigId;
    private final IntSet unspawnableRoles;
    private final SpawningContext spawningContext;
    
    @Nullable
    public static ComponentType<EntityStore, SpawnBeacon> getComponentType() {
        return EntityModule.get().getComponentType(SpawnBeacon.class);
    }
    
    public SpawnBeacon() {
        this.unspawnableRoles = new IntOpenHashSet();
        this.spawningContext = new SpawningContext();
    }
    
    public SpawnBeacon(final World world) {
        super(world);
        this.unspawnableRoles = new IntOpenHashSet();
        this.spawningContext = new SpawningContext();
    }
    
    public BeaconSpawnWrapper getSpawnWrapper() {
        return this.spawnWrapper;
    }
    
    public void setSpawnWrapper(@Nonnull final BeaconSpawnWrapper spawnWrapper) {
        this.spawnWrapper = spawnWrapper;
        this.spawnConfigId = spawnWrapper.getSpawn().getId();
    }
    
    public String getSpawnConfigId() {
        return this.spawnConfigId;
    }
    
    @Override
    public boolean isHiddenFromLivingEntity(@Nonnull final Ref<EntityStore> ref, @Nonnull final Ref<EntityStore> targetRef, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final Player targetPlayerComponent = componentAccessor.getComponent(targetRef, Player.getComponentType());
        return targetPlayerComponent == null || targetPlayerComponent.getGameMode() != GameMode.Creative;
    }
    
    @Override
    public boolean isCollidable() {
        return false;
    }
    
    @Override
    public void moveTo(@Nonnull final Ref<EntityStore> ref, final double locX, final double locY, final double locZ, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        super.moveTo(ref, locX, locY, locZ, componentAccessor);
        final FloodFillPositionSelector positionSelectorComponent = componentAccessor.getComponent(ref, FloodFillPositionSelector.getComponentType());
        assert positionSelectorComponent != null;
        positionSelectorComponent.setCalculatePositionsAfter(SpawnBeaconSystems.POSITION_CALCULATION_DELAY_RANGE[1]);
        positionSelectorComponent.forceRebuildCache();
        this.unspawnableRoles.clear();
    }
    
    public boolean manualTrigger(@Nonnull final Ref<EntityStore> ref, @Nonnull final FloodFillPositionSelector positionSelector, @Nonnull final Ref<EntityStore> targetRef, @Nonnull final Store<EntityStore> store) {
        final int concurrentSpawns = RandomExtra.randomRange(this.spawnWrapper.getSpawn().getConcurrentSpawnsRange());
        int spawnedCount = 0;
        for (int i = 0; i < concurrentSpawns; ++i) {
            final RoleSpawnParameters roleSpawnParameters = this.spawnWrapper.pickRole(ThreadLocalRandom.current());
            if (roleSpawnParameters != null) {
                final int roleIndex = NPCPlugin.get().getIndex(roleSpawnParameters.getId());
                if (!this.unspawnableRoles.contains(roleIndex)) {
                    final ISpawnableWithModel spawnable = (ISpawnableWithModel)NPCPlugin.get().tryGetCachedValidRole(roleIndex);
                    this.spawningContext.setSpawnable(spawnable);
                    if (!positionSelector.hasPositionsForRole(roleIndex)) {
                        this.markUnspawnable(ref, roleIndex, store);
                        this.spawningContext.releaseFull();
                    }
                    else {
                        final Vector3d targetPos = targetRef.getStore().getComponent(targetRef, TransformComponent.getComponentType()).getPosition();
                        if (!positionSelector.prepareSpawnContext(targetPos, concurrentSpawns, roleIndex, this.spawningContext, this.spawnWrapper)) {
                            this.spawningContext.releaseFull();
                        }
                        else {
                            final Vector3d position = this.spawningContext.newPosition();
                            final Vector3f rotation = this.spawningContext.newRotation();
                            final FlockAsset flockDefinition = roleSpawnParameters.getFlockDefinition();
                            final int flockSize = (flockDefinition != null) ? flockDefinition.pickFlockSize() : 1;
                            try {
                                final Pair<Ref<EntityStore>, NPCEntity> npcPair = NPCPlugin.get().spawnEntity(store, roleIndex, position, rotation, this.spawningContext.getModel(), (_npc, _ref, _store) -> postSpawn(_npc, _ref, this.spawnWrapper.getSpawn(), targetRef, _store));
                                final Ref<EntityStore> npcRef = npcPair.first();
                                final NPCEntity npcComponent = npcPair.second();
                                FlockPlugin.trySpawnFlock(npcRef, npcComponent, roleIndex, position, rotation, flockSize, flockDefinition, null, (_npc, _ref, _store) -> postSpawn(_npc, _ref, this.spawnWrapper.getSpawn(), targetRef, _store), store);
                                ++spawnedCount;
                            }
                            catch (final RuntimeException e) {
                                SpawnBeacon.LOGGER.at(Level.WARNING).log("Failed to create %s: %s", NPCPlugin.get().getName(roleIndex), e.getMessage());
                                this.markUnspawnable(ref, roleIndex, store);
                            }
                            finally {
                                this.spawningContext.releaseFull();
                            }
                        }
                    }
                }
            }
        }
        return spawnedCount != 0;
    }
    
    protected void markUnspawnable(final Ref<EntityStore> ref, final int index, final ComponentAccessor<EntityStore> componentAccessor) {
        this.unspawnableRoles.add(index);
        if (this.unspawnableRoles.size() >= this.spawnWrapper.getRoles().size()) {
            final UUIDComponent uuidComponent = componentAccessor.getComponent(ref, UUIDComponent.getComponentType());
            assert uuidComponent != null;
            SpawnBeacon.LOGGER.at(Level.WARNING).log("Removed spawn beacon %s due to being unable to spawn any NPC types", uuidComponent.getUuid());
            this.remove();
        }
    }
    
    protected static void postSpawn(@Nonnull final NPCEntity npc, @Nonnull final Ref<EntityStore> selfRef, @Nonnull final BeaconNPCSpawn spawn, final Ref<EntityStore> targetRef, final ComponentAccessor<EntityStore> componentAccessor) {
        final Role role = npc.getRole();
        role.getMarkedEntitySupport().setMarkedEntity(spawn.getTargetSlot(), targetRef);
        final String spawnState = spawn.getNpcSpawnState();
        if (spawnState != null) {
            role.getStateSupport().setState(selfRef, spawnState, spawn.getNpcSpawnSubState(), componentAccessor);
        }
        NewSpawnStartTickingSystem.queueNewSpawn(selfRef, selfRef.getStore());
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "SpawnBeacon{spawnConfigId='" + this.spawnConfigId + "'} " + super.toString();
    }
    
    static {
        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     2: invokevirtual   java/lang/Class.desiredAssertionStatus:()Z
        //     5: ifne            12
        //     8: iconst_1       
        //     9: goto            13
        //    12: iconst_0       
        //    13: putstatic       com/hypixel/hytale/server/spawning/beacons/SpawnBeacon.$assertionsDisabled:Z
        //    16: ldc             Lcom/hypixel/hytale/server/spawning/beacons/SpawnBeacon;.class
        //    18: invokedynamic   BootstrapMethod #3, get:()Ljava/util/function/Supplier;
        //    23: getstatic       com/hypixel/hytale/server/core/entity/Entity.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //    26: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;Lcom/hypixel/hytale/codec/builder/BuilderCodec;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    29: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    32: dup            
        //    33: ldc_w           "SpawnConfiguration"
        //    36: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
        //    39: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //    42: invokedynamic   BootstrapMethod #4, accept:()Ljava/util/function/BiConsumer;
        //    47: invokedynamic   BootstrapMethod #5, apply:()Ljava/util/function/Function;
        //    52: 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;
        //    55: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //    58: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    61: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //    64: putstatic       com/hypixel/hytale/server/spawning/beacons/SpawnBeacon.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //    67: return         
        //    StackMapTable: 00 02 0C 40 01
        // 
        // 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:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2483)
        //     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:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2483)
        //     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.runInference(TypeAnalysis.java:684)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypesForVariables(TypeAnalysis.java:593)
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:405)
        //     at com.strobel.decompiler.ast.TypeAnalysis.run(TypeAnalysis.java:95)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:109)
        //     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.");
    }
}
