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

package com.hypixel.hytale.server.npc.role.builders;

import com.hypixel.hytale.function.function.TriToIntFunction;
import com.hypixel.hytale.function.function.TriFunction;
import java.util.function.Supplier;
import java.util.function.BiFunction;
import com.hypixel.hytale.logger.sentry.SkipSentryException;
import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState;
import com.hypixel.hytale.server.spawning.SpawnTestResult;
import com.hypixel.hytale.server.spawning.SpawningContext;
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
import java.util.Objects;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.codec.schema.config.StringSchema;
import com.google.gson.JsonElement;
import com.hypixel.hytale.server.npc.asset.builder.BuilderParameters;
import java.util.Map;
import java.util.logging.Level;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
import java.util.List;
import com.hypixel.hytale.server.npc.util.expression.Scope;
import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
import com.hypixel.hytale.server.npc.validators.NPCLoadTimeValidationHelper;
import com.hypixel.hytale.server.npc.asset.builder.StateMappingHelper;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import com.hypixel.hytale.server.npc.asset.builder.BuilderModifier;
import com.hypixel.hytale.server.npc.asset.builder.holder.StringHolder;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.asset.builder.SpawnableWithModelBuilder;

public class BuilderRoleVariant extends SpawnableWithModelBuilder<Role>
{
    protected final StringHolder reference;
    protected int referenceIndex;
    protected BuilderModifier modifier;
    
    public BuilderRoleVariant() {
        this.reference = new StringHolder();
    }
    
    @Nullable
    @Override
    public Role build(@Nonnull final BuilderSupport builderSupport) {
        return this.executeOnSuperRole(builderSupport, Builder::build, () -> null);
    }
    
    @Override
    public StateMappingHelper getStateMappingHelper() {
        final Builder<Object> parentBuilder = this.builderManager.getCachedBuilder(this.referenceIndex, Role.class);
        return parentBuilder.getStateMappingHelper();
    }
    
    @Override
    public boolean validate(final String configName, @Nonnull final NPCLoadTimeValidationHelper validationHelper, @Nonnull final ExecutionContext context, final Scope globalScope, final List<String> errors) {
        final Builder<Object> roleBuilder = this.builderManager.getCachedBuilder(this.referenceIndex, Role.class);
        if (!(roleBuilder instanceof ISpawnableWithModel)) {
            NPCPlugin.get().getLogger().at(Level.SEVERE).log("Variant %s is of a non-spawnable template!", configName);
            return false;
        }
        validationHelper.setIsVariant();
        final String combatConfig = this.modifier.getCombatConfig();
        if (combatConfig != null && context.getCombatConfig() == null) {
            context.setCombatConfig(combatConfig);
        }
        final Map<String, String> interactionVars = this.modifier.getInteractionVars();
        if (interactionVars != null && context.getInteractionVars() == null) {
            context.setInteractionVars(interactionVars);
        }
        final BuilderParameters builderParameters = roleBuilder.getBuilderParameters();
        final Scope newScope = this.modifier.createScope(context, builderParameters, null);
        final Scope oldScope = context.setScope(newScope);
        final boolean result = roleBuilder.validate(configName, validationHelper, context, context.getScope(), errors);
        context.setScope(oldScope);
        return result;
    }
    
    @Nonnull
    @Override
    public Builder<Role> readConfig(@Nonnull final JsonElement data) {
        if (this.isCreatingSchema()) {
            final Map<String, Schema> props = this.builderSchema.getProperties();
            final StringSchema schema = new StringSchema();
            schema.setHytaleAssetRef("NPCRole");
            props.put("Reference", schema);
            props.put("Modify", BuilderModifier.toSchema(this.builderSchemaContext));
            return this;
        }
        if (!this.isCreatingDescriptor()) {
            BuilderModifier.readModifierObject(this.expectJsonObject(data, this.getLabel()), this.getBuilderParameters(), this.reference, holder -> {}, modifier -> this.modifier = modifier, this.stateHelper, this.extraInfo);
            if (!this.reference.isStatic()) {
                throw new IllegalStateException("Computable component references are not supported for Role Variants");
            }
            this.referenceIndex = this.getBuilderManager().getOrCreateIndex(this.reference.get(null));
            this.builderParameters.addDependency(this.referenceIndex);
        }
        this.ignoreAttribute("Reference");
        this.ignoreAttribute("Modify");
        this.ignoreAttribute("$Label");
        return this;
    }
    
