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

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

import com.hypixel.hytale.server.flock.config.FlockAsset;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.npc.NPCPlugin;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.spawning.SpawnRejection;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import com.hypixel.hytale.server.spawning.assets.spawns.config.RoleSpawnParameters;
import com.hypixel.hytale.server.spawning.world.manager.WorldSpawnWrapper;
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
import java.lang.ref.WeakReference;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.universe.world.World;

public class WorldNPCSpawnStat
{
    private final int roleIndex;
    @Nullable
    private final World world;
    @Nullable
    private WeakReference<BuilderInfo> builderInfoReference;
    private int minSpawnSize;
    private double expected;
    private int actual;
    private boolean unspawnable;
    @Nullable
    private final WorldSpawnWrapper spawnWrapper;
    @Nullable
    private final RoleSpawnParameters spawnParams;
    private int spansTried;
    private final Object2IntMap<SpawnRejection> rejections;
    private int spansSuccess;
    private int successfulJobCount;
    private int successfulJobTotalBudget;
    private int failedJobCount;
    private int failedJobTotalBudget;
    private final double weight;
    
    public WorldNPCSpawnStat(final int roleIndex, final WorldSpawnWrapper spawnWrapper, @Nonnull final RoleSpawnParameters spawnParams, final World world) {
        this.rejections = new Object2IntOpenHashMap<SpawnRejection>();
        this.roleIndex = roleIndex;
        this.world = world;
        this.builderInfoReference = new WeakReference<BuilderInfo>(NPCPlugin.get().getRoleBuilderInfo(roleIndex));
        this.weight = spawnParams.getWeight();
        this.spawnWrapper = spawnWrapper;
        this.spawnParams = spawnParams;
    }
    
    private WorldNPCSpawnStat(final int roleIndex) {
        this.rejections = new Object2IntOpenHashMap<SpawnRejection>();
        this.roleIndex = roleIndex;
        this.world = null;
        this.weight = 0.0;
        this.spawnWrapper = null;
        this.spawnParams = null;
    }
    
    public int getRoleIndex() {
        return this.roleIndex;
    }
    
    public double getExpected() {
        return this.expected;
    }
    
    public void setExpected(final double expected) {
        this.expected = expected;
    }
    
    public int getActual() {
        return this.actual;
    }
    
    public void adjustActual(final int count) {
        this.actual += count;
    }
    
    public boolean isUnspawnable() {
        return this.unspawnable;
    }
    
    public void setUnspawnable(final boolean unspawnable) {
        this.unspawnable = unspawnable;
    }
    
    @Nullable
    public WorldSpawnWrapper getSpawnWrapper() {
        return this.spawnWrapper;
    }
    
    @Nullable
    public RoleSpawnParameters getSpawnParams() {
        return this.spawnParams;
    }
    
    public int getSpansTried() {
        return this.spansTried;
    }
    
    public int getSpansSuccess() {
        return this.spansSuccess;
    }
    
    public int getSuccessfulJobCount() {
        return this.successfulJobCount;
    }
    
    public int getSuccessfulJobTotalBudget() {
        return this.successfulJobTotalBudget;
    }
    
    public int getFailedJobCount() {
        return this.failedJobCount;
    }
    
    public int getFailedJobTotalBudget() {
        return this.failedJobTotalBudget;
    }
    
    public double getWeight(final int moonPhase) {
        return this.weight * this.spawnWrapper.getMoonPhaseWeightModifier(moonPhase);
    }
    
    public double getMissingCount(@Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (this.unspawnable || !this.spawnWrapper.spawnParametersMatch(componentAccessor) || !this.isSpawnable()) {
            return 0.0;
        }
        final double slotsLeft = Math.max(this.expected - this.actual, 0.0);
        return (MathUtil.fastCeil(slotsLeft) < this.minSpawnSize) ? 0.0 : slotsLeft;
    }
    
    public int getAvailableSlots() {
        return Math.max((int)MathUtil.fastCeil(this.expected - this.actual), 0);
    }
    
    public int getRejectionCount(final SpawnRejection rejection) {
        return this.rejections.getInt(rejection);
    }
    
    public void updateSpawnStats(final int spansTried, final int spansSuccess, final int budgetUsed, @Nonnull final Object2IntMap<SpawnRejection> rejections, final boolean success) {
        this.spansTried += spansTried;
        this.spansSuccess += spansSuccess;
        for (final SpawnRejection rejection : SpawnRejection.VALUES) {
            this.rejections.mergeInt(rejection, rejections.getInt(rejection), Integer::sum);
        }
        if (success) {
            ++this.successfulJobCount;
            this.successfulJobTotalBudget += budgetUsed;
        }
        else {
            ++this.failedJobCount;
            this.failedJobTotalBudget += budgetUsed;
        }
    }
    
    public void resetUnspawnable() {
        this.unspawnable = false;
        if (this.builderInfoReference == null || this.builderInfoReference.get() != null) {
            this.builderInfoReference = new WeakReference<BuilderInfo>(null);
        }
    }
    
    private boolean isSpawnable() {
        if (this.builderInfoReference == null) {
            return false;
        }
        BuilderInfo builderInfo = this.builderInfoReference.get();
        final NPCPlugin npcModule = NPCPlugin.get();
        if (builderInfo != null && !builderInfo.isRemoved()) {
            return npcModule.testAndValidateRole(builderInfo);
        }
        builderInfo = npcModule.getRoleBuilderInfo(this.roleIndex);
        if (builderInfo == null) {
            this.builderInfoReference = null;
            return false;
        }
        this.builderInfoReference = new WeakReference<BuilderInfo>(builderInfo);
        if (!npcModule.testAndValidateRole(builderInfo)) {
            return false;
        }
        this.recomputeSpawnSize();
        return true;
    }
    
    private void recomputeSpawnSize() {
        final FlockAsset flockDefinition = this.spawnParams.getFlockDefinition();
        if (flockDefinition == null) {
            this.minSpawnSize = 1;
            return;
        }
        this.minSpawnSize = flockDefinition.getMinFlockSize();
    }
    
    public static class CountOnly extends WorldNPCSpawnStat
    {
        public CountOnly(final int roleIndex) {
            super(roleIndex);
        }
        
        @Override
        public double getWeight(final int moonPhase) {
            return 0.0;
        }
        
        @Override
        public double getMissingCount(@Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
            return 0.0;
        }
        
        @Override
        public int getAvailableSlots() {
            return 0;
        }
    }
}
