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

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

import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.schema.config.ArraySchema;
import com.hypixel.hytale.codec.schema.config.StringSchema;
import java.util.LinkedHashMap;
import com.hypixel.hytale.codec.schema.config.ObjectSchema;
import com.hypixel.hytale.codec.schema.NamedSchema;
import com.hypixel.hytale.codec.schema.SchemaConvertable;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.codec.schema.SchemaContext;
import com.hypixel.hytale.server.npc.asset.builder.validators.StringValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.StringNotEmptyValidator;
import java.util.function.Consumer;
import com.hypixel.hytale.server.npc.asset.builder.holder.StringHolder;
import com.google.gson.JsonArray;
import java.util.Iterator;
import java.util.List;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.util.BsonUtil;
import com.hypixel.hytale.server.npc.config.balancing.BalanceAsset;
import com.hypixel.hytale.server.npc.asset.builder.validators.StateStringValidator;
import com.google.gson.JsonElement;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import com.hypixel.hytale.codec.ExtraInfo;
import com.google.gson.JsonObject;
import com.hypixel.hytale.server.npc.asset.builder.expression.BuilderExpression;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import com.hypixel.hytale.logger.sentry.SkipSentryException;
import com.hypixel.hytale.server.npc.util.expression.ValueType;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import com.hypixel.hytale.server.npc.util.expression.StdScope;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
import com.hypixel.hytale.server.npc.util.expression.Scope;
import javax.annotation.Nonnull;
import java.util.Map;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;

public class BuilderModifier
{
    public static final String KEY_MODIFY = "Modify";
    public static final String KEY_EXPORT_STATES = "_ExportStates";
    public static final String KEY_INTERFACE_PARAMETERS = "_InterfaceParameters";
    public static final String KEY_COMBAT_CONFIG = "_CombatConfig";
    public static final String KEY_INTERACTION_VARS = "_InteractionVars";
    private final Object2ObjectMap<String, ExpressionHolder> builderExpressionMap;
    private final StatePair[] exportedStateIndexes;
    private final StateMappingHelper stateHelper;
    private final String combatConfig;
    private final Map<String, String> interactionVars;
    
    protected BuilderModifier(final Object2ObjectMap<String, ExpressionHolder> builderExpressionMap, final StatePair[] exportedStateIndexes, final StateMappingHelper stateHelper, final String combatConfig, final Map<String, String> interactionVars) {
        this.builderExpressionMap = builderExpressionMap;
        this.exportedStateIndexes = exportedStateIndexes;
        this.stateHelper = stateHelper;
        this.combatConfig = combatConfig;
        this.interactionVars = interactionVars;
    }
    
    public String getCombatConfig() {
        return this.combatConfig;
    }
    
    public Map<String, String> getInteractionVars() {
        return this.interactionVars;
    }
    
    public boolean isEmpty() {
        return this.builderExpressionMap.isEmpty();
    }
    
    public int exportedStateCount() {
        return this.exportedStateIndexes.length;
    }
    
    public void applyComponentStateMap(@Nonnull final BuilderSupport support) {
        support.setModifiedStateMap(this.stateHelper, this.exportedStateIndexes);
    }
    
    public void popComponentStateMap(@Nonnull final BuilderSupport support) {
        support.popModifiedStateMap();
    }
    
    @Nonnull
    public Scope createScope(@Nonnull final BuilderSupport builderSupport, @Nonnull final BuilderParameters builderParameters, final Scope globalScope) {
        final ExecutionContext executionContext = builderSupport.getExecutionContext();
        return this.createScope(executionContext, builderParameters, globalScope);
    }
    
    @Nonnull
    public Scope createScope(final ExecutionContext executionContext, @Nonnull final BuilderParameters builderParameters, @Nullable final Scope globalScope) {
        StdScope scope = builderParameters.createScope();
        if (globalScope != null) {
            final StdScope mergedScope = new StdScope(globalScope);
            mergedScope.merge(scope);
            scope = mergedScope;
        }
        final StdScope finalScope = scope;
        final ObjectIterator<Object2ObjectMap.Entry<String, ExpressionHolder>> iterator = Object2ObjectMaps.fastIterator(this.builderExpressionMap);
        while (iterator.hasNext()) {
            final Object2ObjectMap.Entry<String, ExpressionHolder> pair = iterator.next();
            final String name = pair.getKey();
            final ExpressionHolder holder = pair.getValue();
            final ValueType valueType = builderParameters.getParameterType(name);
            final BuilderExpression expression = holder.getExpression(builderParameters.getInterfaceCode());
            if (expression == null) {
                continue;
            }
            if (valueType == ValueType.VOID) {
                throw new SkipSentryException(new IllegalStateException("Parameter " + name + " does not exist or is private"));
            }
            if (!ValueType.isAssignableType(expression.getType(), valueType)) {
                throw new SkipSentryException(new IllegalStateException("Parameter " + name + " has type " + String.valueOf(expression.getType()) + " but should be " + String.valueOf(valueType)));
            }
            expression.updateScope(finalScope, name, executionContext);
        }
        return scope;
    }
    