    @Nonnull
    @Override
    public Class<Role> category() {
        return Role.class;
    }
    
    @Nonnull
    @Override
    public String getIdentifier() {
        final BuilderInfo builderInfo = NPCPlugin.get().getBuilderInfo(this);
        Objects.requireNonNull(builderInfo, "Have builder but can't get builderInfo for it");
        return builderInfo.getKeyName();
    }
    
    @Nonnull
    @Override
    public SpawnTestResult canSpawn(@Nonnull final SpawningContext spawningContext) {
        return this.executeOnSuperRole(spawningContext, (roleBuilder, _context) -> ((ISpawnableWithModel)roleBuilder).canSpawn(_context), () -> SpawnTestResult.FAIL_NOT_SPAWNABLE);
    }
    
    @Nullable
    @Override
    public String getSpawnModelName(@Nonnull final ExecutionContext context, final Scope modifierScope) {
        return this.executeOnSuperRole(context, modifierScope, (roleBuilder, _context, _modifierScope) -> ((ISpawnableWithModel)roleBuilder).getSpawnModelName(_context, _modifierScope), () -> null);
    }
    
    @Override
    public Scope createModifierScope(@Nonnull final ExecutionContext executionContext) {
        final Scope originalScope = executionContext.getScope();
        Builder<Role> roleBuilder = this;
        Scope scope = null;
        do {
            final BuilderRoleVariant variantBuilder = (BuilderRoleVariant)roleBuilder;
            roleBuilder = this.builderManager.getCachedBuilder(variantBuilder.referenceIndex, Role.class);
            if (!(roleBuilder instanceof ISpawnableWithModel)) {
                throw new IllegalStateException("Cannot instantiate a variant of something that isn't a spawnable role!");
            }
            scope = variantBuilder.modifier.createScope(executionContext, roleBuilder.getBuilderParameters(), scope);
            executionContext.setScope(scope);
        } while (roleBuilder instanceof BuilderRoleVariant);
        executionContext.setScope(originalScope);
        return scope;
    }
    
    @Nonnull
    @Override
    public Scope createExecutionScope() {
        return this.getBuilderParameters().createScope();
    }
    
    @Override
    public void markNeedsReload() {
        NPCPlugin.get().setRoleBuilderNeedsReload(this);
    }
    
    @Nonnull
    @Override
    public String getShortDescription() {
        return "Create a variant from an existing NPC JSON file";
    }
    
    @Nonnull
    @Override
    public String getLongDescription() {
        return "Create a variant from an existing NPC JSON file";
    }
    
    @Nonnull
    @Override
    public BuilderDescriptorState getBuilderDescriptorState() {
        return BuilderDescriptorState.WorkInProgress;
    }
    
    @Override
    public final boolean isEnabled(final ExecutionContext context) {
        return true;
    }
    
    public int getReferenceIndex() {
        return this.referenceIndex;
    }
    
    @Override
    public boolean isMemory(@Nonnull final ExecutionContext context, final Scope modifierScope) {
        final Boolean result = this.executeOnSuperRole(context, modifierScope, (roleBuilder, _context, _modifierScope) -> ((ISpawnableWithModel)roleBuilder).isMemory(_context, _modifierScope), () -> null);
        return result != null && result;
    }
    
    @Nullable
    @Override
    public String getMemoriesCategory(@Nonnull final ExecutionContext context, final Scope modifierScope) {
        return this.executeOnSuperRole(context, modifierScope, (roleBuilder, _context, _modifierScope) -> ((ISpawnableWithModel)roleBuilder).getMemoriesCategory(_context, _modifierScope), () -> null);
    }
    
