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

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

import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.protocol.ComponentUpdateType;
import com.hypixel.hytale.protocol.ComponentUpdate;
import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems;
import com.hypixel.hytale.server.core.modules.entity.component.Interactable;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.entity.group.EntityGroup;
import com.hypixel.hytale.server.flock.FlockPlugin;
import java.util.logging.Level;
import com.hypixel.hytale.server.npc.NPCPlugin;
import java.util.Iterator;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSettings;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.component.ComponentAccessor;
import java.util.HashMap;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.role.builders.BuilderRole;
import com.hypixel.hytale.server.npc.statetransition.StateTransitionController;
import java.util.Map;
import com.hypixel.hytale.component.Ref;
import java.util.Set;
import java.util.HashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.BitSet;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import com.hypixel.hytale.server.npc.asset.builder.StateMappingHelper;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentType;

public class StateSupport
{
    public static final int NO_STATE = Integer.MIN_VALUE;
    @Nullable
    protected static final ComponentType<EntityStore, NPCEntity> NPC_COMPONENT_TYPE;
    protected final StateMappingHelper stateHelper;
    protected final int startState;
    protected final int startSubState;
    protected int state;
    protected int subState;
    protected Int2IntMap componentLocalStateMachines;
    protected BitSet localStateMachineAutoResetStates;
    protected final Int2ObjectMap<IntSet> busyStates;
    protected final HashSet<String> missingStates;
    protected boolean needClearOnce;
    protected Set<Ref<EntityStore>> interactablePlayers;
    protected Set<Ref<EntityStore>> interactedPlayers;
    protected Map<Ref<EntityStore>, String> contextualInteractions;
    @Nullable
    protected Ref<EntityStore> interactionIterationTarget;
    @Nullable
    protected final StateTransitionController stateTransitionController;
    
    public StateSupport(@Nonnull final BuilderRole builder, @Nonnull final BuilderSupport support) {
        this.missingStates = new HashSet<String>();
        this.stateHelper = builder.getStateMappingHelper();
        this.busyStates = builder.getBusyStates();
        this.stateTransitionController = builder.getStateTransitionController(support);
        this.startState = builder.getStartStateIndex();
        this.startSubState = builder.getStartSubStateIndex();
    }
    
    @Nullable
    public StateTransitionController getStateTransitionController() {
        return this.stateTransitionController;
    }
    
    public StateMappingHelper getStateHelper() {
        return this.stateHelper;
    }
    
    public void postRoleBuilt(@Nonnull final BuilderSupport builderSupport) {
        if (builderSupport.hasComponentLocalStateMachines()) {
            this.componentLocalStateMachines = builderSupport.getComponentLocalStateMachines();
            this.localStateMachineAutoResetStates = builderSupport.getLocalStateMachineAutoResetStates();
        }
        if (builderSupport.isTrackInteractions()) {
            this.interactedPlayers = new HashSet<Ref<EntityStore>>();
            this.interactablePlayers = new HashSet<Ref<EntityStore>>();
            this.contextualInteractions = new HashMap<Ref<EntityStore>, String>();
        }
        if (this.busyStates != null) {
            final String defaultSubState = this.stateHelper.getDefaultSubState();
            this.busyStates.forEach((key, value) -> {
                final int defaultSubStateIndex = this.stateHelper.getSubStateIndex(key, defaultSubState);
                if (value.contains(defaultSubStateIndex) && value.size() == 1) {
                    for (int maxIndex = this.stateHelper.getHighestSubStateIndex(key), i = 0; i <= maxIndex; ++i) {
                        value.add(i);
                    }
                }
            });
        }
    }
    
    public void update(@Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (this.contextualInteractions != null) {
            this.contextualInteractions.clear();
        }
        if (this.interactablePlayers != null) {
            final Iterator<Ref<EntityStore>> it = this.interactablePlayers.iterator();
            while (it.hasNext()) {
                final Ref<EntityStore> ref = it.next();
                if (!ref.isValid()) {
                    continue;
                }
                final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
                if (playerComponent == null) {
                    it.remove();
                }
                else {
                    if (playerComponent.getGameMode() != GameMode.Creative) {
                        continue;
                    }
                    final PlayerSettings playerSettingsComponent = componentAccessor.getComponent(ref, PlayerSettings.getComponentType());
                    if (playerSettingsComponent != null && playerSettingsComponent.creativeSettings().allowNPCDetection()) {
                        continue;
                    }
                    it.remove();
                }
            }
        }
    }
    