    @Nonnull
    public static BuilderModifier fromJSON(@Nonnull final JsonObject jsonObject, @Nonnull final BuilderParameters builderParameters, @Nonnull final StateMappingHelper helper, @Nonnull final ExtraInfo extraInfo) {
        JsonObject modify = null;
        final JsonElement modifyObject = jsonObject.get("Modify");
        if (modifyObject != null) {
            modify = BuilderBase.expectObject(modifyObject, "Modify");
        }
        if (modify == null || modify.entrySet().isEmpty()) {
            return EmptyBuilderModifier.INSTANCE;
        }
        final Object2ObjectMap<String, ExpressionHolder> map = new Object2ObjectOpenHashMap<String, ExpressionHolder>();
        final List<StatePair> exportedStateIndexes = new ObjectArrayList<StatePair>();
        for (final Map.Entry<String, JsonElement> stringElementPair : modify.entrySet()) {
            final String key = stringElementPair.getKey();
            if (map.containsKey(key)) {
                throw new SkipSentryException(new IllegalStateException("Duplicate entry '" + key + "' in 'Modify' block"));
            }
            if (key.equals("_InterfaceParameters") || key.equals("_CombatConfig")) {
                continue;
            }
            if (key.equals("_InteractionVars")) {
                continue;
            }
            if (key.equals("_ExportStates")) {
                if (!stringElementPair.getValue().isJsonArray()) {
                    throw new SkipSentryException(new IllegalStateException(String.format("%s in modifier block must be a Json Array", "_ExportStates")));
                }
                final StateStringValidator validator = StateStringValidator.requireMainState();
                final JsonArray array = stringElementPair.getValue().getAsJsonArray();
                for (int i = 0; i < array.size(); ++i) {
                    final String state = array.get(i).getAsString();
                    if (!validator.test(state)) {
                        throw new SkipSentryException(new IllegalStateException(validator.errorMessage(state)));
                    }
                    final String substate = validator.hasSubState() ? validator.getSubState() : helper.getDefaultSubState();
                    helper.getAndPutSetterIndex(validator.getMainState(), substate, (m, s) -> exportedStateIndexes.add(new StatePair(validator.getMainState(), m, s)));
                }
            }
            else {
                final BuilderExpression expression = BuilderExpression.fromJSON(stringElementPair.getValue(), builderParameters, false);
                map.put(key, new ExpressionHolder(expression));
            }
        }
        final JsonElement interfaceValue = modify.get("_InterfaceParameters");
        if (interfaceValue != null) {
            final JsonObject interfaceParameters = BuilderBase.expectObject(interfaceValue, "_InterfaceParameters");
            for (final Map.Entry<String, JsonElement> interfaceEntry : interfaceParameters.entrySet()) {
                final String interfaceKey = interfaceEntry.getKey();
                final JsonObject parameters = BuilderBase.expectObject(interfaceEntry.getValue());
                for (final Map.Entry<String, JsonElement> parameterEntry : parameters.entrySet()) {
                    final ExpressionHolder holder = map.computeIfAbsent(parameterEntry.getKey(), key -> new ExpressionHolder());
                    if (holder.hasInterfaceMappedExpression(interfaceKey)) {
                        throw new SkipSentryException(new IllegalStateException("Duplicate entry '" + (String)parameterEntry.getKey() + "' in 'Modify' block for interface '" + interfaceKey));
                    }
                    holder.addInterfaceMappedExpression(interfaceKey, BuilderExpression.fromJSON(parameterEntry.getValue(), builderParameters, false));
                }
            }
        }
        String combatConfig = null;
        final JsonElement combatConfigValue = modify.get("_CombatConfig");
        if (combatConfigValue != null) {
            combatConfig = BalanceAsset.CHILD_ASSET_CODEC.decode(BsonUtil.translateJsonToBson(combatConfigValue), extraInfo);
            extraInfo.getValidationResults()._processValidationResults();
            extraInfo.getValidationResults().logOrThrowValidatorExceptions(HytaleLogger.getLogger());
        }
        Map<String, String> interactionVars = null;
        final JsonElement interactionVarsValue = modify.get("_InteractionVars");
        if (interactionVarsValue != null) {
            interactionVars = RootInteraction.CHILD_ASSET_CODEC_MAP.decode(BsonUtil.translateJsonToBson(interactionVarsValue), extraInfo);
            extraInfo.getValidationResults()._processValidationResults();
            extraInfo.getValidationResults().logOrThrowValidatorExceptions(HytaleLogger.getLogger());
        }
        return new BuilderModifier(map, exportedStateIndexes.toArray(StatePair[]::new), helper, combatConfig, interactionVars);
    }
    
