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

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

import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.npc.navigation.AStarEvaluator;
import com.hypixel.hytale.server.npc.navigation.AStarBase;
import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
import java.util.logging.Level;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import com.hypixel.hytale.server.core.entity.Entity;
import com.hypixel.hytale.server.npc.sensorinfo.IPositionProvider;
import com.hypixel.hytale.server.core.entity.LivingEntity;
import com.hypixel.hytale.server.core.entity.EntityUtils;
import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
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.TrigMathUtil;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionFindBase;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionFindWithTarget;
import javax.annotation.Nullable;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.npc.navigation.AStarWithTarget;

public abstract class BodyMotionFindWithTarget extends BodyMotionFindBase<AStarWithTarget>
{
    protected final double minMoveDistanceWait;
    protected final double minMoveDistanceWaitSquared;
    protected final double minMoveDistanceRecompute;
    protected final double minMoveDistanceRecomputeSquared;
    protected final float cosHalfRecomputeConeAngle;
    protected final double minMoveDistanceReproject;
    protected final double minMoveDistanceReprojectSquared;
    protected final boolean adjustRangeByHitboxSize;
    protected final Vector3d lastPathedPosition;
    protected final Vector3d conePosition;
    protected final Vector3d coneDirection;
    @Nullable
    protected Box targetBoundingBox;
    protected Box selfBoundingBox;
    protected boolean waitForTargetMovement;
    private final Vector3d lastTargetPosition;
    private final Vector3d lastAccessibleTargetPosition;
    private boolean haveValidTargetPosition;
    private boolean haveAccessibleTargetPosition;
    private boolean lastAccessibleTargetPositionIsCurrent;
    protected String self;
    protected String other;
    
    public BodyMotionFindWithTarget(@Nonnull final BuilderBodyMotionFindWithTarget builderMotionFindWithTarget, @Nonnull final BuilderSupport support) {
        super(builderMotionFindWithTarget, support, new AStarWithTarget());
        this.lastPathedPosition = new Vector3d();
        this.conePosition = new Vector3d();
        this.coneDirection = new Vector3d();
        this.waitForTargetMovement = false;
        this.lastTargetPosition = new Vector3d();
        this.lastAccessibleTargetPosition = new Vector3d();
        this.adjustRangeByHitboxSize = builderMotionFindWithTarget.isAdjustRangeByHitboxSize(support);
        this.minMoveDistanceWait = builderMotionFindWithTarget.getMinMoveDistanceWait(support);
        this.minMoveDistanceWaitSquared = this.minMoveDistanceWait * this.minMoveDistanceWait;
        this.minMoveDistanceRecompute = builderMotionFindWithTarget.getMinMoveDistanceRecompute(support);
        this.minMoveDistanceRecomputeSquared = this.minMoveDistanceRecompute * this.minMoveDistanceRecompute;
        this.minMoveDistanceReproject = builderMotionFindWithTarget.getMinMoveDistanceReproject(support);
        this.minMoveDistanceReprojectSquared = this.minMoveDistanceReproject * this.minMoveDistanceReproject;
        final float cosine = TrigMathUtil.cos(builderMotionFindWithTarget.getRecomputeConeAngle(support) / 2.0);
        this.cosHalfRecomputeConeAngle = ((cosine == -1.0f) ? 1.0f : cosine);
    }
    
    @Override
    public void activate(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        super.activate(ref, role, componentAccessor);
        this.haveValidTargetPosition = false;
        this.haveAccessibleTargetPosition = false;
        this.waitForTargetMovement = false;
        this.targetBoundingBox = null;
        this.lastPathedPosition.assign(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE);
        this.self = role.getRoleName();
    }
    