    public boolean pollNeedClearOnce() {
        final boolean ret = this.needClearOnce;
        this.needClearOnce = false;
        return ret;
    }
    
    public boolean inState(final int state) {
        return this.state == state;
    }
    
    public boolean inSubState(final int subState) {
        return this.subState == subState;
    }
    
    public boolean inState(final int state, final int subState) {
        return this.inState(state) && (subState == Integer.MIN_VALUE || this.inSubState(subState));
    }
    
    public boolean inState(final String state, final String subState) {
        final int stateIndex = this.stateHelper.getStateIndex(state);
        if (stateIndex < 0) {
            return false;
        }
        final int subStateIndex = this.stateHelper.getSubStateIndex(stateIndex, subState);
        return subStateIndex >= 0 && this.inState(stateIndex, subStateIndex);
    }
    
    @Nonnull
    public String getStateName() {
        return this.getStateName(this.state, this.subState);
    }
    
    @Nonnull
    public String getStateName(final int state, final int subState) {
        return this.stateHelper.getStateName(state) + "." + this.stateHelper.getSubStateName(state, subState);
    }
    
    public int getStateIndex() {
        return this.state;
    }
    
    public int getSubStateIndex() {
        return this.subState;
    }
    
    public void appendStateName(@Nonnull final StringBuilder builder) {
        builder.append(this.stateHelper.getStateName(this.state)).append('.').append(this.stateHelper.getSubStateName(this.state, this.subState));
    }
    
    public void setState(final int state, final int subState, final boolean clearOnce, final boolean skipTransition) {
        final int oldState = this.state;
        this.state = state;
        this.subState = subState;
        if (clearOnce) {
            this.needClearOnce = true;
        }
        if (!skipTransition && oldState != state && this.stateTransitionController != null) {
            this.stateTransitionController.initiateStateTransition(oldState, state);
        }
    }
    
    public void setState(@Nonnull final Ref<EntityStore> ref, @Nonnull final String state, @Nullable String subState, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final int index = this.stateHelper.getStateIndex(state);
        if (index >= 0) {
            if (subState == null) {
                subState = this.stateHelper.getDefaultSubState();
            }
            final int subStateIndex = this.stateHelper.getSubStateIndex(index, subState);
            if (subStateIndex >= 0) {
                this.setState(index, subStateIndex, true, false);
                return;
            }
        }
        if (!this.missingStates.add(state)) {
            final NPCEntity npcComponent = componentAccessor.getComponent(ref, NPCEntity.getComponentType());
            assert npcComponent != null;
            NPCPlugin.get().getLogger().at(Level.WARNING).log("State '%s.%s' in '%s' does not exist and was set by an external call", state, subState, npcComponent.getRoleName());
        }
    }
    
    public void setSubState(final String subState) {
        final int subStateIndex = this.stateHelper.getSubStateIndex(this.state, subState);
        if (subStateIndex >= 0) {
            this.setState(this.state, subStateIndex, true, true);
        }
    }
    
    public boolean isComponentInState(final int componentIndex, final int targetState) {
        final int state = this.componentLocalStateMachines.get(componentIndex);
        if (state == Integer.MIN_VALUE) {
            throw new IllegalArgumentException("Querying for a component index that doesn't exist");
        }
        return state == targetState;
    }
    
    public void setComponentState(final int componentIndex, final int targetState) {
        this.componentLocalStateMachines.put(componentIndex, targetState);
    }
    
    public void resetLocalStateMachines() {
        if (this.localStateMachineAutoResetStates == null) {
            return;
        }
        for (int i = this.localStateMachineAutoResetStates.nextSetBit(0); i >= 0; i = this.localStateMachineAutoResetStates.nextSetBit(i + 1)) {
            this.componentLocalStateMachines.put(i, 0);
            if (i == Integer.MAX_VALUE) {
                break;
            }
        }
    }
    
    public void flockSetState(final Ref<EntityStore> ref, @Nonnull final String state, @Nullable final String subState, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final Ref<EntityStore> flockReference = FlockPlugin.getFlockReference(ref, componentAccessor);
        if (flockReference == null) {
            return;
        }
        final EntityGroup entityGroupComponent = componentAccessor.getComponent(flockReference, EntityGroup.getComponentType());
        assert entityGroupComponent != null;
        entityGroupComponent.forEachMemberExcludingSelf((member, sender, _state, _substate) -> {
            final Store<EntityStore> memberStore = member.getStore();
            final NPCEntity npcComponent = memberStore.getComponent(member, StateSupport.NPC_COMPONENT_TYPE);
            if (npcComponent != null) {
                npcComponent.onFlockSetState(member, _state, _substate, memberStore);
            }
        }, ref, state, subState);
    }
    
