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

package com.hypixel.hytale.server.spawning.suppression.system;

import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.server.core.prefab.PrefabCopyableComponent;
import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen;
import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab;
import com.hypixel.hytale.component.AddReason;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.spawning.suppression.component.SpawnSuppressionComponent;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ArchetypeChunk;
import it.unimi.dsi.fastutil.longs.LongIterator;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.universe.world.World;
import java.util.Set;
import java.util.Iterator;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.assetstore.map.IndexedAssetMap;
import java.util.Map;
import com.hypixel.hytale.assetstore.event.RemovedAssetsEvent;
import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent;
import com.hypixel.hytale.event.IEventRegistry;
import com.hypixel.hytale.function.consumer.BooleanConsumer;
import java.util.concurrent.CopyOnWriteArrayList;
import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.component.system.StoreSystem;
import com.hypixel.hytale.logger.HytaleLogger;
import it.unimi.dsi.fastutil.objects.ObjectList;
import com.hypixel.fastutil.longs.Long2ObjectConcurrentHashMap;
import com.hypixel.hytale.math.vector.Vector3d;
import it.unimi.dsi.fastutil.ints.IntSet;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import java.util.List;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.spatial.SpatialResource;
import java.util.Comparator;
import java.util.Collection;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.server.spawning.suppression.component.ChunkSuppressionEntry;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.util.MathUtil;
import it.unimi.dsi.fastutil.ints.IntCollection;
import com.hypixel.hytale.builtin.tagset.TagSetPlugin;
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import com.hypixel.hytale.server.spawning.assets.spawnsuppression.SpawnSuppression;
import java.util.logging.Level;
import com.hypixel.hytale.server.spawning.SpawningPlugin;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.spawning.suppression.component.SpawnSuppressionController;
import com.hypixel.hytale.server.spawning.suppression.SpawnSuppressorEntry;
import java.util.UUID;
import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentType;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.spawning.suppression.component.ChunkSuppressionQueue;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.ResourceType;

