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

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

import java.util.UUID;
import com.hypixel.hytale.server.flock.FlockPlugin;
import com.hypixel.hytale.server.flock.FlockMembership;
import com.hypixel.hytale.server.core.prefab.event.PrefabPlaceEntityEvent;
import com.hypixel.hytale.component.system.WorldEventSystem;
import com.hypixel.hytale.component.system.EcsEvent;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.entity.damage.event.KillFeedEvent;
import com.hypixel.hytale.component.system.EntityEventSystem;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.modules.entity.system.ModelSystems;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.server.core.modules.entity.damage.DeferredCorpseRemoval;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.modules.entity.teleport.TeleportSystems;
import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport;
import com.hypixel.hytale.component.system.RefChangeSystem;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.server.core.modules.entity.component.WorldGenId;
import com.hypixel.hytale.component.system.HolderSystem;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.server.core.entity.Frozen;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab;
import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect;
import com.hypixel.hytale.server.npc.config.balancing.BalanceAsset;
import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.npc.blackboard.view.blocktype.BlockTypeView;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.core.modules.entity.component.NewSpawnComponent;
import com.hypixel.hytale.server.core.modules.entity.component.MovementAudioComponent;
import com.hypixel.hytale.server.core.modules.entity.component.PositionDataComponent;
import com.hypixel.hytale.server.core.prefab.PrefabCopyableComponent;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Ref;
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.RefSystem;

