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

package com.hypixel.hytale.server.npc.util;

import com.hypixel.hytale.math.util.TrigMathUtil;
import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.modules.projectile.config.BallisticData;
import com.hypixel.hytale.server.npc.sensorinfo.ExtraInfoProvider;

public class AimingData implements ExtraInfoProvider
{
    public static final double MIN_MOVE_SPEED_STATIC = 0.01;
    public static final double MIN_MOVE_SPEED_STATIC_2 = 1.0E-4;
    public static final double MIN_AIMING_DISTANCE = 0.01;
    public static final double MIN_AIMING_DISTANCE_2 = 1.0E-4;
    public static final double MIN_AIR_TIME = 0.01;
    public static final double ANGLE_EPSILON = 0.1;
    @Nullable
    private BallisticData ballisticData;
    private boolean useFlatTrajectory;
    private double depthOffset;
    private boolean pitchAdjustOffset;
    private boolean haveSolution;
    private boolean haveOrientation;
    private boolean haveAttacked;
    private double chargeDistance;
    private double desiredHitAngle;
    private final float[] pitch;
    private final float[] yaw;
    @Nullable
    private Ref<EntityStore> target;
    private int owner;
    
    public AimingData() {
        this.useFlatTrajectory = true;
        this.pitch = new float[2];
        this.yaw = new float[2];
        this.owner = Integer.MIN_VALUE;
    }
    
    public boolean isHaveAttacked() {
        return this.haveAttacked;
    }
    
    public void setHaveAttacked(final boolean haveAttacked) {
        this.haveAttacked = haveAttacked;
    }
    
    public void requireBallistic(@Nonnull final BallisticData ballisticData) {
        this.ballisticData = ballisticData;
        this.haveSolution = false;
        this.haveOrientation = false;
    }
    
    public void requireCloseCombat() {
        this.ballisticData = null;
        this.haveSolution = false;
        this.haveOrientation = false;
    }
    
    public float getPitch() {
        return this.getPitch(this.useFlatTrajectory);
    }
    
    public float getPitch(final boolean flatTrajectory) {
        return this.pitch[!flatTrajectory];
    }
    
    public float getYaw() {
        return this.getYaw(this.useFlatTrajectory);
    }
    
    public float getYaw(final boolean flatTrajectory) {
        return this.yaw[!flatTrajectory];
    }
    
    public boolean isBallistic() {
        return this.ballisticData != null;
    }
    
    @Nullable
    public BallisticData getBallisticData() {
        return this.ballisticData;
    }
    
    public void setUseFlatTrajectory(final boolean useFlatTrajectory) {
        this.useFlatTrajectory = useFlatTrajectory;
    }
    
    public void setChargeDistance(final double chargeDistance) {
        this.chargeDistance = chargeDistance;
    }
    
    public double getChargeDistance() {
        return this.chargeDistance;
    }
    
    public void setDesiredHitAngle(final double desiredHitAngle) {
        this.desiredHitAngle = desiredHitAngle;
    }
    
    public double getDesiredHitAngle() {
        return this.desiredHitAngle;
    }
    
    @Nonnull
    @Override
    public Class<AimingData> getType() {
        return AimingData.class;
    }
    
    public void setDepthOffset(final double depthOffset, final boolean pitchAdjustOffset) {
        this.depthOffset = depthOffset;
        this.pitchAdjustOffset = (depthOffset != 0.0 && pitchAdjustOffset);
    }
    
    @Nullable
    public Ref<EntityStore> getTarget() {
        if (this.target == null) {
            return null;
        }
        if (!this.target.isValid() || this.target.getStore().getArchetype(this.target).contains(DeathComponent.getComponentType())) {
            return this.target = null;
        }
        return this.target;
    }
    
    public void setTarget(final Ref<EntityStore> ref) {
        this.target = ref;
    }
    
    public boolean haveOrientation() {
        return this.haveOrientation || this.haveSolution;
    }
    
    public void setOrientation(final float yaw, final float pitch) {
        this.yaw[0] = (this.yaw[1] = yaw);
        this.pitch[0] = (this.pitch[1] = pitch);
        this.haveOrientation = true;
    }
    
    public void clearSolution() {
        this.haveOrientation = false;
        this.haveSolution = false;
        this.target = null;
    }
    
