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

package com.hypixel.hytale.builtin.npccombatactionevaluator;

import com.hypixel.hytale.component.Component;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.server.npc.decisionmaker.core.EvaluationContext;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import com.hypixel.hytale.server.npc.role.support.StateSupport;
import com.hypixel.hytale.server.npc.decisionmaker.core.Evaluator;
import java.util.List;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.builtin.npccombatactionevaluator.evaluator.CombatActionEvaluatorConfig;
import java.util.Map;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import java.util.function.Function;
import com.hypixel.hytale.math.random.RandomExtra;
import com.hypixel.hytale.builtin.npccombatactionevaluator.evaluator.combatactions.CombatActionOption;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.builtin.npccombatactionevaluator.memory.TargetMemorySystems;
import com.hypixel.hytale.server.npc.systems.RoleSystems;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.npc.valuestore.ValueStore;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.builtin.npccombatactionevaluator.memory.DamageMemory;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.server.npc.role.support.PositionCache;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.core.modules.interaction.InteractionModule;
import com.hypixel.hytale.builtin.npccombatactionevaluator.memory.TargetMemory;
import java.util.Objects;
import com.hypixel.hytale.builtin.npccombatactionevaluator.evaluator.CombatActionEvaluator;
import com.hypixel.hytale.builtin.npccombatactionevaluator.config.CombatBalanceAsset;
import com.hypixel.hytale.server.npc.config.balancing.BalanceAsset;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.server.npc.systems.BalancingInitialisationSystem;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.query.Query;
import javax.annotation.Nonnull;
import com.hypixel.hytale.component.dependency.Dependency;
import java.util.Set;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.logger.HytaleLogger;

