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

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

import com.hypixel.hytale.server.core.command.system.arguments.system.Argument;
import com.hypixel.hytale.server.spawning.world.WorldNPCSpawnStat;
import com.hypixel.hytale.server.spawning.world.WorldEnvironmentSpawnData;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.server.spawning.world.ChunkEnvironmentSpawnData;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.server.npc.components.SpawnMarkerReference;
import com.hypixel.hytale.component.ComponentType;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.HashMap;
import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import java.util.logging.Level;
import com.hypixel.hytale.server.spawning.SpawnRejection;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnData;
import com.hypixel.hytale.server.core.asset.type.environment.config.Environment;
import java.util.concurrent.atomic.AtomicInteger;
import com.hypixel.hytale.server.spawning.world.component.WorldSpawnData;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand;

public class SpawnStatsCommand extends AbstractWorldCommand
{
    @Nonnull
    private final FlagArg environmentsArg;
    @Nonnull
    private final FlagArg markersArg;
    @Nonnull
    private final FlagArg verboseArg;
    
    public SpawnStatsCommand() {
        super("stats", "server.commands.spawning.stats.desc");
        this.environmentsArg = this.withFlagArg("environments", "server.commands.spawning.stats.arg.environments.desc");
        this.markersArg = this.withFlagArg("markers", "server.commands.spawning.stats.arg.markers.desc");
        this.verboseArg = this.withFlagArg("verbose", "server.commands.spawning.stats.arg.verbose.desc");
    }
    