    public boolean computeSolution(double x, final double y, double z, final double vx, final double vy, final double vz) {
        double xxzz = x * x + z * z;
        double d2 = xxzz + y * y;
        if (d2 < 0.01) {
            return this.haveSolution = false;
        }
        if (!this.isBallistic()) {
            this.yaw[0] = (this.yaw[1] = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)));
            this.pitch[0] = (this.pitch[1] = PhysicsMath.pitchFromDirection(x, y, z));
            return this.haveSolution = true;
        }
        if (!this.pitchAdjustOffset && xxzz > this.depthOffset * this.depthOffset) {
            final double len = Math.sqrt(xxzz);
            final double newLen = len - this.depthOffset;
            final double scale = newLen / len;
            x *= scale;
            z *= scale;
            xxzz = newLen * newLen;
            d2 = xxzz + y * y;
        }
        final double v2 = NPCPhysicsMath.dotProduct(vx, vy, vz);
        if (v2 < 1.0E-4) {
            final boolean computeStaticSolution = this.computeStaticSolution(Math.sqrt(xxzz), y);
            this.haveSolution = computeStaticSolution;
            if (computeStaticSolution) {
                this.yaw[0] = (this.yaw[1] = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)));
            }
            return this.haveSolution;
        }
        final double gravity = this.ballisticData.getGravity();
        final double muzzleVelocity = this.ballisticData.getMuzzleVelocity();
        final double[] solutions = new double[4];
        final double c4 = gravity * gravity / 4.0;
        final double c5 = vy * gravity;
        final double c6 = v2 + y * gravity - muzzleVelocity * muzzleVelocity;
        final double c7 = 2.0 * (x * vx + y * vy + z * vz);
        final int numSolutions = RootSolver.solveQuartic(c4, c5, c6, c7, d2, solutions);
        if (numSolutions == 0) {
            return this.haveSolution = false;
        }
        int numResults = 0;
        double lastT = Double.MAX_VALUE;
        for (final double t : solutions) {
            if (t > 0.01) {
                final double tx = x + t * vx;
                final double tz = z + t * vz;
                xxzz = tx * tx + tz * tz;
                if (xxzz >= 0.01) {
                    final double sine = (y / t + 0.5 * gravity * t) / muzzleVelocity;
                    if (sine >= -1.0) {
                        if (sine <= 1.0) {
                            final float p = TrigMathUtil.asin(sine);
                            final float h = PhysicsMath.headingFromDirection(tx, tz);
                            if (numResults < 2) {
                                if (numResults == 0 || t > lastT) {
                                    lastT = t;
                                    this.pitch[numResults] = p;
                                    this.yaw[numResults] = h;
                                }
                                else {
                                    this.pitch[numResults] = this.pitch[numResults - 1];
                                    this.yaw[numResults] = this.yaw[numResults - 1];
                                    this.pitch[numResults - 1] = p;
                                    this.yaw[numResults - 1] = h;
                                }
                                ++numResults;
                            }
                        }
                    }
                }
            }
        }
        if (numResults == 0) {
            return this.haveSolution = false;
        }
        if (numResults == 1) {
            this.pitch[1] = this.pitch[0];
            this.yaw[1] = this.yaw[0];
        }
        return this.haveSolution = true;
    }
    
    public boolean isOnTarget(final float yaw, final float pitch, final double hitAngle) {
        if (!this.haveOrientation()) {
            return false;
        }
        final double differenceYaw = NPCPhysicsMath.turnAngle(yaw, this.getYaw());
        if (!this.isBallistic()) {
            return -hitAngle <= differenceYaw && differenceYaw <= hitAngle;
        }
        final double differencePitch = NPCPhysicsMath.turnAngle(pitch, this.getPitch());
        return differencePitch >= -0.1 && differencePitch <= 0.1 && differenceYaw >= -0.1 && differenceYaw <= 0.1;
    }
    
    public void tryClaim(final int id) {
        if (this.owner != Integer.MIN_VALUE) {
            return;
        }
        this.owner = id;
        this.clear();
    }
    
    public boolean isClaimedBy(final int id) {
        return this.owner == id;
    }
    
    public void release() {
        this.owner = Integer.MIN_VALUE;
    }
    
    public void clear() {
        this.clearSolution();
        this.ballisticData = null;
        this.useFlatTrajectory = true;
        this.depthOffset = 0.0;
        this.pitchAdjustOffset = false;
        this.haveAttacked = false;
    }
    
    protected boolean computeStaticSolution(final double dx, final double dy) {
        return this.haveSolution = AimingHelper.computePitch(dx, dy, this.ballisticData.getMuzzleVelocity(), this.ballisticData.getGravity(), this.pitch);
    }
}
