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

package com.hypixel.hytale.server.npc.systems;

import com.hypixel.hytale.server.npc.components.messaging.EventSupport;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.server.npc.components.FailedSpawnComponent;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.common.thread.ticking.Tickable;
import it.unimi.dsi.fastutil.ints.IntSet;
import com.hypixel.hytale.server.npc.asset.builder.EventSlotMapper;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.npc.role.SpawnEffect;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab;
import com.hypixel.hytale.server.core.modules.entity.component.ActiveAnimationComponent;
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
import com.hypixel.hytale.server.npc.valuestore.ValueStore;
import com.hypixel.hytale.server.npc.decisionmaker.stateevaluator.StateEvaluator;
import com.hypixel.hytale.server.npc.components.Timers;
import com.hypixel.hytale.server.npc.blackboard.view.event.entity.EntityEventType;
import com.hypixel.hytale.server.npc.components.messaging.NPCEntityEventSupport;
import com.hypixel.hytale.server.npc.components.messaging.PlayerEntityEventSupport;
import com.hypixel.hytale.server.npc.blackboard.view.event.block.BlockEventType;
import com.hypixel.hytale.server.npc.components.messaging.NPCBlockEventSupport;
import com.hypixel.hytale.server.npc.components.messaging.PlayerBlockEventSupport;
import com.hypixel.hytale.server.npc.components.messaging.BeaconSupport;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.server.core.modules.interaction.Interactions;
import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.modules.entity.component.Invulnerable;
import com.hypixel.hytale.logger.sentry.SkipSentryException;
import com.hypixel.hytale.server.npc.role.support.RoleStats;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
import java.util.logging.Level;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.server.core.modules.physics.systems.PhysicsValuesAddSystem;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsSystems;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.HolderSystem;

public class RoleBuilderSystem extends HolderSystem<EntityStore>
{
    @Nonnull
    private final ComponentType<EntityStore, NPCEntity> npcComponentType;
    @Nonnull
    private final ComponentType<EntityStore, TransformComponent> transformComponentType;
    @Nonnull
    private final ComponentType<EntityStore, ModelComponent> modelComponentType;
    @Nonnull
    private final ComponentType<EntityStore, PersistentModel> persistentModelComponentType;
    @Nonnull
    private final Set<Dependency<EntityStore>> dependencies;
    @Nonnull
    private final Query<EntityStore> query;
    