public class NPCSystems
{
    public static class AddedSystem extends RefSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        
        public AddedSystem(@Nonnull final ComponentType<EntityStore, NPCEntity> npcComponentType) {
            this.npcComponentType = npcComponentType;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            final Role role = npcComponent.getRole();
            if (role == null) {
                NPCPlugin.get().getLogger().atSevere().withCause(new IllegalStateException("NPC has no role or role index in onLoad!")).log();
                commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
                return;
            }
            npcComponent.initBlockChangeBlackboardView(ref, commandBuffer);
            role.loaded();
            commandBuffer.ensureComponent(ref, PrefabCopyableComponent.getComponentType());
            commandBuffer.ensureComponent(ref, PositionDataComponent.getComponentType());
            commandBuffer.ensureComponent(ref, MovementAudioComponent.getComponentType());
            if (reason == AddReason.SPAWN) {
                final NewSpawnComponent newSpawnComponent = new NewSpawnComponent(role.getSpawnLockTime());
                commandBuffer.addComponent(ref, NewSpawnComponent.getComponentType(), newSpawnComponent);
            }
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            final BlockTypeView blockTypeView = npcComponent.removeBlockTypeBlackboardView();
            if (blockTypeView != null) {
                blockTypeView.removeSearchedBlockSets(ref, npcComponent, npcComponent.getBlackboardBlockTypeSets());
            }
            switch (reason) {
                case REMOVE: {
                    npcComponent.getRole().removed();
                    break;
                }
                case UNLOAD: {
                    npcComponent.getRole().unloaded();
                    break;
                }
            }
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.npcComponentType;
        }
    }
    
    public static class AddSpawnEntityEffectSystem extends RefSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        
        public AddSpawnEntityEffectSystem(@Nonnull final ComponentType<EntityStore, NPCEntity> npcComponentType) {
            this.npcComponentType = npcComponentType;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            final EffectControllerComponent effectController = store.getComponent(ref, EffectControllerComponent.getComponentType());
            assert effectController != null;
            final Role role = npcComponent.getRole();
            if (role == null) {
                NPCPlugin.get().getLogger().atSevere().withCause(new IllegalStateException("NPC has no role or role index in onLoad!")).log();
                commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
                return;
            }
            final String balanceAssetId = role.getBalanceAsset();
            if (balanceAssetId == null) {
                return;
            }
            final BalanceAsset balanceAsset = BalanceAsset.getAssetMap().getAsset(balanceAssetId);
            final String entityEffectId = balanceAsset.getEntityEffect();
            if (entityEffectId == null) {
                return;
            }
            final EntityEffect entityEffect = EntityEffect.getAssetMap().getAsset(entityEffectId);
            effectController.addEffect(ref, entityEffect, commandBuffer);
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return (Query<EntityStore>)Query.and(this.npcComponentType, EffectControllerComponent.getComponentType());
        }
    }
    
    public static class AddedFromExternalSystem extends RefSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, TransformComponent> transformComponentType;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        @Nonnull
        private final Query<EntityStore> query;
        
        public AddedFromExternalSystem(@Nonnull final ComponentType<EntityStore, NPCEntity> npcComponentType) {
            this.npcComponentType = npcComponentType;
            this.transformComponentType = TransformComponent.getComponentType();
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, AddedSystem.class));
            this.query = (Query<EntityStore>)Query.and(npcComponentType, this.transformComponentType, Query.or(FromWorldGen.getComponentType(), FromPrefab.getComponentType()));
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final WorldTimeResource worldTimeResource = commandBuffer.getResource(WorldTimeResource.getResourceType());
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            final Archetype<EntityStore> archetype = store.getArchetype(ref);
            final boolean fromWorldGen = archetype.contains(FromWorldGen.getComponentType());
            final TransformComponent transformComponent = store.getComponent(ref, this.transformComponentType);
            assert transformComponent != null;
            npcComponent.getLeashPoint().assign(transformComponent.getPosition());
            final Vector3f bodyRotation = transformComponent.getRotation();
            npcComponent.setLeashHeading(bodyRotation.getYaw());
            npcComponent.setLeashPitch(bodyRotation.getPitch());
            npcComponent.setSpawnInstant(worldTimeResource.getGameTime());
            npcComponent.getRole().onLoadFromWorldGenOrPrefab(ref, npcComponent, commandBuffer);
            if (fromWorldGen) {
                commandBuffer.tryRemoveComponent(ref, Frozen.getComponentType());
            }
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityModule.get().getPreClearMarkersGroup();
        }
    }
    
    public static class AddedFromWorldGenSystem extends HolderSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, WorldGenId> worldGenIdComponentType;
        @Nonnull
        private final ComponentType<EntityStore, FromWorldGen> fromWorldGenComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public AddedFromWorldGenSystem() {
            this.npcComponentType = NPCEntity.getComponentType();
            this.worldGenIdComponentType = WorldGenId.getComponentType();
            this.fromWorldGenComponentType = FromWorldGen.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.npcComponentType, this.fromWorldGenComponentType);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityModule.get().getPreClearMarkersGroup();
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            final FromWorldGen fromWorldGenComponent = holder.getComponent(this.fromWorldGenComponentType);
            assert fromWorldGenComponent != null;
            holder.putComponent(this.worldGenIdComponentType, new WorldGenId(fromWorldGenComponent.getWorldGenId()));
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
    }
    
    public static class OnTeleportSystem extends RefChangeSystem<EntityStore, Teleport>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, Teleport> teleportComponentType;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        
        public OnTeleportSystem() {
            this.npcComponentType = NPCEntity.getComponentType();
            this.teleportComponentType = Teleport.getComponentType();
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, TeleportSystems.MoveSystem.class));
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.npcComponentType;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, Teleport> componentType() {
            return this.teleportComponentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final Teleport component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = commandBuffer.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            final World world = store.getExternalData().getWorld();
            final World worldTo = component.getWorld();
            final Role role = npcComponent.getRole();
            assert role != null;
            role.teleported(world, (worldTo == null) ? world : worldTo);
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final Teleport oldComponent, @Nonnull final Teleport newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final Teleport component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
    }
    
    public static class OnDeathSystem extends DeathSystems.OnDeathSystem
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, DeferredCorpseRemoval> deferredCorpseRemovalComponentType;
        
        public OnDeathSystem() {
            this.npcComponentType = NPCEntity.getComponentType();
            this.deferredCorpseRemovalComponentType = DeferredCorpseRemoval.getComponentType();
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.npcComponentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final DeathComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = commandBuffer.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            final Role role = npcComponent.getRole();
            final double deathAnimationTime = role.getDeathAnimationTime();
            if (deathAnimationTime > 0.0) {
                commandBuffer.addComponent(ref, this.deferredCorpseRemovalComponentType, new DeferredCorpseRemoval(deathAnimationTime));
            }
        }
    }
    
    public static class ModelChangeSystem extends RefChangeSystem<EntityStore, ModelComponent>
    {
        @Nonnull
        private final ComponentType<EntityStore, ModelComponent> modelComponentType;
        @Nullable
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        
        public ModelChangeSystem() {
            this.modelComponentType = ModelComponent.getComponentType();
            this.npcComponentType = NPCEntity.getComponentType();
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, ModelSystems.UpdateBoundingBox.class));
        }
        
        @Nullable
        @Override
        public Query<EntityStore> getQuery() {
            return this.npcComponentType;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Nonnull
        @Override
        public ComponentType<EntityStore, ModelComponent> componentType() {
            return this.modelComponentType;
        }
        
        @Override
        public void onComponentAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final ModelComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Model model = component.getModel();
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            npcComponent.getRole().updateMotionControllers(ref, model, model.getBoundingBox(), commandBuffer);
        }
        
        @Override
        public void onComponentSet(@Nonnull final Ref<EntityStore> ref, final ModelComponent oldComponent, @Nonnull final ModelComponent newComponent, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Model model = newComponent.getModel();
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            npcComponent.getRole().updateMotionControllers(ref, model, model.getBoundingBox(), commandBuffer);
        }
        
        @Override
        public void onComponentRemoved(@Nonnull final Ref<EntityStore> ref, @Nonnull final ModelComponent component, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
            assert npcComponent != null;
            npcComponent.getRole().updateMotionControllers(ref, null, null, commandBuffer);
        }
    }
    
    @Deprecated(forRemoval = true)
    public static class LegacyWorldGenId extends HolderSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, WorldGenId> worldGenIdComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public LegacyWorldGenId() {
            this.npcComponentType = NPCEntity.getComponentType();
            this.worldGenIdComponentType = WorldGenId.getComponentType();
            this.query = (Query<EntityStore>)Query.and(this.npcComponentType, Query.not((Query<Object>)this.worldGenIdComponentType), Query.not((Query<Object>)FromWorldGen.getComponentType()));
        }
        
        @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;
            final int worldGenId = npcComponent.getLegacyWorldgenId();
            if (worldGenId != 0) {
                holder.addComponent(this.worldGenIdComponentType, new WorldGenId(worldGenId));
            }
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
    }
    
    public static class KillFeedKillerEventSystem extends EntityEventSystem<EntityStore, KillFeedEvent.KillerMessage>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, Player> playerComponentType;
        
        public KillFeedKillerEventSystem() {
            super(KillFeedEvent.KillerMessage.class);
            this.npcComponentType = NPCEntity.getComponentType();
            this.playerComponentType = Player.getComponentType();
        }
        
        @Override
        public void handle(final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final KillFeedEvent.KillerMessage event) {
            final Ref<EntityStore> targetRef = event.getTargetRef();
            if (!targetRef.isValid()) {
                return;
            }
            final Player playerComponent = store.getComponent(targetRef, this.playerComponentType);
            if (playerComponent == null) {
                event.setCancelled(true);
                return;
            }
            final DisplayNameComponent displayNameComponent = archetypeChunk.getComponent(index, DisplayNameComponent.getComponentType());
            Message displayName;
            if (displayNameComponent != null) {
                displayName = displayNameComponent.getDisplayName();
            }
            else {
                final NPCEntity npcComponent = archetypeChunk.getComponent(index, this.npcComponentType);
                assert npcComponent != null;
                displayName = Message.raw(npcComponent.getRoleName());
            }
            event.setMessage(displayName);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.npcComponentType;
        }
    }
    
    public static class KillFeedDecedentEventSystem extends EntityEventSystem<EntityStore, KillFeedEvent.DecedentMessage>
    {
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        
        public KillFeedDecedentEventSystem() {
            super(KillFeedEvent.DecedentMessage.class);
            this.npcComponentType = NPCEntity.getComponentType();
        }
        
        @Override
        public void handle(final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final KillFeedEvent.DecedentMessage event) {
            event.setCancelled(true);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.npcComponentType;
        }
    }
    
    public static class PrefabPlaceEntityEventSystem extends WorldEventSystem<EntityStore, PrefabPlaceEntityEvent>
    {
        public PrefabPlaceEntityEventSystem() {
            super(PrefabPlaceEntityEvent.class);
        }
        
        @Override
        public void handle(@Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final PrefabPlaceEntityEvent event) {
            final Holder<EntityStore> holder = event.getHolder();
            final FlockMembership flockMembershipComponent = holder.getComponent(FlockMembership.getComponentType());
            if (flockMembershipComponent == null) {
                return;
            }
            final UUID flockId = FlockPlugin.get().getPrefabRemappedFlockReference(event.getPrefabId(), flockMembershipComponent.getFlockId());
            flockMembershipComponent.setFlockId(flockId);
            final NPCEntity npcComponent = holder.getComponent(NPCEntity.getComponentType());
            assert npcComponent != null;
            npcComponent.markNeedsSave();
        }
    }
}
