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

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

import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.server.spawning.controllers.BeaconSpawnController;
import com.hypixel.hytale.server.spawning.beacons.LegacySpawnBeaconEntity;
import com.hypixel.hytale.server.npc.components.SpawnBeaconReference;
import com.hypixel.hytale.server.flock.StoredFlock;
import java.time.Duration;
import java.time.Instant;
import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker;
import java.time.temporal.TemporalAmount;
import com.hypixel.hytale.server.core.entity.reference.InvalidatablePersistentRef;
import com.hypixel.hytale.component.RemoveReason;
import java.util.UUID;
import com.hypixel.hytale.logger.HytaleLogger;
import java.util.logging.Level;
import com.hypixel.hytale.server.spawning.SpawningPlugin;
import com.hypixel.hytale.component.ComponentAccessor;
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 com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.modules.entity.component.WorldGenId;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.components.SpawnMarkerReference;
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 SpawnReferenceSystems
{
    public static class MarkerAddRemoveSystem extends RefSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, SpawnMarkerReference> spawnReferenceComponentType;
        @Nonnull
        private final ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType;
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, WorldGenId> worldGenIdComponentType;
        @Nonnull
        private final ComponentType<EntityStore, UUIDComponent> uuidComponentComponentType;
        @Nonnull
        private final ResourceType<EntityStore, WorldTimeResource> worldTimeResourceResourceType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public MarkerAddRemoveSystem(@Nonnull final ComponentType<EntityStore, SpawnMarkerReference> spawnReferenceComponentType, @Nonnull final ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType) {
            this.spawnReferenceComponentType = spawnReferenceComponentType;
            this.spawnMarkerEntityComponentType = spawnMarkerEntityComponentType;
            this.npcComponentType = NPCEntity.getComponentType();
            this.worldGenIdComponentType = WorldGenId.getComponentType();
            this.uuidComponentComponentType = UUIDComponent.getComponentType();
            this.worldTimeResourceResourceType = WorldTimeResource.getResourceType();
            this.query = (Query<EntityStore>)Archetype.of(spawnReferenceComponentType, this.npcComponentType, this.uuidComponentComponentType);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            switch (reason) {
                case LOAD: {
                    final SpawnMarkerReference spawnReferenceComponent = store.getComponent(ref, this.spawnReferenceComponentType);
                    assert spawnReferenceComponent != null;
                    final Ref<EntityStore> markerReference = spawnReferenceComponent.getReference().getEntity(store);
                    if (markerReference == null) {
                        return;
                    }
                    final SpawnMarkerEntity markerTypeComponent = store.getComponent(markerReference, this.spawnMarkerEntityComponentType);
                    assert markerTypeComponent != null;
                    final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
                    assert npcComponent != null;
                    spawnReferenceComponent.getReference().setEntity(markerReference, store);
                    spawnReferenceComponent.refreshTimeoutCounter();
                    markerTypeComponent.refreshTimeout();
                    final WorldGenId worldGenIdComponent = commandBuffer.getComponent(markerReference, this.worldGenIdComponentType);
                    final int worldGenId = (worldGenIdComponent != null) ? worldGenIdComponent.getWorldGenId() : 0;
                    commandBuffer.putComponent(markerReference, WorldGenId.getComponentType(), new WorldGenId(worldGenId));
                    final HytaleLogger.Api context = SpawningPlugin.get().getLogger().at(Level.FINE);
                    if (!context.isEnabled()) {
                        break;
                    }
                    final UUIDComponent uuidComponent = commandBuffer.getComponent(markerReference, this.uuidComponentComponentType);
                    assert uuidComponent != null;
                    final UUID uuid = uuidComponent.getUuid();
                    context.log("%s synced up with marker %s", npcComponent.getRoleName(), uuid);
                    break;
                }
            }
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            switch (reason) {
                case REMOVE: {
                    final SpawnMarkerReference spawnReferenceComponent = store.getComponent(ref, this.spawnReferenceComponentType);
                    if (spawnReferenceComponent == null) {
                        return;
                    }
                    final Ref<EntityStore> spawnMarkerRef = spawnReferenceComponent.getReference().getEntity(store);
                    if (spawnMarkerRef == null) {
                        return;
                    }
                    final SpawnMarkerEntity spawnMarkerComponent = store.getComponent(spawnMarkerRef, this.spawnMarkerEntityComponentType);
                    assert spawnMarkerComponent != null;
                    final UUIDComponent uuidComponent = store.getComponent(ref, this.uuidComponentComponentType);
                    assert uuidComponent != null;
                    final UUID uuid = uuidComponent.getUuid();
                    final int spawnCount = spawnMarkerComponent.decrementAndGetSpawnCount();
                    final SpawnMarker cachedMarker = spawnMarkerComponent.getCachedMarker();
                    if (spawnCount > 0 && cachedMarker.getDeactivationDistance() > 0.0) {
                        final InvalidatablePersistentRef[] newReferences = new InvalidatablePersistentRef[spawnCount];
                        int pos = 0;
                        final InvalidatablePersistentRef[] npcReferences2;
                        final InvalidatablePersistentRef[] npcReferences = npcReferences2 = spawnMarkerComponent.getNpcReferences();
                        for (final InvalidatablePersistentRef npcRef : npcReferences2) {
                            if (!npcRef.getUuid().equals(uuid)) {
                                newReferences[pos++] = npcRef;
                            }
                        }
                        spawnMarkerComponent.setNpcReferences(newReferences);
                    }
                    if (spawnCount <= 0 && !cachedMarker.isRealtimeRespawn()) {
                        Instant instant = store.getResource(this.worldTimeResourceResourceType).getGameTime();
                        final Duration gameTimeRespawn = spawnMarkerComponent.pollGameTimeRespawn();
                        if (gameTimeRespawn != null) {
                            instant = instant.plus((TemporalAmount)gameTimeRespawn);
                        }
                        spawnMarkerComponent.setSpawnAfter(instant);
                        spawnMarkerComponent.setNpcReferences(null);
                        final StoredFlock storedFlock = spawnMarkerComponent.getStoredFlock();
                        if (storedFlock != null) {
                            storedFlock.clear();
                        }
                    }
                    break;
                }
            }
        }
    }
    
    public static class BeaconAddRemoveSystem extends RefSystem<EntityStore>
    {
        @Nonnull
        private final ComponentType<EntityStore, SpawnBeaconReference> spawnReferenceComponentType;
        @Nonnull
        private final ComponentType<EntityStore, LegacySpawnBeaconEntity> legacySpawnBeaconComponent;
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public BeaconAddRemoveSystem(@Nonnull final ComponentType<EntityStore, SpawnBeaconReference> spawnReferenceComponentType, @Nonnull final ComponentType<EntityStore, LegacySpawnBeaconEntity> legacySpawnBeaconComponent) {
            this.spawnReferenceComponentType = spawnReferenceComponentType;
            this.legacySpawnBeaconComponent = legacySpawnBeaconComponent;
            this.npcComponentType = NPCEntity.getComponentType();
            this.query = (Query<EntityStore>)Archetype.of(spawnReferenceComponentType, this.npcComponentType);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            switch (reason) {
                case LOAD: {
                    final SpawnBeaconReference spawnReferenceComponent = store.getComponent(ref, this.spawnReferenceComponentType);
                    assert spawnReferenceComponent != null;
                    final Ref<EntityStore> markerReference = spawnReferenceComponent.getReference().getEntity(store);
                    if (markerReference == null) {
                        return;
                    }
                    final LegacySpawnBeaconEntity legacySpawnBeaconComponent = store.getComponent(markerReference, this.legacySpawnBeaconComponent);
                    assert legacySpawnBeaconComponent != null;
                    final NPCEntity npcComponent = store.getComponent(ref, this.npcComponentType);
                    assert npcComponent != null;
                    spawnReferenceComponent.getReference().setEntity(markerReference, store);
                    spawnReferenceComponent.refreshTimeoutCounter();
                    final BeaconSpawnController spawnController = legacySpawnBeaconComponent.getSpawnController();
                    if (!spawnController.hasSlots()) {
                        npcComponent.setToDespawn();
                        return;
                    }
                    spawnController.notifySpawnedEntityExists(markerReference, commandBuffer);
                    break;
                }
            }
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            switch (reason) {
                case REMOVE: {
                    final SpawnBeaconReference spawnReference = store.getComponent(ref, this.spawnReferenceComponentType);
                    if (spawnReference == null) {
                        return;
                    }
                    final Ref<EntityStore> spawnBeaconRef = spawnReference.getReference().getEntity(store);
                    if (spawnBeaconRef == null) {
                        return;
                    }
                    final LegacySpawnBeaconEntity legacySpawnBeaconComponent = store.getComponent(spawnBeaconRef, this.legacySpawnBeaconComponent);
                    if (legacySpawnBeaconComponent == null) {
                        return;
                    }
                    legacySpawnBeaconComponent.getSpawnController().notifyNPCRemoval(ref, store);
                    break;
                }
            }
        }
    }
    
    public static class TickingSpawnMarkerSystem extends EntityTickingSystem<EntityStore>
    {
        @Nonnull
        private static final Set<Dependency<EntityStore>> DEPENDENCIES;
        @Nonnull
        private final ComponentType<EntityStore, SpawnMarkerReference> spawnReferenceComponentType;
        @Nonnull
        private final ComponentType<EntityStore, SpawnMarkerEntity> markerTypeComponentType;
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcEntityComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public TickingSpawnMarkerSystem(@Nonnull final ComponentType<EntityStore, SpawnMarkerReference> spawnReferenceComponentType, @Nonnull final ComponentType<EntityStore, SpawnMarkerEntity> markerTypeComponentType) {
            this.spawnReferenceComponentType = spawnReferenceComponentType;
            this.markerTypeComponentType = markerTypeComponentType;
            this.npcEntityComponentType = NPCEntity.getComponentType();
            this.query = (Query<EntityStore>)Archetype.of(spawnReferenceComponentType, this.npcEntityComponentType);
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return TickingSpawnMarkerSystem.DEPENDENCIES;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = archetypeChunk.getComponent(index, this.npcEntityComponentType);
            assert npcComponent != null;
            if (npcComponent.isDespawning() || npcComponent.isPlayingDespawnAnim()) {
                return;
            }
            final SpawnMarkerReference spawnReferenceComponent = archetypeChunk.getComponent(index, this.spawnReferenceComponentType);
            assert spawnReferenceComponent != null;
            if (!spawnReferenceComponent.tickMarkerLostTimeoutCounter(dt)) {
                return;
            }
            final Ref<EntityStore> spawnMarkerRef = spawnReferenceComponent.getReference().getEntity(commandBuffer);
            if (spawnMarkerRef != null) {
                final SpawnMarkerEntity spawnMarkerComponent = commandBuffer.getComponent(spawnMarkerRef, this.markerTypeComponentType);
                assert spawnMarkerComponent != null;
                spawnReferenceComponent.refreshTimeoutCounter();
                spawnMarkerComponent.refreshTimeout();
            }
            else if (npcComponent.getRole().getStateSupport().isInBusyState()) {
                spawnReferenceComponent.refreshTimeoutCounter();
            }
            else {
                npcComponent.setToDespawn();
                final HytaleLogger.Api context = SpawningPlugin.get().getLogger().at(Level.WARNING);
                if (context.isEnabled()) {
                    context.log("NPCEntity despawning due to lost marker: %s", archetypeChunk.getReferenceTo(index));
                }
            }
        }
        
        static {
            DEPENDENCIES = Set.of(new SystemDependency(Order.AFTER, NPCPreTickSystem.class), new SystemDependency(Order.BEFORE, DeathSystems.CorpseRemoval.class));
        }
    }
    
    public static class TickingSpawnBeaconSystem extends EntityTickingSystem<EntityStore>
    {
        @Nonnull
        private static final Set<Dependency<EntityStore>> DEPENDENCIES;
        @Nonnull
        private final ComponentType<EntityStore, SpawnBeaconReference> spawnReferenceComponentType;
        @Nonnull
        private final ComponentType<EntityStore, NPCEntity> npcEntityComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public TickingSpawnBeaconSystem(@Nonnull final ComponentType<EntityStore, SpawnBeaconReference> spawnReferenceComponentType) {
            this.spawnReferenceComponentType = spawnReferenceComponentType;
            this.npcEntityComponentType = NPCEntity.getComponentType();
            this.query = (Query<EntityStore>)Archetype.of(spawnReferenceComponentType, this.npcEntityComponentType);
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return TickingSpawnBeaconSystem.DEPENDENCIES;
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return EntityTickingSystem.maybeUseParallel(archetypeChunkSize, taskCount);
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = archetypeChunk.getComponent(index, this.npcEntityComponentType);
            assert npcComponent != null;
            if (npcComponent.isDespawning() || npcComponent.isPlayingDespawnAnim()) {
                return;
            }
            final SpawnBeaconReference spawnReferenceComponent = archetypeChunk.getComponent(index, this.spawnReferenceComponentType);
            assert spawnReferenceComponent != null;
            if (!spawnReferenceComponent.tickMarkerLostTimeoutCounter(dt)) {
                return;
            }
            final Ref<EntityStore> spawnBeaconRef = spawnReferenceComponent.getReference().getEntity(commandBuffer);
            if (spawnBeaconRef != null) {
                spawnReferenceComponent.refreshTimeoutCounter();
            }
            else if (npcComponent.getRole().getStateSupport().isInBusyState()) {
                spawnReferenceComponent.refreshTimeoutCounter();
            }
            else {
                npcComponent.setToDespawn();
                final HytaleLogger.Api context = SpawningPlugin.get().getLogger().at(Level.WARNING);
                if (context.isEnabled()) {
                    context.log("NPCEntity despawning due to lost marker: %s", archetypeChunk.getReferenceTo(index));
                }
            }
        }
        
        static {
            DEPENDENCIES = Set.of(new SystemDependency(Order.AFTER, NPCPreTickSystem.class), new SystemDependency(Order.BEFORE, DeathSystems.CorpseRemoval.class));
        }
    }
}