public class CombatActionEvaluatorSystems
{
    private static final HytaleLogger LOGGER;
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
    }
    
    public static class OnAdded extends HolderSystem<EntityStore>
    {
        @Nullable
        private final ComponentType<EntityStore, NPCEntity> componentType;
        private final ComponentType<EntityStore, CombatConstructionData> combatConstructionDataComponentType;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        @Nonnull
        private final Query<EntityStore> query;
        
        public OnAdded(final ComponentType<EntityStore, CombatConstructionData> combatConstructionDataComponentType) {
            this.componentType = NPCEntity.getComponentType();
            this.combatConstructionDataComponentType = combatConstructionDataComponentType;
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, BalancingInitialisationSystem.class));
            this.query = (Query<EntityStore>)Query.and(combatConstructionDataComponentType, combatConstructionDataComponentType);
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            final Role role = holder.getComponent(this.componentType).getRole();
            if (role.getBalanceAsset() == null) {
                return;
            }
            final BalanceAsset balancingAsset = BalanceAsset.getAssetMap().getAsset(role.getBalanceAsset());
            if (balancingAsset instanceof final CombatBalanceAsset combatBalance) {
                final CombatConstructionData constructionData = holder.getComponent(this.combatConstructionDataComponentType);
                final CombatActionEvaluator evaluator = new CombatActionEvaluator(role, combatBalance.getEvaluatorConfig(), constructionData);
                evaluator.setupNPC(holder);
                final PositionCache positionCache = role.getPositionCache();
                final CombatActionEvaluator obj = evaluator;
                Objects.requireNonNull(obj);
                positionCache.addExternalPositionCacheRegistration(obj::setupNPC);
                holder.putComponent(TargetMemory.getComponentType(), new TargetMemory(combatBalance.getTargetMemoryDuration()));
                holder.putComponent(CombatActionEvaluator.getComponentType(), evaluator);
                holder.ensureComponent(InteractionModule.get().getChainingDataComponent());
                holder.removeComponent(this.combatConstructionDataComponentType);
            }
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
    }
    
    public static class EvaluatorTick extends EntityTickingSystem<EntityStore>
    {
        private final ComponentType<EntityStore, CombatActionEvaluator> componentType;
        private final ComponentType<EntityStore, TargetMemory> targetMemoryComponentType;
        private final ComponentType<EntityStore, DamageMemory> damageMemoryComponentType;
        @Nullable
        private final ComponentType<EntityStore, NPCEntity> npcComponentType;
        @Nonnull
        private final ComponentType<EntityStore, Player> playerComponentType;
        private final ComponentType<EntityStore, ValueStore> valueStoreComponentType;
        private final ComponentType<EntityStore, TransformComponent> transformComponentType;
        private final Query<EntityStore> query;
        @Nonnull
        private final Set<Dependency<EntityStore>> dependencies;
        
        public EvaluatorTick(final ComponentType<EntityStore, CombatActionEvaluator> componentType, final ComponentType<EntityStore, TargetMemory> targetMemoryComponentType, final ComponentType<EntityStore, DamageMemory> damageMemoryComponentType) {
            this.componentType = componentType;
            this.targetMemoryComponentType = targetMemoryComponentType;
            this.damageMemoryComponentType = damageMemoryComponentType;
            this.npcComponentType = NPCEntity.getComponentType();
            this.playerComponentType = Player.getComponentType();
            this.valueStoreComponentType = ValueStore.getComponentType();
            this.transformComponentType = TransformComponent.getComponentType();
            this.query = (Query<EntityStore>)Archetype.of(componentType, targetMemoryComponentType, this.npcComponentType, this.valueStoreComponentType, this.transformComponentType);
            this.dependencies = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.BEFORE, RoleSystems.PreBehaviourSupportTickSystem.class), new SystemDependency(Order.AFTER, TargetMemorySystems.Ticking.class));
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return this.dependencies;
        }
        
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Override
        public boolean isParallel(final int archetypeChunkSize, final int taskCount) {
            return false;
        }
        
        @Override
        public void tick(final float dt, final int index, @Nonnull final ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            final NPCEntity npcComponent = archetypeChunk.getComponent(index, this.npcComponentType);
            assert npcComponent != null;
            final Role role = npcComponent.getRole();
            if (archetypeChunk.getArchetype().contains(DeathComponent.getComponentType())) {
                return;
            }
            final CombatActionEvaluator evaluatorComponent = archetypeChunk.getComponent(index, this.componentType);
            assert evaluatorComponent != null;
            evaluatorComponent.tickBasicAttackCoolDown(dt);
            final StateSupport stateSupport = role.getStateSupport();
            final int currentState = stateSupport.getStateIndex();
            if (currentState != evaluatorComponent.getRunInState()) {
                if (evaluatorComponent.getCurrentAction() != null) {
                    evaluatorComponent.completeCurrentAction(true, true);
                    evaluatorComponent.clearPrimaryTarget();
                    role.getMarkedEntitySupport().clearMarkedEntity(evaluatorComponent.getMarkedTargetSlot());
                    final HytaleLogger.Api context = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
                    if (context.isEnabled()) {
                        context.log("%s: Leaving combat", archetypeChunk.getReferenceTo(index));
                    }
                }
                final DamageMemory damageMemory = archetypeChunk.getComponent(index, this.damageMemoryComponentType);
                if (damageMemory != null) {
                    damageMemory.clearTotalDamage();
                }
                return;
            }
            if (role.getCombatSupport().isExecutingAttack()) {
                return;
            }
            final ValueStore valueStoreComponent = archetypeChunk.getComponent(index, this.valueStoreComponentType);
            assert valueStoreComponent != null;
            final double[] postExecutionDistanceRange = evaluatorComponent.consumePostExecutionDistanceRange();
            if (postExecutionDistanceRange != null) {
                valueStoreComponent.storeDouble(evaluatorComponent.getMinRangeSlot(), postExecutionDistanceRange[0]);
                valueStoreComponent.storeDouble(evaluatorComponent.getMaxRangeSlot(), postExecutionDistanceRange[1]);
            }
            final int currentSubState = stateSupport.getSubStateIndex();
            final CombatActionEvaluatorConfig.BasicAttacks basicAttacks = evaluatorComponent.getBasicAttacks(currentSubState);
            if (basicAttacks != null) {
                evaluatorComponent.setCurrentBasicAttackSet(currentSubState, basicAttacks);
                final String currentBasicAttack = evaluatorComponent.getCurrentBasicAttack();
                if (currentBasicAttack != null) {
                    if (!evaluatorComponent.tickBasicAttackTimeout(dt)) {
                        role.getMarkedEntitySupport().setMarkedEntity(evaluatorComponent.getMarkedTargetSlot(), evaluatorComponent.getBasicAttackTarget());
                        return;
                    }
                    evaluatorComponent.clearCurrentBasicAttack();
                    final HytaleLogger.Api context2 = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
                    if (context2.isEnabled()) {
                        context2.log("%s: Basic attack timed out", archetypeChunk.getReferenceTo(index));
                    }
                }
                if (evaluatorComponent.canUseBasicAttack(index, archetypeChunk, commandBuffer)) {
                    final MotionController activeMotionController = role.getActiveMotionController();
                    final TransformComponent transformComponent = archetypeChunk.getComponent(index, this.transformComponentType);
                    assert transformComponent != null;
                    final Vector3d position = transformComponent.getPosition();
                    Ref<EntityStore> targetRef = null;
                    final CombatActionEvaluator.CombatOptionHolder currentAction = evaluatorComponent.getCurrentAction();
                    if (currentAction == null || currentAction.getOption().getActionTarget() != CombatActionOption.Target.Friendly) {
                        final Ref<EntityStore> primaryTargetRef = evaluatorComponent.getPrimaryTarget();
                        if (primaryTargetRef != null && primaryTargetRef.isValid()) {
                            final TransformComponent targetTransformComponent = commandBuffer.getComponent(primaryTargetRef, this.transformComponentType);
                            assert targetTransformComponent != null;
                            final Vector3d targetPosition = targetTransformComponent.getPosition();
                            if (activeMotionController.getSquaredDistance(position, targetPosition, basicAttacks.shouldUseProjectedDistance()) < basicAttacks.getMaxRangeSquared()) {
                                targetRef = primaryTargetRef;
                            }
                        }
                    }
                    if (targetRef == null) {
                        final TargetMemory targetMemoryComponent = archetypeChunk.getComponent(index, this.targetMemoryComponentType);
                        assert targetMemoryComponent != null;
                        targetRef = targetMemoryComponent.getClosestHostile();
                    }
                    if (targetRef != null) {
                        final TransformComponent targetTransformComponent2 = commandBuffer.getComponent(targetRef, this.transformComponentType);
                        assert targetTransformComponent2 != null;
                        final Vector3d targetPosition2 = targetTransformComponent2.getPosition();
                        if (activeMotionController.getSquaredDistance(position, targetPosition2, basicAttacks.shouldUseProjectedDistance()) < basicAttacks.getMaxRangeSquared()) {
                            evaluatorComponent.setBasicAttackTarget(targetRef);
                            role.getMarkedEntitySupport().setMarkedEntity(evaluatorComponent.getMarkedTargetSlot(), targetRef);
                            final String[] basicAttackOptions = basicAttacks.getAttacks();
                            String attack;
                            if (basicAttacks.isRandom()) {
                                attack = basicAttackOptions[RandomExtra.randomRange(basicAttackOptions.length)];
                            }
                            else {
                                int nextAttackIndex = evaluatorComponent.getNextBasicAttackIndex();
                                attack = basicAttackOptions[nextAttackIndex];
                                if (++nextAttackIndex >= basicAttackOptions.length) {
                                    nextAttackIndex = 0;
                                }
                                evaluatorComponent.setNextBasicAttackIndex(nextAttackIndex);
                            }
                            final CombatActionEvaluator combatActionEvaluator = evaluatorComponent;
                            final String attack2 = attack;
                            final boolean damageFriendlies = basicAttacks.isDamageFriendlies();
                            final CombatActionEvaluatorConfig.BasicAttacks obj = basicAttacks;
                            Objects.requireNonNull(obj);
                            combatActionEvaluator.setCurrentBasicAttack(attack2, damageFriendlies, (Function<InteractionContext, Map<String, String>>)obj::getInteractionVars);
                            evaluatorComponent.setBasicAttackTimeout(basicAttacks.getTimeout());
                            final HytaleLogger.Api context3 = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
                            if (context3.isEnabled()) {
                                context3.log("%s: Started basic attack %s", archetypeChunk.getReferenceTo(index), attack);
                            }
                        }
                    }
                }
            }
            else {
                evaluatorComponent.setCurrentBasicAttackSet(currentSubState, null);
            }
            final CombatActionEvaluator.CombatOptionHolder currentAction2 = evaluatorComponent.getCurrentAction();
            if (currentAction2 != null) {
                if (currentAction2.getOption().getActionTarget() == CombatActionOption.Target.Self) {
                    return;
                }
                if (!evaluatorComponent.hasTimedOut(dt)) {
                    final Ref<EntityStore> targetRef2 = evaluatorComponent.getPrimaryTarget();
                    if (targetRef2 != null && targetRef2.isValid() && !commandBuffer.getArchetype(targetRef2).contains(DeathComponent.getComponentType())) {
                        final Player targetPlayerComponent = commandBuffer.getComponent(targetRef2, this.playerComponentType);
                        if (targetPlayerComponent == null || targetPlayerComponent.getGameMode() == GameMode.Adventure) {
                            role.getMarkedEntitySupport().setMarkedEntity(evaluatorComponent.getMarkedTargetSlot(), targetRef2);
                            return;
                        }
                    }
                }
                evaluatorComponent.terminateCurrentAction();
                evaluatorComponent.clearPrimaryTarget();
                role.getMarkedEntitySupport().clearMarkedEntity(evaluatorComponent.getMarkedTargetSlot());
                final HytaleLogger.Api context2 = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
                if (context2.isEnabled()) {
                    context2.log("%s: Lost current action target or timed out", archetypeChunk.getReferenceTo(index));
                }
            }
            if (!evaluatorComponent.getOptionsBySubState().containsKey(currentSubState)) {
                return;
            }
            final EvaluationContext evaluationContext = evaluatorComponent.getEvaluationContext();
            final double minRunUtility = evaluatorComponent.getMinRunUtility();
            evaluationContext.setMinimumUtility(minRunUtility);
            evaluationContext.setMinimumWeightCoefficient(0.0);
            evaluationContext.setLastUsedNanos(evaluatorComponent.getLastRunNanos());
            final CombatActionEvaluator.RunOption runOption = evaluatorComponent.getRunOption();
            final double utility = runOption.calculateUtility(index, archetypeChunk, evaluatorComponent.getPrimaryTarget(), commandBuffer, evaluationContext);
            evaluationContext.reset();
            if (utility < minRunUtility) {
                return;
            }
            final Int2ObjectMap<List<Evaluator.OptionHolder>> optionLists = evaluatorComponent.getOptionsBySubState();
            final List<Evaluator.OptionHolder> currentStateOptions = optionLists.get(currentSubState);
            evaluatorComponent.setActiveOptions(currentStateOptions);
            evaluatorComponent.selectNextCombatAction(index, archetypeChunk, commandBuffer, role, valueStoreComponent);
            evaluatorComponent.setLastRunNanos(java.lang.System.nanoTime());
            final DamageMemory damageMemory2 = archetypeChunk.getComponent(index, this.damageMemoryComponentType);
            if (damageMemory2 != null) {
                damageMemory2.clearRecentDamage();
            }
            final HytaleLogger.Api context3 = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
            if (context3.isEnabled()) {
                context3.log("%s: Has run the combat action evaluator", archetypeChunk.getReferenceTo(index));
            }
        }
    }
    
    public static class CombatConstructionData implements Component<EntityStore>
    {
        protected String combatState;
        protected int markedTargetSlot;
        protected int minRangeSlot;
        protected int maxRangeSlot;
        protected int positioningAngleSlot;
        
        public static ComponentType<EntityStore, CombatConstructionData> getComponentType() {
            return NPCCombatActionEvaluatorPlugin.get().getCombatConstructionDataComponentType();
        }
        
        public String getCombatState() {
            return this.combatState;
        }
        
        public void setCombatState(final String state) {
            if (this.combatState != null && !this.combatState.equals(state)) {
                throw new IllegalStateException("Cannot have more than one combat state in an NPC!");
            }
            this.combatState = state;
        }
        
        public int getMarkedTargetSlot() {
            return this.markedTargetSlot;
        }
        
        public void setMarkedTargetSlot(final int markedTargetSlot) {
            this.markedTargetSlot = markedTargetSlot;
        }
        
        public int getMinRangeSlot() {
            return this.minRangeSlot;
        }
        
        public void setMinRangeSlot(final int minRangeSlot) {
            this.minRangeSlot = minRangeSlot;
        }
        
        public int getMaxRangeSlot() {
            return this.maxRangeSlot;
        }
        
        public void setMaxRangeSlot(final int maxRangeSlot) {
            this.maxRangeSlot = maxRangeSlot;
        }
        
        public int getPositioningAngleSlot() {
            return this.positioningAngleSlot;
        }
        
        public void setPositioningAngleSlot(final int positioningAngleSlot) {
            this.positioningAngleSlot = positioningAngleSlot;
        }
        
        @Nonnull
        @Override
        public Component<EntityStore> clone() {
            final CombatConstructionData data = new CombatConstructionData();
            data.combatState = this.combatState;
            data.markedTargetSlot = this.markedTargetSlot;
            data.minRangeSlot = this.minRangeSlot;
            data.maxRangeSlot = this.maxRangeSlot;
            data.positioningAngleSlot = this.positioningAngleSlot;
            return data;
        }
    }
}