    public static void readModifierObject(@Nonnull final JsonObject jsonObject, @Nonnull final BuilderParameters builderParameters, @Nonnull final StringHolder holder, @Nonnull final Consumer<StringHolder> referenceConsumer, @Nonnull final Consumer<BuilderModifier> builderModifierConsumer, @Nonnull final StateMappingHelper helper, @Nonnull final ExtraInfo extraInfo) {
        holder.readJSON(BuilderBase.expectKey(jsonObject, "Reference"), StringNotEmptyValidator.get(), "Reference", builderParameters);
        final BuilderModifier modifier = fromJSON(jsonObject, builderParameters, helper, extraInfo);
        referenceConsumer.accept(holder);
        builderModifierConsumer.accept(modifier);
    }
    
    @Nonnull
    public static Schema toSchema(@Nonnull final SchemaContext context) {
        return context.refDefinition(SchemaGenerator.INSTANCE);
    }
    
    private static class SchemaGenerator implements SchemaConvertable<Void>, NamedSchema
    {
        @Nonnull
        public static SchemaGenerator INSTANCE;
        
        @Nonnull
        @Override
        public String getSchemaName() {
            return "NPC:Type:BuilderModifier";
        }
        
        @Nonnull
        @Override
        public Schema toSchema(@Nonnull final SchemaContext context) {
            final ObjectSchema s = new ObjectSchema();
            s.setTitle("BuilderModifier");
            final LinkedHashMap<String, Schema> props = new LinkedHashMap<String, Schema>();
            s.setProperties(props);
            props.put("_ExportStates", new ArraySchema(new StringSchema()));
            props.put("_InterfaceParameters", new ObjectSchema());
            final Schema combatConfigKeySchema = context.refDefinition(Codec.STRING);
            combatConfigKeySchema.setTitle("Reference to " + BalanceAsset.class.getSimpleName());
            final Schema combatConfigNestedSchema = context.refDefinition(BalanceAsset.CHILD_ASSET_CODEC);
            final Schema combatConfigSchema = Schema.anyOf(combatConfigKeySchema, combatConfigNestedSchema);
            props.put("_CombatConfig", combatConfigSchema);
            final ObjectSchema interactionVars = new ObjectSchema();
            interactionVars.setTitle("Map");
            final Schema childSchema = context.refDefinition(RootInteraction.CHILD_ASSET_CODEC);
            interactionVars.setAdditionalProperties(childSchema);
            props.put("_InteractionVars", interactionVars);
            s.setAdditionalProperties(BuilderExpression.toSchema(context));
            return s;
        }
        
        static {
            SchemaGenerator.INSTANCE = new SchemaGenerator();
        }
    }
    
    private static class ExpressionHolder
    {
        private final BuilderExpression expression;
        private Object2ObjectMap<String, BuilderExpression> interfaceMappedExpressions;
        
        public ExpressionHolder() {
            this(null);
        }
        
        public ExpressionHolder(final BuilderExpression expression) {
            this.expression = expression;
        }
        
        public boolean hasInterfaceMappedExpression(final String interfaceKey) {
            return this.interfaceMappedExpressions != null && this.interfaceMappedExpressions.containsKey(interfaceKey);
        }
        
        public void addInterfaceMappedExpression(final String interfaceKey, final BuilderExpression expression) {
            if (this.interfaceMappedExpressions == null) {
                this.interfaceMappedExpressions = new Object2ObjectOpenHashMap<String, BuilderExpression>();
            }
            this.interfaceMappedExpressions.put(interfaceKey, expression);
        }
        
        public BuilderExpression getExpression(@Nullable final String interfaceKey) {
            if (interfaceKey == null || this.interfaceMappedExpressions == null || !this.interfaceMappedExpressions.containsKey(interfaceKey)) {
                return this.expression;
            }
            return this.interfaceMappedExpressions.get(interfaceKey);
        }
    }
}