    @Override
    protected void execute(@Nonnull final CommandContext context, @Nonnull final World world, @Nonnull final Store<EntityStore> store) {
        if (((Argument<Arg, Boolean>)this.environmentsArg).get(context)) {
            final WorldSpawnData worldSpawnData = store.getResource(WorldSpawnData.getResourceType());
            final AtomicInteger filtered = new AtomicInteger();
            final boolean verbose = ((Argument<Arg, Boolean>)this.verboseArg).get(context);
            worldSpawnData.forEachEnvironmentSpawnData(worldEnvironmentSpawnData -> {
                if (!verbose && (!worldEnvironmentSpawnData.hasNPCs() || worldEnvironmentSpawnData.getExpectedNPCs() == 0.0)) {
                    filtered.getAndIncrement();
                    return;
                }
                else {
                    final int environmentIndex = worldEnvironmentSpawnData.getEnvironmentIndex();
                    final String name = Environment.getAssetMap().getAsset(environmentIndex).getId();
                    final Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
                    final double[] chunkExpected = { 0.0 };
                    worldEnvironmentSpawnData.getChunkRefSet().forEach(ref -> {
                        final ChunkEnvironmentSpawnData chunkEnvironmentSpawnData = chunkStore.getComponent(ref, ChunkSpawnData.getComponentType()).getEnvironmentSpawnData(environmentIndex);
                        final int n;
                        chunkExpected[n] += chunkEnvironmentSpawnData.getExpectedNPCs();
                        return;
                    });
                    final String message2 = String.format("Environment: %-30s Exp %6.2f Act %4d Blk %s Chunk exp: %6.2f", name, worldEnvironmentSpawnData.getExpectedNPCs(), worldEnvironmentSpawnData.getActualNPCs(), worldEnvironmentSpawnData.getSegmentCount(), chunkExpected[0]);
                    NPCPlugin.get().getLogger().atInfo().log(message2);
                    worldEnvironmentSpawnData.forEachNpcStat((npcIndex, stats) -> {
                        final int all = stats.getSpansTried();
                        final double failPercent = (all > 0) ? MathUtil.percent(all - stats.getSpansSuccess(), all) : 0.0;
                        String message3;
                        if (verbose) {
                            final int successfulJobCount = stats.getSuccessfulJobCount();
                            final int successfulBudget = stats.getSuccessfulJobTotalBudget();
                            final int failedJobCount = stats.getFailedJobCount();
                            final int failedBudget = stats.getFailedJobTotalBudget();
                            message3 = String.format("        NPC: %-30s Exp %6.2f Act %4d | Spns %8d: Succ %4d Lgt %5.1f Blk %5.1f Pos %5.1f Geo %5.1f Bre %5.1f Oth %5.1f | Fail%% %5.1f | Spwnbl %s | Succ Job Bgt %6d Avg Bgt %6.2f | Fail Jobs %6d Bgt %6d Avg Bgt %6.2f ", NPCPlugin.get().getName(npcIndex), stats.getExpected(), stats.getActual(), all, stats.getSpansSuccess(), MathUtil.percent(stats.getRejectionCount(SpawnRejection.OUTSIDE_LIGHT_RANGE), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.INVALID_SPAWN_BLOCK), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.NO_POSITION), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.INVALID_POSITION), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.NOT_BREATHABLE), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.OTHER), all), failPercent, !stats.isUnspawnable(), successfulBudget, (successfulJobCount > 0) ? (successfulBudget / (double)successfulJobCount) : 0.0, failedJobCount, failedBudget, (failedJobCount > 0) ? (failedBudget / (double)failedJobCount) : 0.0);
                        }
                        else {
                            message3 = String.format("        NPC: %-30s Exp %6.2f Act %4d | Spns %8d: Succ %4d Lgt %5.1f Blk %5.1f Pos %5.1f Geo %5.1f Bre %5.1f Oth %5.1f | Fail%% %5.1f | Spwnbl %s ", NPCPlugin.get().getName(npcIndex), stats.getExpected(), stats.getActual(), all, stats.getSpansSuccess(), MathUtil.percent(stats.getRejectionCount(SpawnRejection.OUTSIDE_LIGHT_RANGE), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.INVALID_SPAWN_BLOCK), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.NO_POSITION), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.INVALID_POSITION), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.NOT_BREATHABLE), all), MathUtil.percent(stats.getRejectionCount(SpawnRejection.OTHER), all), failPercent, !stats.isUnspawnable());
                        }
                        NPCPlugin.get().getLogger().at((failPercent < 60.0) ? Level.INFO : Level.WARNING).log(message3);
                    });
                    return;
                }
            });
            final AtomicInteger trackedNPC = new AtomicInteger();
            final AtomicInteger totalNPC = new AtomicInteger();
            store.forEachEntityParallel(NPCEntity.getComponentType(), (index, archetypeChunk, commandBuffer) -> {
                totalNPC.getAndIncrement();
                final NPCEntity npc = archetypeChunk.getComponent(index, NPCEntity.getComponentType());
                if (npc.getEnvironment() == Integer.MIN_VALUE || npc.getSpawnConfiguration() == Integer.MIN_VALUE) {
                    return;
                }
                else {
                    trackedNPC.getAndIncrement();
                    return;
                }
            });
            final int spawnJobsCompleted = worldSpawnData.getTotalSpawnJobsCompleted();
            final String message = String.format("Total: Exp %.2f Exp-Empty %.2f Act %d Job Pending Act %d Tracked %d Total %d Unspawnable %s AvgSegCount %s Chunks %s Filtered empty envs %d Active Jobs %d Total Jobs Run %d Avg Job Budget %.2f", worldSpawnData.getExpectedNPCs(), worldSpawnData.getExpectedNPCsInEmptyEnvironments(), worldSpawnData.getActualNPCs(), worldSpawnData.getTrackedCountFromJobs(), trackedNPC.get(), totalNPC.get(), worldSpawnData.isUnspawnable(), worldSpawnData.averageSegmentCount(), worldSpawnData.getChunkCount(), filtered.get(), worldSpawnData.getActiveSpawnJobs(), spawnJobsCompleted, (spawnJobsCompleted > 0) ? (worldSpawnData.getTotalSpawnJobBudgetUsed() / (double)spawnJobsCompleted) : 0.0);
            NPCPlugin.get().getLogger().atInfo().log(message);
        }
        if (((Argument<Arg, Boolean>)this.markersArg).get(context)) {
            final AtomicInteger spawnMarkerCount = new AtomicInteger();
            final AtomicInteger inactiveSpawnMarkerCount = new AtomicInteger();
            final Object2IntOpenHashMap<String> spawnMarkerTypeCounts = new Object2IntOpenHashMap<String>();
            store.forEachChunk(SpawnMarkerEntity.getComponentType(), (archetypeChunk, componentStoreCommandBuffer) -> {
                for (int index2 = 0; index2 < archetypeChunk.size(); ++index2) {
                    final SpawnMarkerEntity entity = archetypeChunk.getComponent(index2, SpawnMarkerEntity.getComponentType());
                    spawnMarkerCount.getAndIncrement();
                    spawnMarkerTypeCounts.mergeInt(entity.getSpawnMarkerId(), 1, Integer::sum);
                    if (entity.getSpawnCount() == 0) {
                        inactiveSpawnMarkerCount.getAndIncrement();
                    }
                }
                return;
            });
            final AtomicInteger spawnMarkerNPCCount = new AtomicInteger();
            final Object2IntOpenHashMap<String> roleCounts = new Object2IntOpenHashMap<String>();
            final HashMap<String, Object2IntMap<String>> roleCountsPerMarkerType = new HashMap<String, Object2IntMap<String>>();
            store.forEachChunk((Query<EntityStore>)Archetype.of(NPCEntity.getComponentType(), SpawnMarkerReference.getComponentType()), (archetypeChunk, componentStoreCommandBuffer) -> {
                for (int index3 = 0; index3 < archetypeChunk.size(); ++index3) {
                    final NPCEntity entity2 = archetypeChunk.getComponent(index3, NPCEntity.getComponentType());
                    final SpawnMarkerReference spawnMarkerReference = archetypeChunk.getComponent(index3, SpawnMarkerReference.getComponentType());
                    spawnMarkerNPCCount.getAndIncrement();
                    final String roleName = entity2.getRoleName();
                    roleCounts.mergeInt(roleName, 1, Integer::sum);
                    final Ref<EntityStore> markerRef = spawnMarkerReference.getReference().getEntity(componentStoreCommandBuffer);
                    final SpawnMarkerEntity marker = componentStoreCommandBuffer.getComponent(markerRef, SpawnMarkerEntity.getComponentType());
                    final Object2IntMap<String> spawnedRoles = roleCountsPerMarkerType.computeIfAbsent(marker.getSpawnMarkerId(), key -> new Object2IntOpenHashMap());
                    spawnedRoles.mergeInt(roleName, 1, Integer::sum);
                }
                return;
            });
            final StringBuilder sb = new StringBuilder();
            sb.append("Markers: ").append(spawnMarkerCount.get()).append(" (With zero spawns: ").append(inactiveSpawnMarkerCount.get()).append(")\nSpawned NPCs: ").append(spawnMarkerNPCCount.get());
            roleCounts.object2IntEntrySet().fastForEach(stringEntry -> sb.append("\n  ").append((String)stringEntry.getKey()).append(": ").append(stringEntry.getIntValue()));
            sb.append("\nRoles by marker type:");
            roleCountsPerMarkerType.forEach((key, spawnedRoles) -> {
                sb.append("\n  ").append(key).append(" (Instances: ").append(spawnMarkerTypeCounts.getInt(key)).append(")");
                spawnedRoles.object2IntEntrySet().forEach(entry -> sb.append("\n    ").append((String)entry.getKey()).append(": ").append(entry.getIntValue()));
                return;
            });
            NPCPlugin.get().getLogger().atInfo().log(sb.toString());
        }
        context.sendMessage(Message.translation("server.commands.spawning.stats.results"));
    }
}