    public RoleBuilderSystem() {
        this.transformComponentType = TransformComponent.getComponentType();
        this.modelComponentType = ModelComponent.getComponentType();
        this.persistentModelComponentType = PersistentModel.getComponentType();
        this.npcComponentType = NPCEntity.getComponentType();
        this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, EntityStatsSystems.Setup.class), new SystemDependency(Order.AFTER, PhysicsValuesAddSystem.class));
        this.query = (Query<EntityStore>)Archetype.of(this.npcComponentType, this.transformComponentType);
    }
    
    @Nonnull
    @Override
    public Set<Dependency<EntityStore>> getDependencies() {
        return this.dependencies;
    }
    
    @Nonnull
    @Override
    public Query<EntityStore> getQuery() {
        return this.query;
    }
    
    @Override
    public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
        final NPCEntity npcComponent = holder.getComponent(this.npcComponentType);
        assert npcComponent != null;
        if (npcComponent.getRole() != null) {
            return;
        }
        final NPCPlugin npcPlugin = NPCPlugin.get();
        int roleIndex = npcComponent.getRoleIndex();
        if (roleIndex == Integer.MIN_VALUE) {
            final String roleName = npcComponent.getRoleName();
            roleIndex = npcPlugin.getIndex(roleName);
            if (roleIndex < 0) {
                this.fail(holder);
                npcPlugin.getLogger().at(Level.SEVERE).log("Reloading nonexistent role %s!", roleName);
                return;
            }
            if (npcPlugin.tryGetCachedValidRole(roleIndex) == null) {
                this.fail(holder);
                npcPlugin.getLogger().at(Level.SEVERE).log("Reloading invalid role %s!", roleName);
                return;
            }
            npcComponent.setRoleIndex(roleIndex);
        }
        final BuilderInfo builderInfo = npcPlugin.prepareRoleBuilderInfo(roleIndex);
        final Builder<Role> roleBuilder = (Builder<Role>)builderInfo.getBuilder();
        if (!roleBuilder.isSpawnable()) {
            this.fail(holder);
            npcPlugin.getLogger().at(Level.SEVERE).log("Attempting to spawn un-spawnable (abstract) role: %s", npcComponent.getRoleName());
            return;
        }
        final BuilderSupport builderSupport = new BuilderSupport(npcPlugin.getBuilderManager(), npcComponent, holder, new ExecutionContext(), roleBuilder, null);
        Role role;
        try {
            role = NPCPlugin.buildRole(roleBuilder, builderInfo, builderSupport, roleIndex);
        }
        catch (final SkipSentryException e) {
            this.fail(holder);
            npcPlugin.getLogger().at(Level.SEVERE).log("Error: %s for NPC %s", e.getMessage(), npcComponent.getRole());
            return;
        }
        npcComponent.setRole(role);
        if (role.isInvulnerable()) {
            holder.ensureComponent(Invulnerable.getComponentType());
        }
        final Message roleNameMessage = Message.translation(role.getNameTranslationKey());
        holder.putComponent(DisplayNameComponent.getComponentType(), new DisplayNameComponent(roleNameMessage));
        final Interactions interactionsComponent = holder.ensureAndGetComponent(Interactions.getComponentType());
        interactionsComponent.setInteractionId(InteractionType.Use, "*UseNPC");
        if (role.getDeathInteraction() != null) {
            interactionsComponent.setInteractionId(InteractionType.Death, role.getDeathInteraction());
        }
        final Object2IntMap<String> beaconSlotMappings = builderSupport.getBeaconSlotMappings();
        if (beaconSlotMappings != null) {
            final BeaconSupport beaconSupport = new BeaconSupport();
            beaconSupport.initialise(beaconSlotMappings);
            holder.putComponent(BeaconSupport.getComponentType(), beaconSupport);
        }
        if (builderSupport.hasBlockEventSupport()) {
            final EventSlotMapper<BlockEventType> playerEventSlotMapper = builderSupport.getPlayerBlockEventSlotMapper();
            if (playerEventSlotMapper != null) {
                final PlayerBlockEventSupport playerBlockEventSupport = new PlayerBlockEventSupport();
                ((EventSupport<BlockEventType, NotificationType>)playerBlockEventSupport).initialise(playerEventSlotMapper.getEventSlotMappings(), playerEventSlotMapper.getEventSlotRanges(), playerEventSlotMapper.getEventSlotCount());
                holder.putComponent(PlayerBlockEventSupport.getComponentType(), playerBlockEventSupport);
            }
            final EventSlotMapper<BlockEventType> npcEventSlotMapper = builderSupport.getNPCBlockEventSlotMapper();
            if (npcEventSlotMapper != null) {
                final NPCBlockEventSupport npcBlockEventSupport = new NPCBlockEventSupport();
                ((EventSupport<BlockEventType, NotificationType>)npcBlockEventSupport).initialise(npcEventSlotMapper.getEventSlotMappings(), npcEventSlotMapper.getEventSlotRanges(), npcEventSlotMapper.getEventSlotCount());
                holder.putComponent(NPCBlockEventSupport.getComponentType(), npcBlockEventSupport);
            }
            for (int i = 0; i < BlockEventType.VALUES.length; ++i) {
                final BlockEventType type = BlockEventType.VALUES[i];
                final IntSet sets = builderSupport.getBlockChangeSets(type);
                if (sets != null) {
                    npcComponent.addBlackboardBlockChangeSets(type, sets);
                }
            }
        }
        if (builderSupport.hasEntityEventSupport()) {
            final EventSlotMapper<EntityEventType> playerEventSlotMapper2 = builderSupport.getPlayerEntityEventSlotMapper();
            if (playerEventSlotMapper2 != null) {
                final PlayerEntityEventSupport playerEntityEventSupport = new PlayerEntityEventSupport();
                ((EventSupport<EntityEventType, NotificationType>)playerEntityEventSupport).initialise(playerEventSlotMapper2.getEventSlotMappings(), playerEventSlotMapper2.getEventSlotRanges(), playerEventSlotMapper2.getEventSlotCount());
                holder.putComponent(PlayerEntityEventSupport.getComponentType(), playerEntityEventSupport);
            }
            final EventSlotMapper<EntityEventType> npcEventSlotMapper2 = builderSupport.getNPCEntityEventSlotMapper();
            if (npcEventSlotMapper2 != null) {
                final NPCEntityEventSupport npcEntityEventSupport = new NPCEntityEventSupport();
                ((EventSupport<EntityEventType, NotificationType>)npcEntityEventSupport).initialise(npcEventSlotMapper2.getEventSlotMappings(), npcEventSlotMapper2.getEventSlotRanges(), npcEventSlotMapper2.getEventSlotCount());
                holder.putComponent(NPCEntityEventSupport.getComponentType(), npcEntityEventSupport);
            }
            for (final EntityEventType type2 : EntityEventType.VALUES) {
                final IntSet sets2 = builderSupport.getEventNPCGroups(type2);
                if (sets2 != null) {
                    npcComponent.addBlackboardEntityEventSets(type2, sets2);
                }
            }
        }
        final Tickable[] timers = builderSupport.allocateTimers();
        if (timers != null) {
            holder.putComponent(Timers.getComponentType(), new Timers(timers));
        }
        final StateEvaluator stateEvaluator = builderSupport.getStateEvaluator();
        if (stateEvaluator != null) {
            holder.putComponent(StateEvaluator.getComponentType(), stateEvaluator);
        }
        final ValueStore.Builder valueStoreBuilder = builderSupport.getValueStoreBuilder();
        if (valueStoreBuilder != null) {
            holder.putComponent(ValueStore.getComponentType(), valueStoreBuilder.build());
        }
        holder.ensureComponent(EffectControllerComponent.getComponentType());
        holder.ensureComponent(ActiveAnimationComponent.getComponentType());
        final boolean fromPrefab = holder.getArchetype().contains(FromPrefab.getComponentType());
        final boolean spawnedOrPrefab = reason.equals(AddReason.SPAWN) || fromPrefab;
        if (spawnedOrPrefab) {
            final ModelComponent modelComponent = holder.getComponent(this.modelComponentType);
            if (modelComponent == null) {
                final String appearance = role.getAppearanceName();
                if (appearance == null || appearance.isEmpty()) {
                    this.fail(holder);
                    npcPlugin.getLogger().at(Level.SEVERE).log("Appearance can't be initially empty for role %s", npcComponent.getRoleName());
                    return;
                }
                final ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(appearance);
                if (modelAsset == null) {
                    this.fail(holder);
                    npcPlugin.getLogger().at(Level.SEVERE).log("Model asset not found: %s for role %s", appearance, npcComponent.getRoleName());
                    return;
                }
                final float scale = modelAsset.generateRandomScale();
                npcComponent.setInitialModelScale(scale);
                final Model scaledModel = Model.createScaledModel(modelAsset, scale);
                holder.putComponent(this.persistentModelComponentType, new PersistentModel(scaledModel.toReference()));
                holder.putComponent(this.modelComponentType, new ModelComponent(scaledModel));
            }
            role.spawned(holder, npcComponent);
            if (roleBuilder instanceof final SpawnEffect spawnEffect) {
                final TransformComponent transformComponent = holder.getComponent(this.transformComponentType);
                assert transformComponent != null;
                spawnEffect.spawnEffect(transformComponent.getPosition(), transformComponent.getRotation(), store);
            }
        }
    }
    
    @Override
    public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
    }
    
    private void fail(@Nonnull final Holder<EntityStore> holder) {
        final Archetype<EntityStore> archetype = holder.getArchetype();
        if (archetype == null) {
            return;
        }
        for (int i = archetype.getMinIndex(); i < archetype.length(); ++i) {
            final ComponentType<EntityStore, ? extends Component<EntityStore>> componentType = (ComponentType<EntityStore, ? extends Component<EntityStore>>)archetype.get(i);
            if (componentType != null) {
                holder.removeComponent(componentType);
            }
        }
        holder.ensureComponent(FailedSpawnComponent.getComponentType());
    }
}
