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

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

import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.core.modules.projectile.config.BallisticData;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.server.npc.sensorinfo.IPositionProvider;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
import java.util.concurrent.ThreadLocalRandom;
import com.hypixel.hytale.server.core.modules.physics.component.Velocity;
import com.hypixel.hytale.server.npc.movement.Steering;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Store;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderHeadMotionBase;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.corecomponents.combat.builders.BuilderHeadMotionAim;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.npc.util.AimingData;
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.npc.corecomponents.HeadMotionBase;

public class HeadMotionAim extends HeadMotionBase
{
    protected static final ComponentType<EntityStore, TransformComponent> TRANSFORM_COMPONENT_TYPE;
    protected static final ComponentType<EntityStore, ModelComponent> MODEL_COMPONENT_TYPE;
    protected static final ComponentType<EntityStore, BoundingBox> BOUNDING_BOX_COMPONENT_TYPE;
    protected final double spread;
    protected final boolean deflection;
    protected final double hitProbability;
    protected final double relativeTurnSpeed;
    protected final AimingData aimingData;
    protected Ref<EntityStore> lastTargetReference;
    protected double spreadX;
    protected double spreadY;
    protected double spreadZ;
    
    public HeadMotionAim(@Nonnull final BuilderHeadMotionAim builder, @Nonnull final BuilderSupport support) {
        super(builder);
        this.aimingData = new AimingData();
        this.spread = builder.getSpread(support);
        this.hitProbability = builder.getHitProbability(support);
        this.deflection = builder.isDeflection(support);
        this.relativeTurnSpeed = builder.getRelativeTurnSpeed(support);
    }
    
    @Override
    public void preComputeSteering(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nullable final InfoProvider sensorInfo, @Nonnull final Store<EntityStore> store) {
        if (sensorInfo == null) {
            return;
        }
        sensorInfo.passExtraInfo(this.aimingData);
    }
    
    @Override
    public void activate(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role role, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        this.aimingData.setHaveAttacked(true);
    }
    
    @Override
    public boolean computeSteering(@Nonnull final Ref<EntityStore> ref, @Nonnull final Role support, @Nullable final InfoProvider sensorInfo, final double dt, @Nonnull final Steering desiredSteering, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (sensorInfo == null || !sensorInfo.hasPosition()) {
            desiredSteering.clear();
            return true;
        }
        final TransformComponent transformComponent = componentAccessor.getComponent(ref, HeadMotionAim.TRANSFORM_COMPONENT_TYPE);
        assert transformComponent != null;
        final ModelComponent modelComponent = componentAccessor.getComponent(ref, HeadMotionAim.MODEL_COMPONENT_TYPE);
        assert modelComponent != null;
        final Vector3d position = transformComponent.getPosition();
        final IPositionProvider positionProvider = sensorInfo.getPositionProvider();
        double x = positionProvider.getX() - position.getX();
        double y = positionProvider.getY() - position.getY() - modelComponent.getModel().getEyeHeight();
        double z = positionProvider.getZ() - position.getZ();
        double vx = 0.0;
        double vy = 0.0;
        double vz = 0.0;
        final Ref<EntityStore> targetRef = positionProvider.getTarget();
        if (targetRef != null) {
            final Velocity targetVelocityComponent = componentAccessor.getComponent(targetRef, Velocity.getComponentType());
            assert targetVelocityComponent != null;
            final BoundingBox boundingBoxComponent = componentAccessor.getComponent(ref, HeadMotionAim.BOUNDING_BOX_COMPONENT_TYPE);
            final Box boundingBox = (boundingBoxComponent != null) ? boundingBoxComponent.getBoundingBox() : null;
            if (this.aimingData.isBallistic()) {
                if (boundingBox != null) {
                    x += (boundingBox.getMax().getX() + boundingBox.getMin().getX()) / 2.0;
                    y += (boundingBox.getMax().getY() + boundingBox.getMin().getY()) / 2.0;
                    z += (boundingBox.getMax().getZ() + boundingBox.getMin().getZ()) / 2.0;
                }
                if (this.deflection) {
                    final Vector3d steeringVelocity = targetVelocityComponent.getVelocity();
                    vx = steeringVelocity.getX();
                    vy = steeringVelocity.getY();
                    vz = steeringVelocity.getZ();
                }
            }
            else if (boundingBox != null) {
                final double minY = y + boundingBox.getMin().y;
                final double maxY = y + boundingBox.getMax().y;
                if (minY > 0.0) {
                    y = minY;
                }
                else if (maxY < 0.0) {
                    y = maxY;
                }
                else {
                    y = 0.0;
                }
            }
        }
        if (this.aimingData.isBallistic()) {
            final BallisticData ballisticData = this.aimingData.getBallisticData();
            if (ballisticData != null) {
                y += ballisticData.getVerticalCenterShot();
                this.aimingData.setDepthOffset(ballisticData.getDepthShot(), ballisticData.isPitchAdjustShot());
            }
            else {
                this.aimingData.setDepthOffset(0.0, false);
            }
            if (targetRef != null && (this.lastTargetReference == null || !this.lastTargetReference.equals(targetRef))) {
                this.lastTargetReference = targetRef;
                this.aimingData.setHaveAttacked(true);
            }
            if (this.aimingData.isHaveAttacked()) {
                final ThreadLocalRandom random = ThreadLocalRandom.current();
                if (this.spread > 0.0 && random.nextDouble() > this.hitProbability) {
                    final double spread2 = 2.0 * this.spread * Math.sqrt(NPCPhysicsMath.dotProduct(x, y, z)) / 10.0;
                    this.spreadX += spread2 * (random.nextDouble() - 0.5);
                    this.spreadY += spread2 * (random.nextDouble() - 0.5);
                    this.spreadZ += spread2 * (random.nextDouble() - 0.5);
                }
                else {
                    this.spreadX = 0.0;
                    this.spreadY = 0.0;
                    this.spreadZ = 0.0;
                }
                this.aimingData.setHaveAttacked(false);
            }
            x += this.spreadX;
            y += this.spreadY;
            z += this.spreadZ;
        }
        float yaw;
        float pitch;
        if (this.aimingData.computeSolution(x, y, z, vx, vy, vz)) {
            yaw = this.aimingData.getYaw();
            pitch = this.aimingData.getPitch();
            this.aimingData.setTarget(targetRef);
        }
        else {
            final HeadRotation headRotationComponent = componentAccessor.getComponent(ref, HeadRotation.getComponentType());
            assert headRotationComponent != null;
            final double xxzz = x * x + z * z;
            final double xxyyzz = xxzz + y * y;
            final Vector3f headRotation = headRotationComponent.getRotation();
            yaw = ((xxzz >= 1.0E-4) ? PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)) : headRotation.getYaw());
            pitch = ((xxyyzz >= 1.0E-4) ? PhysicsMath.pitchFromDirection(x, y, z) : headRotation.getPitch());
            this.aimingData.setOrientation(yaw, pitch);
            this.aimingData.setTarget(null);
        }
        desiredSteering.clearTranslation();
        desiredSteering.setYaw(yaw);
        desiredSteering.setPitch(pitch);
        desiredSteering.setRelativeTurnSpeed(this.relativeTurnSpeed);
        return true;
    }
    
    static {
        TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType();
        MODEL_COMPONENT_TYPE = ModelComponent.getComponentType();
        BOUNDING_BOX_COMPONENT_TYPE = BoundingBox.getComponentType();
    }
}
