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

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

import java.util.function.Supplier;
import com.hypixel.hytale.server.npc.asset.builder.BuilderContext;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import java.util.Iterator;
import java.util.Map;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.HashMap;
import java.util.EnumMap;

public class ValueStoreValidator
{
    private final EnumMap<ValueStore.Type, HashMap<String, List<ValueUsage>>> usages;
    
    public ValueStoreValidator() {
        this.usages = new EnumMap<ValueStore.Type, HashMap<String, List<ValueUsage>>>(ValueStore.Type.class);
    }
    
    public void registerValueUsage(@Nonnull final ValueUsage usage) {
        if (usage.useType == UseType.READ) {
            return;
        }
        final HashMap<String, List<ValueUsage>> usagesOfType = this.usages.computeIfAbsent(usage.valueType, k -> new HashMap());
        final List<ValueUsage> usagesByParameter = usagesOfType.computeIfAbsent(usage.name, k -> new ObjectArrayList());
        usagesByParameter.add(usage);
    }
    
    public boolean validate(@Nonnull final List<String> errors) {
        boolean result = true;
        for (final ValueStore.Type type : ValueStore.Type.VALUES) {
            result &= this.validateType(type, errors);
        }
        return result;
    }
    
    private boolean validateType(@Nonnull final ValueStore.Type type, @Nonnull final List<String> errors) {
        final HashMap<String, List<ValueUsage>> usagesOfType = this.usages.get(type);
        if (usagesOfType == null) {
            return true;
        }
        boolean result = true;
        final ObjectArrayList<ValueUsage> writes = new ObjectArrayList<ValueUsage>();
        final ObjectArrayList<ValueUsage> exclusiveWrites = new ObjectArrayList<ValueUsage>();
        for (final Map.Entry<String, List<ValueUsage>> usagesByParameter : usagesOfType.entrySet()) {
            for (final ValueUsage usage : usagesByParameter.getValue()) {
                writes.add(usage);
                if (usage.useType == UseType.EXCLUSIVE_WRITE) {
                    exclusiveWrites.add(usage);
                }
            }
            if (writes.size() > 1 && !exclusiveWrites.isEmpty()) {
                final StringBuilder sb = new StringBuilder();
                sb.append("The core components [ ");
                for (final ValueUsage writer : exclusiveWrites) {
                    sb.append(writer.context.getLabel()).append(" ");
                }
                sb.append("] require an exclusive write of the ").append(type.get()).append(" parameter '").append(usagesByParameter.getKey()).append("' but it is written to by [ ");
                for (final ValueUsage writer : writes) {
                    sb.append(writer.context.getLabel()).append(" ");
                }
                sb.append("]");
                errors.add(sb.toString());
                result = false;
            }
            writes.clear();
            exclusiveWrites.clear();
        }
        return result;
    }
    
    public static class ValueUsage
    {
        protected final String name;
        protected final ValueStore.Type valueType;
        protected final UseType useType;
        protected final BuilderContext context;
        
        public ValueUsage(final String name, final ValueStore.Type valueType, final UseType useType, final BuilderContext context) {
            this.name = name;
            this.valueType = valueType;
            this.useType = useType;
            this.context = context;
        }
    }
    
    public enum UseType implements Supplier<String>
    {
        READ("Reads the value"), 
        WRITE("Writes the value"), 
        EXCLUSIVE_WRITE("Has exclusive write ownership of the value");
        
        private final String description;
        
        private UseType(final String description) {
            this.description = description;
        }
        
        @Override
        public String get() {
            return this.description;
        }
    }
}