    @Nullable
    @Override
    public String getMemoriesNameOverride(@Nonnull final ExecutionContext context, final Scope modifierScope) {
        return this.executeOnSuperRole(context, modifierScope, (roleBuilder, _context, _modifierScope) -> ((ISpawnableWithModel)roleBuilder).getMemoriesNameOverride(_context, _modifierScope), () -> null);
    }
    
    @Nonnull
    @Override
    public String getNameTranslationKey(final ExecutionContext context, final Scope modifierScope) {
        return this.executeOnSuperRole(context, modifierScope, (roleBuilder, _context, _modifierScope) -> ((ISpawnableWithModel)roleBuilder).getNameTranslationKey(_context, _modifierScope), () -> {
            new SkipSentryException(new IllegalStateException("Failed to get translation key for role!"));
            throw;
        });
    }
    
    protected <V> V executeOnSuperRole(@Nonnull final BuilderSupport builderSupport, @Nonnull final BiFunction<Builder<Role>, BuilderSupport, V> func, @Nonnull final Supplier<V> failed) {
        final Builder<Role> roleBuilder = builderSupport.getBuilderManager().getCachedBuilder(this.referenceIndex, Role.class);
        if (!(roleBuilder instanceof ISpawnableWithModel)) {
            return failed.get();
        }
        final BuilderParameters builderParameters = roleBuilder.getBuilderParameters();
        final Scope newScope = this.modifier.createScope(builderSupport, builderParameters, null);
        final ExecutionContext context = builderSupport.getExecutionContext();
        final String combatConfig = this.modifier.getCombatConfig();
        if (combatConfig != null && context.getCombatConfig() == null) {
            context.setCombatConfig(combatConfig);
        }
        final Map<String, String> interactionVars = this.modifier.getInteractionVars();
        if (interactionVars != null && context.getInteractionVars() == null) {
            context.setInteractionVars(interactionVars);
        }
        final Scope oldScope = context.setScope(newScope);
        builderSupport.setGlobalScope(newScope);
        final V v = func.apply(roleBuilder, builderSupport);
        context.setScope(oldScope);
        return v;
    }
    
    protected <V> V executeOnSuperRole(@Nonnull final SpawningContext spawningContext, @Nonnull final BiFunction<Builder<Role>, SpawningContext, V> func, @Nonnull final Supplier<V> failed) {
        final Builder<Role> roleBuilder = this.builderManager.getCachedBuilder(this.referenceIndex, Role.class);
        if (!(roleBuilder instanceof ISpawnableWithModel)) {
            return failed.get();
        }
        final ExecutionContext executionContext = spawningContext.getExecutionContext();
        final Scope oldScope = executionContext.setScope(spawningContext.getModifierScope());
        final V v = func.apply(roleBuilder, spawningContext);
        executionContext.setScope(oldScope);
        return v;
    }
    
    protected <V> V executeOnSuperRole(@Nonnull final ExecutionContext context, final Scope modifierScope, @Nonnull final TriFunction<Builder<Role>, ExecutionContext, Scope, V> func, @Nonnull final Supplier<V> failed) {
        final Builder<Role> roleBuilder = this.builderManager.getCachedBuilder(this.referenceIndex, Role.class);
        if (!(roleBuilder instanceof ISpawnableWithModel)) {
            return failed.get();
        }
        final Scope oldScope = context.setScope(modifierScope);
        final V v = func.apply(roleBuilder, context, modifierScope);
        context.setScope(oldScope);
        return v;
    }
    
    protected int executeOnSuperRole(@Nonnull final ExecutionContext context, final Scope modifierScope, @Nonnull final TriToIntFunction<Builder<Role>, ExecutionContext, Scope> func, final int failed) {
        final Builder<Role> roleBuilder = this.builderManager.getCachedBuilder(this.referenceIndex, Role.class);
        if (!(roleBuilder instanceof ISpawnableWithModel)) {
            return failed;
        }
        final Scope oldScope = context.setScope(modifierScope);
        final int v = func.apply(roleBuilder, context, modifierScope);
        context.setScope(oldScope);
        return v;
    }
}
