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

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

import java.util.Objects;
import java.util.Iterator;
import java.util.Collection;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List;
import java.util.logging.Level;
import com.hypixel.hytale.server.npc.NPCPlugin;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.valuestore.ValueStoreValidator;
import java.util.ArrayDeque;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import java.util.Set;
import java.util.HashSet;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;

public class NPCLoadTimeValidationHelper
{
    private final String fileName;
    private final Model spawnModel;
    private final boolean isAbstract;
    private final HashSet<String> evaluatedAnimations;
    private final Set<Class<? extends MotionController>> providedMotionControllers;
    private final Set<Class<? extends MotionController>> requiredMotionControllers;
    private final ArrayDeque<HashSet<String>> seenFilterStack;
    private final ValueStoreValidator valueStoreValidator;
    @Nullable
    private Set<String> prioritiserProvidedFilterTypes;
    private int inventorySize;
    private int hotbarSize;
    private int offHandSize;
    private boolean parentSensorOnce;
    private boolean isVariant;
    private final ArrayDeque<String> stateStack;
    
    public NPCLoadTimeValidationHelper(final String fileName, final Model spawnModel, final boolean isAbstract) {
        this.evaluatedAnimations = new HashSet<String>();
        this.providedMotionControllers = new HashSet<Class<? extends MotionController>>();
        this.requiredMotionControllers = new HashSet<Class<? extends MotionController>>();
        this.seenFilterStack = new ArrayDeque<HashSet<String>>();
        this.valueStoreValidator = new ValueStoreValidator();
        this.stateStack = new ArrayDeque<String>();
        this.fileName = fileName;
        this.spawnModel = spawnModel;
        this.isAbstract = isAbstract;
    }
    
    public void setInventorySizes(final int inventorySize, final int hotbarSize, final int offHandSize) {
        this.inventorySize = inventorySize;
        this.hotbarSize = hotbarSize;
        this.offHandSize = offHandSize;
    }
    
    public Model getSpawnModel() {
        return this.spawnModel;
    }
    
    public boolean isAbstract() {
        return this.isAbstract;
    }
    
    public boolean isParentSensorOnce() {
        return this.parentSensorOnce;
    }
    
    public void updateParentSensorOnce(final boolean parentSensorOnce) {
        this.parentSensorOnce |= parentSensorOnce;
    }
    
    public void clearParentSensorOnce() {
        this.parentSensorOnce = false;
    }
    
    public void setIsVariant() {
        this.isVariant = true;
    }
    
    public boolean isVariant() {
        return this.isVariant;
    }
    
    @Nonnull
    public ValueStoreValidator getValueStoreValidator() {
        return this.valueStoreValidator;
    }
    
    @Nullable
    public String getCurrentStateName() {
        return this.stateStack.peek();
    }
    
    public void pushCurrentStateName(@Nonnull final String currentStateName) {
        this.stateStack.push(currentStateName);
    }
    
    public void popCurrentStateName() {
        this.stateStack.pop();
    }
    
    public void validateAnimation(@Nullable final String animation) {
        if (animation == null || animation.isEmpty()) {
            return;
        }
        if (!this.evaluatedAnimations.add(animation)) {
            return;
        }
        if (!this.spawnModel.getAnimationSetMap().containsKey(animation)) {
            NPCPlugin.get().getLogger().at(Level.WARNING).log("Animation %s does not exist for model %s!", animation, this.spawnModel.getModelAssetId());
        }
    }
    
    public void registerMotionControllerType(final Class<? extends MotionController> clazz) {
        this.providedMotionControllers.add(clazz);
    }
    
    public void requireMotionControllerType(final Class<? extends MotionController> clazz) {
        this.requiredMotionControllers.add(clazz);
    }
    
    public boolean validateMotionControllers(@Nonnull final List<String> errors) {
        if (this.requiredMotionControllers.isEmpty()) {
            return true;
        }
        final ObjectArrayList<Class<? extends MotionController>> providedMotionControllerList = new ObjectArrayList<Class<? extends MotionController>>(this.providedMotionControllers);
        int validCount = 0;
        for (final Class<? extends MotionController> requiredMotionController : this.requiredMotionControllers) {
            boolean missing = true;
            for (int i = 0; i < providedMotionControllerList.size(); ++i) {
                if (providedMotionControllerList.get(i).isAssignableFrom(requiredMotionController)) {
                    ++validCount;
                    missing = false;
                    break;
                }
            }
            if (missing) {
                errors.add(String.format("%s: Missing required motion controller: %s", this.fileName, requiredMotionController.getSimpleName()));
            }
        }
        return this.requiredMotionControllers.size() == validCount;
    }
    
    public boolean validateInventoryHasSlot(final int slot, final String context, @Nonnull final List<String> errors) {
        if (slot < this.inventorySize) {
            return true;
        }
        errors.add(String.format("%s: Inventory too small for slot %d, requested by %s", this.fileName, slot, context));
        return false;
    }
    
    public boolean validateHotbarHasSlot(final int slot, final String context, @Nonnull final List<String> errors) {
        if (slot < 0) {
            errors.add(String.format("%s: Hotbar slot %s is not valid for parameter %s. Must be >= 0", this.fileName, slot, context));
            return false;
        }
        if (slot < this.hotbarSize) {
            return true;
        }
        errors.add(String.format("%s: Hotbar too small for slot %d, requested by %s. Actual size is %d", this.fileName, slot, context, this.hotbarSize));
        return false;
    }
    
    public boolean validateOffHandHasSlot(final int slot, final String context, @Nonnull final List<String> errors) {
        if (slot < -1) {
            errors.add(String.format("%s: Off-hand slot %s is not valid for parameter %s. Must be -1 for empty, or >= 0", this.fileName, slot, context));
            return false;
        }
        if (slot < this.offHandSize) {
            return true;
        }
        errors.add(String.format("%s: Off-hand inventory too small for slot %d, requested by %s. Actual size is %d", this.fileName, slot, context, this.offHandSize));
        return false;
    }
    
    public void pushFilterSet() {
        this.seenFilterStack.push(new HashSet<String>());
    }
    
    public void popFilterSet() {
        this.seenFilterStack.pop();
    }
    
    public boolean hasSeenFilter(final String filter) {
        final HashSet<String> set = this.seenFilterStack.peek();
        Objects.requireNonNull(set, "A filter set must have been pushed before checking if a filter has been seen!");
        return !set.add(filter);
    }
    
    public void setPrioritiserProvidedFilterTypes(final Set<String> prioritiserProvidedFilterTypes) {
        this.prioritiserProvidedFilterTypes = prioritiserProvidedFilterTypes;
    }
    
    public boolean isFilterExternallyProvided(final String filter) {
        return this.prioritiserProvidedFilterTypes != null && this.prioritiserProvidedFilterTypes.contains(filter);
    }
    
    public void clearPrioritiserProvidedFilterTypes() {
        this.prioritiserProvidedFilterTypes = null;
    }
}