    public boolean isInBusyState() {
        if (this.busyStates == null) {
            return false;
        }
        final IntSet busySubStates = this.busyStates.get(this.state);
        return busySubStates != null && busySubStates.contains(this.subState);
    }
    
    public void addContextualInteraction(@Nonnull final Ref<EntityStore> playerRef, @Nonnull final String context) {
        if (this.contextualInteractions != null) {
            this.contextualInteractions.put(playerRef, context);
        }
    }
    
    public boolean hasContextualInteraction(@Nonnull final Ref<EntityStore> playerReference, @Nonnull final String context) {
        final String contextualInteraction = this.contextualInteractions.get(playerReference);
        return contextualInteraction != null && contextualInteraction.equals(context);
    }
    
    public void addInteraction(@Nonnull final Player player) {
        this.interactedPlayers.add(player.getReference());
    }
    
    public boolean consumeInteraction(@Nonnull final Ref<EntityStore> playerReference) {
        return this.interactedPlayers.remove(playerReference);
    }
    
    public void setInteractable(@Nonnull final Ref<EntityStore> playerReference, final boolean interactable) {
        if (interactable) {
            this.interactablePlayers.add(playerReference);
        }
        else {
            this.interactablePlayers.remove(playerReference);
        }
    }
    
    public void setInteractable(@Nonnull final Ref<EntityStore> entityRef, @Nonnull final Ref<EntityStore> playerReference, final boolean interactable, @Nullable final String hint, final boolean showPrompt, @Nonnull final Store<EntityStore> store) {
        final boolean wasInteractable = this.interactablePlayers.contains(playerReference);
        if (interactable) {
            this.interactablePlayers.add(playerReference);
        }
        else {
            this.interactablePlayers.remove(playerReference);
        }
        if (showPrompt) {
            final boolean hasComponent = store.getArchetype(entityRef).contains(Interactable.getComponentType());
            if (interactable) {
                boolean needsHint = !wasInteractable && hint != null;
                if (!hasComponent) {
                    store.ensureComponent(entityRef, Interactable.getComponentType());
                    needsHint = (hint != null);
                }
                if (needsHint) {
                    this.sendInteractionHintToPlayer(entityRef, playerReference, hint, store);
                }
            }
            else if (hasComponent && this.interactablePlayers.isEmpty()) {
                store.removeComponent(entityRef, Interactable.getComponentType());
            }
        }
    }
    
    private void sendInteractionHintToPlayer(@Nonnull final Ref<EntityStore> entityRef, @Nonnull final Ref<EntityStore> playerReference, @Nonnull final String hint, @Nonnull final Store<EntityStore> store) {
        final EntityTrackerSystems.EntityViewer viewerComponent = store.getComponent(playerReference, EntityTrackerSystems.EntityViewer.getComponentType());
        if (viewerComponent == null || !viewerComponent.visible.contains(entityRef)) {
            return;
        }
        final ComponentUpdate update = new ComponentUpdate();
        update.type = ComponentUpdateType.Interactable;
        update.interactionHint = hint;
        viewerComponent.queueUpdate(entityRef, update);
    }
    
    public void setInteractionIterationTarget(@Nullable final Ref<EntityStore> playerReference) {
        this.interactionIterationTarget = playerReference;
    }
    
    @Nullable
    public Ref<EntityStore> getInteractionIterationTarget() {
        return this.interactionIterationTarget;
    }
    
    public boolean willInteractWith(@Nonnull final Ref<EntityStore> playerReference) {
        return this.interactablePlayers != null && this.interactablePlayers.contains(playerReference) && !this.isInBusyState();
    }
    
    public boolean runTransitionActions(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, final double dt, @Nonnull final Store<EntityStore> store) {
        return this.stateTransitionController != null && this.stateTransitionController.runTransitionActions(ref, role, dt, store);
    }
    
    public boolean isRunningTransitionActions() {
        return this.stateTransitionController != null && this.stateTransitionController.isRunningTransitionActions();
    }
    
    public void activate() {
        this.setState(this.startState, this.startSubState, true, true);
    }
    
    static {
        NPC_COMPONENT_TYPE = NPCEntity.getComponentType();
    }
}
