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

package com.hypixel.hytale.server.npc.corecomponents.movement;

import com.hypixel.hytale.server.npc.navigation.AStarWithTarget;
import com.hypixel.hytale.server.npc.navigation.AStarNode;
import com.hypixel.hytale.server.npc.navigation.AStarBase;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.npc.movement.steeringforces.SteeringForceWithTarget;
import com.hypixel.hytale.math.random.RandomExtra;
import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath;
import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.npc.movement.Steering;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionFindWithTarget;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionMoveAway;
import com.hypixel.hytale.server.npc.movement.steeringforces.SteeringForceEvade;

public class BodyMotionMoveAway extends BodyMotionFindWithTarget
{
    protected final double stopDistance;
    protected final double stopDistanceSquared;
    protected final double[] holdDirectionDurationRange;
    protected final float changeDirectionViewSector;
    protected final float jitterAngle;
    protected final double erraticDistanceSquared;
    protected final float erraticJitter;
    protected final double erraticChangeDurationMultiplier;
    protected final SteeringForceEvade evade;
    protected float fleeDirection;
    protected double holdDirectionTimeRemaining;
    
    public BodyMotionMoveAway(@Nonnull final BuilderBodyMotionMoveAway builderMotionFind, @Nonnull final BuilderSupport support) {
        super(builderMotionFind, support);
        this.evade = new SteeringForceEvade();
        this.stopDistance = builderMotionFind.getStopDistance(support);
        this.stopDistanceSquared = this.stopDistance * this.stopDistance;
        this.holdDirectionDurationRange = builderMotionFind.getHoldDirectionDurationRange(support);
        this.changeDirectionViewSector = builderMotionFind.getChangeDirectionViewSectorRadians(support);
        this.jitterAngle = builderMotionFind.getDirectionJitterRadians(support);
        final double erraticDistance = builderMotionFind.getErraticDistance(support);
        this.erraticDistanceSquared = erraticDistance * erraticDistance;
        final float erraticExtraJitter = builderMotionFind.getErraticExtraJitterRadians(support);
        this.erraticJitter = MathUtil.clamp(this.jitterAngle + erraticExtraJitter, 0.0f, 3.1415927f);
        this.erraticChangeDurationMultiplier = builderMotionFind.getErraticChangeDurationMultiplier(support);
        this.evade.setDistances(builderMotionFind.getSlowdownDistance(support), this.stopDistance);
        this.evade.setFalloff(builderMotionFind.getFalloff(support));
        this.evade.setAdhereToDirectionHint(true);
    }
    
    @Override
    public void activate(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        super.activate(ref, role, componentAccessor);
        this.holdDirectionTimeRemaining = 0.0;
    }
    
    @Override
    public boolean computeSteering(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nullable final InfoProvider infoProvider, final double dt, @Nonnull final Steering desiredSteering, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final NPCEntity npcComponent = componentAccessor.getComponent(ref, NPCEntity.getComponentType());
        assert npcComponent != null;
        final float speedMultiplier = npcComponent.getCurrentHorizontalSpeedMultiplier(ref, componentAccessor);
        if (speedMultiplier == 0.0f) {
            desiredSteering.clear();
            return true;
        }
        this.holdDirectionTimeRemaining -= dt * speedMultiplier;
        return super.computeSteering(ref, role, infoProvider, dt, desiredSteering, componentAccessor);
    }
    
    @Override
    protected boolean computeSteering(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, final Vector3d position, @Nonnull final Steering desiredSteering, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType());
        assert transformComponent != null;
        final Vector3d selfPosition = transformComponent.getPosition();
        final Vector3f bodyRotation = transformComponent.getRotation();
        final Vector3d lastTargetPosition = this.getLastTargetPosition();
        if (NPCPhysicsMath.inViewSector(selfPosition.x, selfPosition.z, bodyRotation.getYaw(), this.changeDirectionViewSector, lastTargetPosition.x, lastTargetPosition.z)) {
            this.holdDirectionTimeRemaining = 0.0;
        }
        if (this.holdDirectionTimeRemaining <= 0.0) {
            final boolean inErraticRange = selfPosition.distanceSquaredTo(lastTargetPosition) < this.erraticDistanceSquared;
            final float jitter = inErraticRange ? this.erraticJitter : this.jitterAngle;
            this.fleeDirection = PhysicsMath.headingFromDirection(selfPosition.x - lastTargetPosition.x, selfPosition.z - lastTargetPosition.z) + RandomExtra.randomRange(-jitter, jitter);
            this.holdDirectionTimeRemaining = RandomExtra.randomRange(this.holdDirectionDurationRange);
            if (inErraticRange) {
                this.holdDirectionTimeRemaining *= this.erraticChangeDurationMultiplier;
            }
        }
        this.evade.setPositions(selfPosition, lastTargetPosition);
        this.evade.setDirectionHint(this.fleeDirection);
        final MotionController motionController = role.getActiveMotionController();
        final double desiredAltitudeWeight = (this.desiredAltitudeWeight >= 0.0) ? this.desiredAltitudeWeight : motionController.getDesiredAltitudeWeight();
        return this.scaleSteering(ref, role, this.evade, desiredSteering, desiredAltitudeWeight, componentAccessor);
    }
    
    @Override
    public boolean isGoalReached(final Ref<EntityStore> ref, final AStarBase aStarBase, @Nonnull final AStarNode aStarNode, final MotionController motionController, final ComponentAccessor<EntityStore> componentAccessor) {
        return aStarNode.getEstimateToGoal() <= 0.0f;
    }
    
    @Override
    protected boolean isGoalReached(final Ref<EntityStore> ref, @Nonnull final MotionController motionController, final Vector3d position, final Vector3d lastTestedPosition, final ComponentAccessor<EntityStore> componentAccessor) {
        return motionController.waypointDistanceSquared(position, lastTestedPosition) >= this.stopDistanceSquared;
    }
    
    @Override
    public float estimateToGoal(@Nonnull final AStarBase aStarBase, final Vector3d fromPosition, @Nonnull final MotionController motionController) {
        return Math.max(0.0f, (float)(this.stopDistance - motionController.waypointDistance(fromPosition, ((AStarWithTarget)aStarBase).getTargetPosition())));
    }
    
    @Override
    public void findBestPath(@Nonnull final AStarBase aStarBase, final MotionController controller) {
        aStarBase.buildBestPath(AStarNode::getEstimateToGoal, (oldV, v) -> v < oldV, Float.MAX_VALUE);
    }
}