    public boolean canComputeMotion(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nullable final InfoProvider infoProvider, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final BoundingBox boundingBoxComponent = componentAccessor.getComponent(ref, BoundingBox.getComponentType());
        assert boundingBoxComponent != null;
        this.targetBoundingBox = null;
        this.selfBoundingBox = boundingBoxComponent.getBoundingBox();
        this.lastAccessibleTargetPositionIsCurrent = false;
        if (infoProvider != null && infoProvider.hasPosition()) {
            final IPositionProvider positionProvider = infoProvider.getPositionProvider();
            if (positionProvider.providePosition(this.lastTargetPosition)) {
                this.haveValidTargetPosition = true;
                final Ref<EntityStore> targetEntityReference = positionProvider.getTarget();
                if (targetEntityReference != null) {
                    final BoundingBox targetEntityBoundingBoxComponent = componentAccessor.getComponent(targetEntityReference, BoundingBox.getComponentType());
                    assert targetEntityBoundingBoxComponent != null;
                    final NPCEntity npcEntityComponent = componentAccessor.getComponent(targetEntityReference, NPCEntity.getComponentType());
                    this.other = ((npcEntityComponent != null) ? npcEntityComponent.getRoleName() : null);
                    this.targetBoundingBox = targetEntityBoundingBoxComponent.getBoundingBox();
                    final MovementStatesComponent movementStatesComponent = componentAccessor.getComponent(targetEntityReference, MovementStatesComponent.getComponentType());
                    final Entity entity = EntityUtils.getEntity(targetEntityReference, targetEntityReference.getStore());
                    if (entity instanceof LivingEntity && movementStatesComponent != null && movementStatesComponent.getMovementStates().onGround) {
                        this.lastAccessibleTargetPosition.assign(this.lastTargetPosition);
                        this.haveAccessibleTargetPosition = true;
                        this.lastAccessibleTargetPositionIsCurrent = true;
                    }
                }
                else {
                    this.targetBoundingBox = null;
                }
            }
        }
        this.targetDeltaSquared = (this.haveValidTargetPosition ? role.getActiveMotionController().waypointDistanceSquared(this.getLastTargetPosition(), this.lastPathedPosition) : Double.MAX_VALUE);
        return this.haveValidTargetPosition;
    }
    
    public boolean mustRecomputePath(@Nonnull final MotionController activeMotionController) {
        if (super.mustRecomputePath(activeMotionController)) {
            return true;
        }
        if (this.minMoveDistanceRecomputeSquared > 0.0 && this.targetDeltaSquared > this.minMoveDistanceRecomputeSquared) {
            if (this.dbgStatus) {
                NPCPlugin.get().getLogger().at(Level.INFO).log("Recomputing Path - Target moved");
            }
            this.resetThrottleCount();
            return true;
        }
        if (this.cosHalfRecomputeConeAngle < 1.0f) {
            if (activeMotionController.is2D()) {
                if (NPCPhysicsMath.isInViewCone(this.conePosition, this.coneDirection, this.cosHalfRecomputeConeAngle, this.getLastTargetPosition(), activeMotionController.getComponentSelector())) {
                    if (this.dbgStatus) {
                        NPCPlugin.get().getLogger().at(Level.INFO).log("Recomputing Path - Target left 2D cone");
                    }
                    return true;
                }
            }
            else if (NPCPhysicsMath.isInViewCone(this.conePosition, this.coneDirection, this.cosHalfRecomputeConeAngle, this.getLastTargetPosition())) {
                if (this.dbgStatus) {
                    NPCPlugin.get().getLogger().at(Level.INFO).log("Recomputing Path - Target left cone");
                }
                return true;
            }
        }
        return false;
    }
    
    public void forceRecomputePath(final MotionController activeMotionController) {
        super.forceRecomputePath(activeMotionController);
    }
    
