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

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

import com.hypixel.hytale.server.npc.asset.builder.expression.BuilderExpressionStaticBooleanArray;
import com.hypixel.hytale.server.npc.asset.builder.expression.BuilderExpressionStaticNumberArray;
import com.hypixel.hytale.server.npc.asset.builder.expression.BuilderExpressionStaticStringArray;
import com.hypixel.hytale.server.npc.asset.builder.expression.BuilderExpressionStaticEmptyArray;
import com.hypixel.hytale.codec.schema.config.BooleanSchema;
import java.util.LinkedHashMap;
import com.hypixel.hytale.server.npc.asset.builder.expression.BuilderExpression;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.codec.schema.config.ArraySchema;
import com.hypixel.hytale.codec.schema.config.StringSchema;
import com.hypixel.hytale.codec.schema.config.ObjectSchema;
import com.hypixel.hytale.codec.schema.SchemaContext;
import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
import java.util.List;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.hypixel.hytale.server.npc.util.expression.ValueType;
import java.util.HashMap;
import com.hypixel.hytale.server.npc.util.expression.Scope;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.util.expression.compile.CompileContext;
import com.hypixel.hytale.server.npc.util.expression.StdScope;
import javax.annotation.Nonnull;
import java.util.Map;

public class BuilderParameters
{
    public static final String KEY_PARAMETERS = "Parameters";
    public static final String KEY_IMPORT_STATES = "_ImportStates";
    public static final String KEY_INTERFACE = "Interface";
    @Nonnull
    protected final Map<String, Parameter> parameters;
    protected StdScope scope;
    @Nullable
    protected CompileContext compileContext;
    protected final String fileName;
    protected final IntSet dependencies;
    protected final String interfaceCode;
    
    protected BuilderParameters(final StdScope scope, final String fileName, final String interfaceCode) {
        this(scope, fileName, interfaceCode, new IntOpenHashSet());
    }
    
    protected BuilderParameters(final StdScope scope, final String fileName, final String interfaceCode, final IntSet dependencies) {
        this.scope = new StdScope(scope);
        this.compileContext = new CompileContext(this.scope);
        this.parameters = new HashMap<String, Parameter>();
        this.fileName = fileName;
        this.dependencies = dependencies;
        this.interfaceCode = interfaceCode;
    }
    
    protected BuilderParameters(@Nonnull final BuilderParameters other) {
        this(other.scope, other.fileName, other.interfaceCode, other.dependencies);
    }
    
    public boolean isEmpty() {
        return this.parameters.isEmpty();
    }
    
    public void addParametersToScope() {
        this.parameters.forEach((name, parameter) -> parameter.expression.addToScope(name, this.scope));
    }
    
    public ValueType getParameterType(final String name) {
        final Parameter p = this.parameters.get(name);
        if (p == null || p.isPrivate()) {
            return ValueType.VOID;
        }
        return p.getExpression().getType();
    }
    
    public void readJSON(@Nonnull final JsonObject jsonObject, @Nonnull final StateMappingHelper stateHelper) {
        final JsonElement parameterValue = jsonObject.get("Parameters");
        if (parameterValue == null) {
            return;
        }
        final JsonObject modify = BuilderBase.expectObject(parameterValue, "Parameters");
        modify.entrySet().forEach(stringElementPair -> {
            final String key = stringElementPair.getKey();
            if (this.parameters.containsKey(key)) {
                throw new IllegalStateException("Duplicate entry '" + key + "' in 'Parameters' block");
            }
            else if (key.equals("_ImportStates")) {
                if (!stringElementPair.getValue().isJsonArray()) {
                    new IllegalStateException(String.format("%s in parameter block must be a Json Array", "_ImportStates"));
                    throw;
                }
                else {
                    stateHelper.setComponentImportStateMappings(stringElementPair.getValue().getAsJsonArray());
                }
            }
            else {
                this.parameters.put(key, Parameter.fromJSON(stringElementPair.getValue(), this, key));
            }
        });
    }
    
    public void createCompileContext() {
        this.compileContext = new CompileContext();
    }
    
    public void disposeCompileContext() {
        this.compileContext = null;
    }
    
    @Nullable
    public CompileContext getCompileContext() {
        return this.compileContext;
    }
    
    public ValueType compile(@Nonnull final String expression) {
        return this.getCompileContext().compile(expression, this.getScope(), false);
    }
    
    public List<ExecutionContext.Instruction> getInstructions() {
        return this.getCompileContext().getInstructions();
    }
    
    @Nullable
    public ExecutionContext.Operand getConstantOperand() {
        return this.getCompileContext().getAsOperand();
    }
    
    public StdScope getScope() {
        return this.scope;
    }
    