public class SpawnSuppressionSystems
{
    private static void suppressSpawns(@Nonnull final ResourceType<ChunkStore, ChunkSuppressionQueue> chunkSuppressionQueueResourceType, @Nonnull final ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType, final UUID uuid, @Nonnull final SpawnSuppressorEntry entry, @Nonnull final SpawnSuppressionController suppressionController, @Nonnull final Store<EntityStore> store, @Nonnull final ChunkStore chunkComponentStore) {
        final String suppressionId = entry.getSuppressionId();
        SpawningPlugin.get().getLogger().at(Level.FINEST).log("Suppressing spawns with id '%s' from suppressor %s", suppressionId, uuid);
        final SpawnSuppression suppression = SpawnSuppression.getAssetMap().getAsset(suppressionId);
        if (suppression == null) {
            SpawningPlugin.get().getLogger().at(Level.WARNING).log("Spawn suppression config '%s' does not exist", suppressionId);
            return;
        }
        final int[] suppressedGroups = suppression.getSuppressedGroupIds();
        IntSet suppressedRoles;
        if (suppressedGroups != null && suppressedGroups.length > 0) {
            suppressedRoles = new IntOpenHashSet();
            for (final int suppressedGroup : suppressedGroups) {
                final IntSet set = TagSetPlugin.get(NPCGroup.class).getSet(suppressedGroup);
                if (set != null) {
                    suppressedRoles.addAll(set);
                }
            }
        }
        else {
            suppressedRoles = null;
        }
        final double radius = suppression.getRadius();
        final Vector3d position = entry.getPosition();
        final int minChunkX = MathUtil.floor(position.x - radius) >> 5;
        final int minChunkZ = MathUtil.floor(position.z - radius) >> 5;
        final int maxChunkX = MathUtil.floor(position.x + radius) >> 5;
        final int maxChunkZ = MathUtil.floor(position.z + radius) >> 5;
        final int minY = MathUtil.floor(position.y - radius);
        final int maxY = MathUtil.floor(position.y + radius);
        final Long2ObjectConcurrentHashMap<ChunkSuppressionEntry> chunkSuppressionMap = suppressionController.getChunkSuppressionMap();
        for (int chunkX = minChunkX; chunkX <= maxChunkX; ++chunkX) {
            for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; ++chunkZ) {
                final long chunkIndex = ChunkUtil.indexChunk(chunkX, chunkZ);
                SpawningPlugin.get().getLogger().at(Level.FINEST).log("Suppressing chunk index %s with id '%s'", chunkIndex, suppressionId);
                final ChunkSuppressionEntry oldEntry = chunkSuppressionMap.get(chunkIndex);
                List<ChunkSuppressionEntry.SuppressionSpan> suppressionSpanList;
                if (oldEntry != null) {
                    suppressionSpanList = new ObjectArrayList<ChunkSuppressionEntry.SuppressionSpan>(oldEntry.getSuppressionSpans());
                }
                else {
                    suppressionSpanList = new ObjectArrayList<ChunkSuppressionEntry.SuppressionSpan>();
                }
                suppressionSpanList.add(new ChunkSuppressionEntry.SuppressionSpan(uuid, minY, maxY, suppressedRoles));
                suppressionSpanList.sort(Comparator.comparingInt(ChunkSuppressionEntry.SuppressionSpan::getMinY));
                final ChunkSuppressionEntry chunkEntry = new ChunkSuppressionEntry(suppressionSpanList);
                chunkSuppressionMap.put(chunkIndex, chunkEntry);
                final Ref<ChunkStore> chunkReference = chunkComponentStore.getChunkReference(chunkIndex);
                if (chunkReference != null) {
                    final ChunkSuppressionQueue chunkSuppressionQueue = chunkComponentStore.getStore().getResource(chunkSuppressionQueueResourceType);
                    chunkSuppressionQueue.queueForAdd(chunkReference, chunkEntry);
                    SpawningPlugin.get().getLogger().at(Level.FINEST).log("Queueing annotation of chunk index %s with id '%s'", chunkIndex, suppressionId);
                }
            }
        }
        if (!suppression.isSuppressSpawnMarkers()) {
            return;
        }
        final ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
        final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(SpawningPlugin.get().getSpawnMarkerSpatialResource());
        spatialResource.getSpatialStructure().collect(position, radius, results);
        for (int i = 0; i < results.size(); ++i) {
            final Ref<EntityStore> markerRef = results.get(i);
            final SpawnMarkerEntity marker = store.getComponent(markerRef, spawnMarkerEntityComponentType);
            marker.suppress(uuid);
            final HytaleLogger.Api context = SpawningPlugin.get().getLogger().at(Level.FINEST);
            if (context.isEnabled()) {
                context.log("Suppressing spawn marker %s", store.getComponent(markerRef, UUIDComponent.getComponentType()).getUuid());
            }
        }
    }
    
    public static class Load extends StoreSystem<EntityStore>
    {
        private final ResourceType<EntityStore, SpawnSuppressionController> spawnSuppressionControllerResourceType;
        private final ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType;
        private final ResourceType<ChunkStore, ChunkSuppressionQueue> chunkSuppressionQueueResourceType;
        private final ComponentType<ChunkStore, ChunkSuppressionEntry> chunkSuppressionEntryComponentType;
        @Nonnull
        private final EventRegistry eventRegistry;
        
        public Load(final ResourceType<EntityStore, SpawnSuppressionController> spawnSuppressionControllerResourceType, final ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType, final ResourceType<ChunkStore, ChunkSuppressionQueue> chunkSuppressionQueueResourceType, final ComponentType<ChunkStore, ChunkSuppressionEntry> chunkSuppressionEntryComponentType) {
            this.spawnSuppressionControllerResourceType = spawnSuppressionControllerResourceType;
            this.spawnMarkerEntityComponentType = spawnMarkerEntityComponentType;
            this.chunkSuppressionQueueResourceType = chunkSuppressionQueueResourceType;
            this.chunkSuppressionEntryComponentType = chunkSuppressionEntryComponentType;
            (this.eventRegistry = new EventRegistry(new CopyOnWriteArrayList<BooleanConsumer>(), () -> true, null, SpawningPlugin.get().getEventRegistry())).register(LoadedAssetsEvent.class, SpawnSuppression.class, this::onSpawnSuppressionsLoaded);
            this.eventRegistry.register(RemovedAssetsEvent.class, SpawnSuppression.class, this::onSpawnSuppressionsRemoved);
        }
        
        @Override
        public void onSystemAddedToStore(@Nonnull final Store<EntityStore> store) {
            final SpawnSuppressionController resource = store.getResource(this.spawnSuppressionControllerResourceType);
            final Map<UUID, SpawnSuppressorEntry> spawnSuppressorMap = resource.getSpawnSuppressorMap();
            spawnSuppressorMap.forEach((id, entry) -> SpawnSuppressionSystems.suppressSpawns(this.chunkSuppressionQueueResourceType, this.spawnMarkerEntityComponentType, id, entry, resource, store, store.getExternalData().getWorld().getChunkStore()));
        }
        
        @Override
        public void onSystemRemovedFromStore(@Nonnull final Store<EntityStore> store) {
        }
        
        @Override
        public void onSystemUnregistered() {
            this.eventRegistry.shutdown();
        }
        
        private void onSpawnSuppressionsLoaded(@Nonnull final LoadedAssetsEvent<String, SpawnSuppression, IndexedAssetMap<String, SpawnSuppression>> event) {
            final Map<String, SpawnSuppression> loadedAssets = event.getLoadedAssets();
            Universe.get().getWorlds().forEach((name, world) -> world.execute(() -> {
                boolean hasChanges = false;
                final Store<EntityStore> store = world.getEntityStore().getStore();
                final SpawnSuppressionController suppressionController = store.getResource(this.spawnSuppressionControllerResourceType);
                final Map<UUID, SpawnSuppressorEntry> spawnSuppressorMap = suppressionController.getSpawnSuppressorMap();
                for (final SpawnSuppressorEntry entry : spawnSuppressorMap.values()) {
                    if (loadedAssets.containsKey(entry.getSuppressionId())) {
                        hasChanges = true;
                        break;
                    }
                }
                if (!(!hasChanges)) {
                    this.rebuildSuppressionMap(world, store, suppressionController);
                }
            }));
        }
        
        private void onSpawnSuppressionsRemoved(@Nonnull final RemovedAssetsEvent<String, SpawnSuppression, IndexedAssetMap<String, SpawnSuppression>> event) {
            final Set<String> removedAssets = event.getRemovedAssets();
            Universe.get().getWorlds().forEach((name, world) -> world.execute(() -> {
                boolean hasChanges = false;
                final Store<EntityStore> store = world.getEntityStore().getStore();
                final SpawnSuppressionController suppressionController = store.getResource(this.spawnSuppressionControllerResourceType);
                final Map<UUID, SpawnSuppressorEntry> spawnSuppressorMap = suppressionController.getSpawnSuppressorMap();
                for (final SpawnSuppressorEntry entry : spawnSuppressorMap.values()) {
                    if (removedAssets.contains(entry.getSuppressionId())) {
                        hasChanges = true;
                        break;
                    }
                }
                if (!(!hasChanges)) {
                    this.rebuildSuppressionMap(world, store, suppressionController);
                }
            }));
        }
        
        private void rebuildSuppressionMap(@Nonnull final World world, @Nonnull final Store<EntityStore> store, @Nonnull final SpawnSuppressionController suppressionController) {
            SpawningPlugin.get().getLogger().at(Level.INFO).log("Rebuilding spawn suppression map for world %s", world.getName());
            final ChunkStore chunkComponentStore = world.getChunkStore();
            final Store<ChunkStore> chunkStore = chunkComponentStore.getStore();
            final Long2ObjectConcurrentHashMap<ChunkSuppressionEntry> chunkSuppressionMap = suppressionController.getChunkSuppressionMap();
            for (final long key : chunkSuppressionMap.keySet()) {
                final Ref<ChunkStore> chunkReference = chunkComponentStore.getChunkReference(key);
                if (chunkReference == null) {
                    continue;
                }
                chunkStore.tryRemoveComponent(chunkReference, this.chunkSuppressionEntryComponentType);
            }
            chunkSuppressionMap.clear();
            store.forEachEntityParallel(SpawnMarkerEntity.getComponentType(), (index, archetypeChunk, commandBuffer) -> archetypeChunk.getComponent(index, SpawnMarkerEntity.getComponentType()).clearAllSuppressions());
            final Map<UUID, SpawnSuppressorEntry> spawnSuppressorMap = suppressionController.getSpawnSuppressorMap();
            spawnSuppressorMap.forEach((id, entry) -> SpawnSuppressionSystems.suppressSpawns(this.chunkSuppressionQueueResourceType, this.spawnMarkerEntityComponentType, id, entry, suppressionController, store, store.getExternalData().getWorld().getChunkStore()));
        }
    }
    
    public static class Suppressor extends RefSystem<EntityStore>
    {
        private final ComponentType<EntityStore, SpawnSuppressionComponent> spawnSuppressorComponentType;
        private final ResourceType<EntityStore, SpawnSuppressionController> spawnSuppressionControllerResourceType;
        private final ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType;
        private final ResourceType<ChunkStore, ChunkSuppressionQueue> chunkSuppressionQueueResourceType;
        private final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> spawnMarkerSpatialResourceType;
        private final ComponentType<EntityStore, TransformComponent> transformComponentType;
        private final ComponentType<EntityStore, UUIDComponent> uuidComponentType;
        @Nonnull
        private final Query<EntityStore> query;
        
        public Suppressor(final ComponentType<EntityStore, SpawnSuppressionComponent> spawnSuppressorComponentType, final ResourceType<EntityStore, SpawnSuppressionController> spawnSuppressionControllerResourceType, final ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerEntityComponentType, final ResourceType<ChunkStore, ChunkSuppressionQueue> chunkSuppressionQueueResourceType, final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> spawnMarkerSpatialResourceType) {
            this.transformComponentType = TransformComponent.getComponentType();
            this.uuidComponentType = UUIDComponent.getComponentType();
            this.spawnSuppressorComponentType = spawnSuppressorComponentType;
            this.spawnSuppressionControllerResourceType = spawnSuppressionControllerResourceType;
            this.spawnMarkerEntityComponentType = spawnMarkerEntityComponentType;
            this.chunkSuppressionQueueResourceType = chunkSuppressionQueueResourceType;
            this.spawnMarkerSpatialResourceType = spawnMarkerSpatialResourceType;
            this.query = (Query<EntityStore>)Query.and(spawnSuppressorComponentType, this.transformComponentType, this.uuidComponentType);
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nullable
        @Override
        public SystemGroup<EntityStore> getGroup() {
            return EntityModule.get().getPreClearMarkersGroup();
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> reference, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final Archetype<EntityStore> archetype = store.getArchetype(reference);
            final SpawnSuppressionComponent suppressor = store.getComponent(reference, this.spawnSuppressorComponentType);
            final TransformComponent transform = commandBuffer.getComponent(reference, this.transformComponentType);
            final UUIDComponent uuidComponent = commandBuffer.getComponent(reference, this.uuidComponentType);
            final boolean fromExternal = archetype.contains(FromPrefab.getComponentType()) || archetype.contains(FromWorldGen.getComponentType());
            if (reason != AddReason.SPAWN && !fromExternal) {
                return;
            }
            final SpawnSuppressionController suppressionController = store.getResource(this.spawnSuppressionControllerResourceType);
            final SpawnSuppressorEntry entry = new SpawnSuppressorEntry(suppressor.getSpawnSuppression(), transform.getPosition().clone());
            final UUID uuid = uuidComponent.getUuid();
            final SpawnSuppressorEntry prev = suppressionController.getSpawnSuppressorMap().put(uuid, entry);
            if (prev != null) {
                throw new IllegalStateException(String.format("A spawn suppressor with the ID %s is already registered.", uuid));
            }
            SpawnSuppressionSystems.suppressSpawns(this.chunkSuppressionQueueResourceType, this.spawnMarkerEntityComponentType, uuid, entry, suppressionController, store, store.getExternalData().getWorld().getChunkStore());
            commandBuffer.ensureComponent(reference, PrefabCopyableComponent.getComponentType());
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> reference, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            if (reason != RemoveReason.REMOVE) {
                return;
            }
            final SpawnSuppressionController suppressionController = store.getResource(this.spawnSuppressionControllerResourceType);
            final UUIDComponent uuidComponent = commandBuffer.getComponent(reference, this.uuidComponentType);
            final Map<UUID, SpawnSuppressorEntry> spawnSuppressorMap = suppressionController.getSpawnSuppressorMap();
            final UUID uuid = uuidComponent.getUuid();
            final SpawnSuppressorEntry entry = spawnSuppressorMap.remove(uuid);
            final String suppressionId = entry.getSuppressionId();
            final SpawnSuppression suppression = SpawnSuppression.getAssetMap().getAsset(suppressionId);
            if (suppression == null) {
                SpawningPlugin.get().getLogger().at(Level.WARNING).log("Spawn suppression config '%s' does not exist", suppressionId);
                return;
            }
            final double radius = suppression.getRadius();
            final Vector3d position = entry.getPosition();
            final int minChunkX = MathUtil.floor(position.x - radius) >> 5;
            final int minChunkZ = MathUtil.floor(position.z - radius) >> 5;
            final int maxChunkX = MathUtil.floor(position.x + radius) >> 5;
            final int maxChunkZ = MathUtil.floor(position.z + radius) >> 5;
            final ChunkStore chunkComponentStore = store.getExternalData().getWorld().getChunkStore();
            final Store<ChunkStore> chunkStore = chunkComponentStore.getStore();
            final Long2ObjectConcurrentHashMap<ChunkSuppressionEntry> chunkSuppressionMap = suppressionController.getChunkSuppressionMap();
            for (int chunkX = minChunkX; chunkX <= maxChunkX; ++chunkX) {
                for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; ++chunkZ) {
                    final long chunkIndex = ChunkUtil.indexChunk(chunkX, chunkZ);
                    ChunkSuppressionEntry chunkEntry = null;
                    final ChunkSuppressionEntry oldEntry = chunkSuppressionMap.get(chunkIndex);
                    if (oldEntry.containsOnly(uuid)) {
                        chunkSuppressionMap.remove(chunkIndex);
                    }
                    else {
                        final List<ChunkSuppressionEntry.SuppressionSpan> oldSpans = oldEntry.getSuppressionSpans();
                        final ObjectArrayList<ChunkSuppressionEntry.SuppressionSpan> suppressedSpans = new ObjectArrayList<ChunkSuppressionEntry.SuppressionSpan>();
                        for (final ChunkSuppressionEntry.SuppressionSpan span : oldSpans) {
                            if (span.getSuppressorId().equals(uuid)) {
                                continue;
                            }
                            suppressedSpans.add(span);
                        }
                        chunkEntry = new ChunkSuppressionEntry(suppressedSpans);
                        chunkSuppressionMap.put(chunkIndex, chunkEntry);
                    }
                    final Ref<ChunkStore> chunkReference = chunkComponentStore.getChunkReference(chunkIndex);
                    if (chunkReference != null) {
                        final ChunkSuppressionQueue chunkSuppressionQueue = chunkStore.getResource(this.chunkSuppressionQueueResourceType);
                        if (chunkEntry == null) {
                            chunkSuppressionQueue.queueForRemove(chunkReference);
                        }
                        else {
                            chunkSuppressionQueue.queueForAdd(chunkReference, chunkEntry);
                        }
                        SpawningPlugin.get().getLogger().at(Level.FINEST).log("Queuing removal of suppression from chunk index %s, %s", chunkIndex, suppressionId);
                    }
                }
            }
            if (!suppression.isSuppressSpawnMarkers()) {
                return;
            }
            final ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
            final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(this.spawnMarkerSpatialResourceType);
            spatialResource.getSpatialStructure().collect(position, radius, results);
            for (int i = 0; i < results.size(); ++i) {
                final Ref<EntityStore> markerRef = results.get(i);
                final SpawnMarkerEntity marker = commandBuffer.getComponent(markerRef, this.spawnMarkerEntityComponentType);
                marker.releaseSuppression(uuid);
                final HytaleLogger.Api context = SpawningPlugin.get().getLogger().at(Level.FINEST);
                if (context.isEnabled()) {
                    context.log("Releasing suppression of spawn marker %s", commandBuffer.getComponent(markerRef, this.uuidComponentType).getUuid());
                }
            }
        }
    }
    
    public static class EnsureNetworkSendable extends HolderSystem<EntityStore>
    {
        private final Query<EntityStore> query;
        
        public EnsureNetworkSendable() {
            this.query = (Query<EntityStore>)Query.and(SpawnSuppressionComponent.getComponentType(), Query.not((Query<Object>)NetworkId.getComponentType()));
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
        }
        
        @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;
        }
    }
}