    public boolean shouldDeferPathComputation(@Nonnull final MotionController motionController, final Vector3d position, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (this.throttleCount > this.throttleIgnoreCount) {
            final double distanceSquared = this.getLastTargetPosition().distanceSquaredTo(this.lastPathedPosition);
            if (distanceSquared < 1.0000000000000002E-10 || (this.waitForTargetMovement && distanceSquared < this.minMoveDistanceWaitSquared)) {
                return true;
            }
        }
        this.waitForTargetMovement = false;
        this.lastPathedPosition.assign(this.getLastAccessibleTargetPosition(motionController, false, componentAccessor));
        return false;
    }
    
    @Override
    protected boolean mustAbortThrottling(final MotionController motionController, final Ref<EntityStore> ref) {
        if (super.mustAbortThrottling(motionController, ref)) {
            return true;
        }
        if (this.minMoveDistanceRecomputeSquared > 0.0 && this.targetDeltaSquared > this.minMoveDistanceRecomputeSquared) {
            if (this.dbgMotionState) {
                NPCPlugin.get().getLogger().at(Level.INFO).log("MotionFindWithTarget: Aborting throttling - Target moved");
            }
            return true;
        }
        return false;
    }
    
    public boolean isGoalReached(final Ref<EntityStore> ref, final MotionController activeMotionController, final Vector3d position, final ComponentAccessor<EntityStore> componentAccessor) {
        return this.isGoalReached(ref, activeMotionController, position, this.getLastTargetPosition(), componentAccessor);
    }
    
    public AStarBase.Progress startComputePath(@Nonnull final Ref<EntityStore> ref, final Role role, @Nonnull final MotionController activeMotionController, @Nonnull final Vector3d position, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (this.cosHalfRecomputeConeAngle < 1.0f) {
            this.conePosition.assign(position);
            this.coneDirection.assign(this.lastPathedPosition).subtract(position);
        }
        return ((AStarWithTarget)this.aStar).initComputePath(ref, position, this.lastPathedPosition, this, activeMotionController, this.probeMoveData, this.sharedNodePoolProvider, componentAccessor);
    }
    
    public void onBlockedPath() {
        super.onBlockedPath();
    }
    
    public void onNoPathFound(final MotionController motionController) {
        super.onNoPathFound(motionController);
        this.waitForTargetMovement = true;
    }
    
    @Override
    protected void onSteering(final MotionController activeMotionController, @Nonnull final Ref<EntityStore> ref, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType());
        assert transformComponent != null;
        this.lastPathedPosition.assign(transformComponent.getPosition());
    }
    
    @Override
    protected void decorateDebugString(@Nonnull final StringBuilder dbgString) {
        dbgString.append("D:").append(MathUtil.round(Math.sqrt(this.targetDeltaSquared), 1));
    }
    
    protected abstract boolean isGoalReached(final Ref<EntityStore> p0, final MotionController p1, final Vector3d p2, final Vector3d p3, final ComponentAccessor<EntityStore> p4);
    
    protected Vector3d getLastTargetPosition() {
        return this.lastTargetPosition;
    }
    
    protected Vector3d getLastAccessibleTargetPosition(@Nonnull final MotionController motionController, final boolean approximate, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (this.lastAccessibleTargetPositionIsCurrent || (approximate && this.haveAccessibleTargetPosition && motionController.waypointDistanceSquared(this.lastTargetPosition, this.lastAccessibleTargetPosition) < this.minMoveDistanceReprojectSquared)) {
            return this.lastAccessibleTargetPosition;
        }
        if (this.dbgMotionState && motionController.is2D()) {
            NPCPlugin.get().getLogger().at(Level.INFO).log("MotionFindWithTarget: Reprojecting %s -> %s", this.self, this.other);
        }
        this.lastAccessibleTargetPosition.assign(this.lastTargetPosition);
        motionController.translateToAccessiblePosition(this.lastAccessibleTargetPosition, this.targetBoundingBox, 0.0, 320.0, componentAccessor);
        this.haveAccessibleTargetPosition = true;
        this.lastAccessibleTargetPositionIsCurrent = true;
        return this.lastAccessibleTargetPosition;
    }
}
