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

package com.hypixel.hytale.server.npc.decisionmaker.core;

import com.hypixel.hytale.common.map.IWeightedElement;
import javax.annotation.Nullable;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.component.CommandBuffer;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.server.npc.role.Role;
import java.util.Iterator;
import java.util.Comparator;
import java.util.List;
import com.hypixel.hytale.logger.HytaleLogger;

public abstract class Evaluator<OptionType extends Option>
{
    public static final HytaleLogger LOGGER;
    public static long NOT_USED;
    protected List<OptionHolder> options;
    
    public void initialise() {
        this.options.sort(Comparator.comparingDouble(OptionHolder::getWeightCoefficient).reversed());
        for (final OptionHolder optionHolder : this.options) {
            optionHolder.option.sortConditions();
        }
    }
    
    public void setupNPC(final Role role) {
        for (final OptionHolder optionHolder : this.options) {
            optionHolder.option.setupNPC(role);
        }
    }
    
    public void setupNPC(final Holder<EntityStore> holder) {
        for (final OptionHolder optionHolder : this.options) {
            optionHolder.option.setupNPC(holder);
        }
    }
    
    @Nullable
    public OptionHolder evaluate(final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, final CommandBuffer<EntityStore> commandBuffer, @Nonnull final EvaluationContext context) {
        final NPCEntity npcComponent = archetypeChunk.getComponent(index, NPCEntity.getComponentType());
        assert npcComponent != null;
        final UUIDComponent uuidComponent = archetypeChunk.getComponent(index, UUIDComponent.getComponentType());
        assert uuidComponent != null;
        OptionHolder bestOption = null;
        final double minimumWeight = context.getMinimumWeightCoefficient();
        int nonMatchingIndex = this.options.size();
        for (int i = 0; i < this.options.size(); ++i) {
            final OptionHolder optionHolder = this.options.get(i);
            if (optionHolder.getWeightCoefficient() < minimumWeight) {
                nonMatchingIndex = i;
                break;
            }
            final double utility = optionHolder.calculateUtility(index, archetypeChunk, commandBuffer, context);
            final HytaleLogger.Api logContext = Evaluator.LOGGER.at(Level.FINE);
            if (logContext.isEnabled()) {
                logContext.log("%s with uuid %s: Scored option %s at %s", npcComponent.getRoleName(), uuidComponent.getUuid(), optionHolder.option, utility);
            }
            if (utility > 0.0) {
                if (bestOption == null || utility > bestOption.utility) {
                    bestOption = optionHolder;
                }
            }
        }
        if (bestOption == null) {
            return null;
        }
        final float predictability = context.getPredictability();
        if (predictability == 1.0f) {
            return bestOption;
        }
        final double threshold = bestOption.utility * predictability;
        double sum = 0.0;
        for (int j = 0; j < nonMatchingIndex; ++j) {
            final OptionHolder optionHolder2 = this.options.get(j);
            if (optionHolder2.utility >= threshold) {
                sum += optionHolder2.getTotalUtility(threshold);
            }
        }
        double randomWeight = ThreadLocalRandom.current().nextDouble(sum);
        for (int k = 0; k < nonMatchingIndex; ++k) {
            final OptionHolder optionHolder3 = this.options.get(k);
            if (optionHolder3.utility >= threshold) {
                randomWeight = optionHolder3.tryPick(randomWeight, threshold);
                if (randomWeight <= 0.0) {
                    bestOption = optionHolder3;
                    break;
                }
            }
        }
        return bestOption;
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
        Evaluator.NOT_USED = 0L;
    }
    
    public abstract class OptionHolder implements IWeightedElement
    {
        protected final OptionType option;
        protected double utility;
        
        public OptionHolder(final Evaluator this$0, final OptionType option) {
            this.option = option;
        }
        
        @Override
        public double getWeight() {
            return this.utility;
        }
        
        public double getWeightCoefficient() {
            return this.option.getWeightCoefficient();
        }
        
        public OptionType getOption() {
            return this.option;
        }
        
        public double getTotalUtility(final double threshold) {
            return this.utility;
        }
        
        public double tryPick(final double currentWeight, final double threshold) {
            return currentWeight - this.utility;
        }
        
        public abstract double calculateUtility(final int p0, final ArchetypeChunk<EntityStore> p1, final CommandBuffer<EntityStore> p2, final EvaluationContext p3);
    }
}
