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

package com.hypixel.hytale.server.npc.asset.builder;

import com.hypixel.hytale.server.npc.instructions.Instruction;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import javax.annotation.Nonnull;
import java.util.Objects;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import com.hypixel.hytale.server.npc.instructions.builders.BuilderInstructionReference;
import java.util.List;

public class InternalReferenceResolver
{
    private final List<BuilderInstructionReference> builders;
    @Nullable
    private Object2IntMap<String> indexMap;
    @Nullable
    private Int2ObjectMap<String> nameMap;
    @Nullable
    private IntSet recordedDependencies;
    
    public InternalReferenceResolver() {
        this.builders = new ObjectArrayList<BuilderInstructionReference>();
        this.nameMap = new Int2ObjectOpenHashMap<String>();
        (this.indexMap = new Object2IntOpenHashMap<String>()).defaultReturnValue(Integer.MIN_VALUE);
    }
    
    public int getOrCreateIndex(final String name) {
        int index = this.indexMap.getInt(name);
        if (index == Integer.MIN_VALUE) {
            index = this.builders.size();
            this.indexMap.put(name, index);
            this.nameMap.put(index, name);
            this.builders.add(null);
        }
        if (this.recordedDependencies != null) {
            this.recordedDependencies.add(index);
        }
        return index;
    }
    
    public void setRecordDependencies() {
        this.recordedDependencies = new IntOpenHashSet();
    }
    
    @Nullable
    public IntSet getRecordedDependenices() {
        return this.recordedDependencies;
    }
    
    public void stopRecordingDependencies() {
        this.recordedDependencies = null;
    }
    
    public void addBuilder(final int index, final BuilderInstructionReference builder) {
        Objects.requireNonNull(builder, "Builder cannot be null when adding as a reference");
        if (index < 0 || index >= this.builders.size()) {
            throw new IllegalArgumentException("Slot for putting builder must be >= 0 and < the size of the list");
        }
        if (this.builders.get(index) != null) {
            throw new IllegalStateException(String.format("Duplicate internal reference builder with name: %s", this.nameMap.get(index)));
        }
        this.builders.set(index, builder);
    }
    
    public void validateInternalReferences(final String configName, @Nonnull final List<String> errors) {
        for (int i = 0; i < this.builders.size(); ++i) {
            final BuilderInstructionReference builder = this.builders.get(i);
            if (builder == null) {
                errors.add(configName + ": Internal reference builder: " + (String)this.nameMap.get(i) + " doesn't exist");
            }
            else {
                try {
                    this.validateNoCycles(builder, i, new IntArrayList());
                }
                catch (final IllegalArgumentException e) {
                    errors.add(configName + ": " + e.getMessage());
                }
            }
        }
    }
    
    private void validateNoCycles(@Nonnull final BuilderInstructionReference builder, final int index, @Nonnull final IntArrayList path) {
        if (path.contains(index)) {
            throw new IllegalArgumentException("Cyclic reference detected for internal component reference: " + (String)this.nameMap.get(index));
        }
        path.add(index);
        final IntIterator i = builder.getInternalDependencies().iterator();
        while (i.hasNext()) {
            final int dependency = i.nextInt();
            final BuilderInstructionReference nextBuilder = this.builders.get(dependency);
            if (nextBuilder == null) {
                throw new IllegalStateException("Reference to internal reference builder: " + (String)this.nameMap.get(dependency) + " which doesn't exist");
            }
            this.validateNoCycles(nextBuilder, dependency, path);
        }
        path.removeInt(path.size() - 1);
    }
    
    public <T> Builder<T> getBuilder(final int index, final Class<?> classType) {
        if (classType != Instruction.class) {
            throw new IllegalArgumentException("Internal references are currently only supported for instruction list");
        }
        return (Builder)this.builders.get(index);
    }
    
    public void optimise() {
        this.indexMap = null;
        this.nameMap = null;
    }
}