    @Nonnull
    public StdScope createScope() {
        return StdScope.copyOf(this.scope);
    }
    
    public void validateNoDuplicateParameters(@Nonnull final BuilderParameters other) {
        other.parameters.keySet().forEach(key -> {
            if (this.parameters.containsKey(key)) {
                throw new IllegalStateException("Parameter '" + key + "' in 'Parameters' block hides parameter from parent scope");
            }
        });
    }
    
    public String getFileName() {
        return this.fileName;
    }
    
    public IntSet getDependencies() {
        return this.dependencies;
    }
    
    public String getInterfaceCode() {
        return this.interfaceCode;
    }
    
    public void addDependency(final int d) {
        this.dependencies.add(d);
    }
    
    @Nonnull
    public static ObjectSchema toSchema(@Nonnull final SchemaContext context) {
        final ObjectSchema schema = new ObjectSchema();
        schema.setTitle("Parameters");
        schema.setProperties((Map<String, Schema>)Map.of("_ImportStates", new ArraySchema(new StringSchema())));
        schema.setAdditionalProperties(Parameter.toSchema(context));
        return schema;
    }
    
    public static class Parameter
    {
        public static final String KEY_VALUE = "Value";
        public static final String KEY_TYPE_HINT = "TypeHint";
        public static final String KEY_VALIDATE = "Validate";
        public static final String KEY_CONFINE = "Confine";
        public static final String KEY_DESCRIPTION = "Description";
        public static final String KEY_PRIVATE = "Private";
        private final BuilderExpression expression;
        private final String description;
        private final String code;
        private List<ExecutionContext.Instruction> instructionList;
        private final boolean isValidation;
        private final boolean isPrivate;
        
        public Parameter(final BuilderExpression expression, final String description, final String code, final boolean isValidation, final boolean isPrivate) {
            this.expression = expression;
            this.description = description;
            this.code = code;
            this.isValidation = isValidation;
            this.isPrivate = isPrivate;
        }
        
        public BuilderExpression getExpression() {
            return this.expression;
        }
        
        public String getDescription() {
            return this.description;
        }
        
        public boolean isValidation() {
            return this.isValidation;
        }
        
        public boolean isPrivate() {
            return this.isPrivate;
        }
        
        @Nonnull
        public static ObjectSchema toSchema(@Nonnull final SchemaContext context) {
            final ObjectSchema props = new ObjectSchema();
            props.setTitle("Parameter");
            final LinkedHashMap<String, Schema> map = new LinkedHashMap<String, Schema>();
            map.put("Value", BuilderExpression.toSchema(context));
            map.put("TypeHint", new StringSchema());
            map.put("Validate", new StringSchema());
            map.put("Confine", new StringSchema());
            map.put("Description", new StringSchema());
            map.put("Private", new BooleanSchema());
            props.setProperties(map);
            return props;
        }
        
        @Nonnull
        private static Parameter fromJSON(@Nonnull final JsonElement element, @Nonnull final BuilderParameters builderParameters, final String parameterName) {
            final JsonObject jsonObject = BuilderBase.expectObject(element);
            BuilderExpression expression = BuilderExpression.fromJSON(BuilderBase.expectKey(jsonObject, "Value"), builderParameters, true);
            if (expression instanceof BuilderExpressionStaticEmptyArray) {
                if (!jsonObject.has("TypeHint")) {
                    throw new IllegalStateException("TypeHint missing for parameter " + parameterName);
                }
                final String type = BuilderBase.readString(jsonObject, "TypeHint");
                if ("STRING".equalsIgnoreCase(type)) {
                    expression = BuilderExpressionStaticStringArray.INSTANCE_EMPTY;
                }
                else if ("NUMBER".equalsIgnoreCase(type)) {
                    expression = BuilderExpressionStaticNumberArray.INSTANCE_EMPTY;
                }
                else {
                    if (!"BOOLEAN".equalsIgnoreCase(type)) {
                        throw new IllegalStateException("TypeHint must be one of STRING, NUMBER, BOOLEAN for parameter " + parameterName);
                    }
                    expression = BuilderExpressionStaticBooleanArray.INSTANCE_EMPTY;
                }
            }
            final String validate = BuilderBase.readString(jsonObject, "Validate", null);
            final String confine = BuilderBase.readString(jsonObject, "Confine", null);
            final boolean hasValidate = validate != null;
            if (hasValidate && confine != null) {
                throw new IllegalStateException("Only either 'Confine' or 'Validate' allowed for parameter " + parameterName);
            }
            final String code = hasValidate ? validate : confine;
            return new Parameter(expression, BuilderBase.readString(jsonObject, "Description", null), code, hasValidate, BuilderBase.readBoolean(jsonObject, "Private", false));
        }
    }
}
