package net.starlikeclient.minecraft.entities.entity;

import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IProjectile;
import net.minecraft.entity.monster.EntityEnderman;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.S2BPacketChangeGameState;
import net.minecraft.util.*;
import net.minecraft.world.World;

/**
 * + This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source
 * code.
 *
 * Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" Mod
 * Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team
 *
 * EaglercraftX 1.8 patch files (c) 2022-2024 lax1dude, ayunami2000. All Rights
 * Reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
public class EntityBullet extends Entity implements IProjectile {

    private static final double DEFAULT_DAMAGE = 2.0D;
    private static final float DEFAULT_SIZE = 0.5F;
    private static final double RENDER_DISTANCE_WEIGHT = 10.0D;
    private int xTile = -1;
    private int yTile = -1;
    private int zTile = -1;
    private Block inTile;
    private int inData;
    private boolean inGround;
    public int canBePickedUp;
    public int arrowShake;
    public Entity shootingEntity;
    private int ticksInGround;
    private int ticksInAir;
    private double damage = DEFAULT_DAMAGE;
    private int knockbackStrength;
    public boolean isChair = false;
    public EntityBullet(World worldIn) {
        super(worldIn);
        initializeBullet();
    }
    public EntityBullet(World worldIn, double x, double y, double z) {
        super(worldIn);
        initializeBullet();
        setPosition(x, y, z);
    }
    public EntityBullet(World worldIn, EntityLivingBase shooter, EntityLivingBase target, float velocity, float inaccuracy) {
        super(worldIn);
        initializeBullet();
        setupShooter(shooter);
        calculateInitialPosition(shooter, target, velocity, inaccuracy);
    }
    public EntityBullet(World worldIn, EntityLivingBase shooter, float velocity) {
        super(worldIn);
        initializeBullet();
        setupShooter(shooter);
        setupInitialMotion(velocity);
    }
    private void initializeBullet() {
        this.renderDistanceWeight = RENDER_DISTANCE_WEIGHT;
        this.setSize(DEFAULT_SIZE, DEFAULT_SIZE);
    }
    private void setupShooter(EntityLivingBase shooter) {
        this.shootingEntity = shooter;
        if (shooter instanceof EntityPlayer) {
            this.canBePickedUp = 1;
        }
    }
    private void calculateInitialPosition(EntityLivingBase shooter, EntityLivingBase target, float velocity, float inaccuracy) {
        this.posY = shooter.posY + shooter.getEyeHeight() - 0.1D;
        double dX = target.posX - shooter.posX;
        double dY = target.getEntityBoundingBox().minY + target.height / 3.0F - this.posY;
        double dZ = target.posZ - shooter.posZ;
        double distance = MathHelper.sqrt_double(dX * dX + dZ * dZ);

        if (distance >= 1.0E-7D) {
            float yaw = (float) (MathHelper.func_181159_b(dZ, dX) * 180.0D / Math.PI) - 90.0F;
            float pitch = (float) (-(MathHelper.func_181159_b(dY, distance) * 180.0D / Math.PI));
            double dXNorm = dX / distance;
            double dZNorm = dZ / distance;
            this.setLocationAndAngles(shooter.posX + dXNorm, this.posY, shooter.posZ + dZNorm, yaw, pitch);
            float velocityFactor = (float) (distance * 0.2D);
            setThrowableHeading(dX, dY + velocityFactor, dZ, velocity, inaccuracy);
        }
    }
    private void setupInitialMotion(float velocity) {
        this.setSize(DEFAULT_SIZE, DEFAULT_SIZE);
        this.setLocationAndAngles(this.shootingEntity.posX, this.shootingEntity.posY + this.shootingEntity.getEyeHeight(), this.shootingEntity.posZ, this.shootingEntity.rotationYaw, this.shootingEntity.rotationPitch);
        this.posX -= MathHelper.cos(this.rotationYaw / 180.0F * (float) Math.PI) * 0.16F;
        this.posY -= 0.1D;
        this.posZ -= MathHelper.sin(this.rotationYaw / 180.0F * (float) Math.PI) * 0.16F;
        this.setPosition(this.posX, this.posY, this.posZ);
        this.motionX = -MathHelper.sin(this.rotationYaw / 180.0F * (float) Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float) Math.PI);
        this.motionZ = MathHelper.cos(this.rotationYaw / 180.0F * (float) Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float) Math.PI);
        this.motionY = -MathHelper.sin(this.rotationPitch / 180.0F * (float) Math.PI);
        setThrowableHeading(this.motionX, this.motionY, this.motionZ, velocity * 5.0F, 0.0F);    }
    @Override
    public boolean canAttackWithItem() {
        return false;
    }
    @Override
    protected boolean canTriggerWalking() {
        return false;
    }
    @Override
    protected void entityInit() {
        this.dataWatcher.addObject(16, Byte.valueOf((byte) 0));
    }
    public double getDamage() {
        return this.damage;
    }
    @Override
    public float getEyeHeight() {
        return 0.0F;
    }
    public boolean getIsCritical() {
        return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0;
    }
    @Override
    public void onCollideWithPlayer(EntityPlayer entityplayer) {
        if (!this.worldObj.isRemote && this.inGround && this.arrowShake <= 0) {
            boolean canPickup = this.canBePickedUp == 1 || (this.canBePickedUp == 2 && entityplayer.capabilities.isCreativeMode); {
                canPickup = false;
            }
            if (canPickup) {
                this.playSound("random.landhit", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F);
                entityplayer.onItemPickup(this, 1);
                this.setDead();
            }
        }
    }
    @Override
    public void onUpdate() {
        super.onUpdate();
        if (isChair) {
            handleChairUpdate();
            return;
        }
        if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) {
            initializeRotation();
        }
        BlockPos blockPos = new BlockPos(this.xTile, this.yTile, this.zTile);
        IBlockState blockState = this.worldObj.getBlockState(blockPos);
        Block block = blockState.getBlock();
        if (block.getMaterial() != Material.air) {
            handleBlockCollision(block, blockPos, blockState);
        }
        if (this.arrowShake > 0) {
            --this.arrowShake;
        }
        if (this.inGround) {
            handleInGroundState(block, blockState);
        } else {
            handleInAirState();
        }
    }
    private void handleChairUpdate() {
        if (!(riddenByEntity instanceof EntityPlayer)) {
            isChair = false;
            setDead();
        }
    }
    private void initializeRotation() {
        float f = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
        this.prevRotationYaw = this.rotationYaw = (float) (MathHelper.func_181159_b(this.motionX, this.motionZ) * 180.0D / Math.PI);
        this.prevRotationPitch = this.rotationPitch = (float) (MathHelper.func_181159_b(this.motionY, (double) f) * 180.0D / Math.PI);
    }
    private void handleBlockCollision(Block block, BlockPos blockPos, IBlockState blockState) {
        block.setBlockBoundsBasedOnState(this.worldObj, blockPos);
        AxisAlignedBB axisAlignedBB = block.getCollisionBoundingBox(this.worldObj, blockPos, blockState);

        if (axisAlignedBB != null && axisAlignedBB.isVecInside(new Vec3(this.posX, this.posY, this.posZ))) {
            this.inGround = true;
        }
    }
    private void handleInGroundState(Block block, IBlockState blockState) {
        int meta = block.getMetaFromState(blockState);

        if (block == this.inTile && meta == this.inData) {
            ++this.ticksInGround;
            if (this.ticksInGround >= 1200) {
                this.setDead();
            }
        } else {
            this.inGround = false;
            this.motionX *= this.rand.nextFloat() * 0.2F;
            this.motionY *= this.rand.nextFloat() * 0.2F;
            this.motionZ *= this.rand.nextFloat() * 0.2F;
            this.ticksInGround = 0;
            this.ticksInAir = 0;
        }
    }
    private void handleInAirState() {
        ++this.ticksInAir;
        Vec3 currentPos = new Vec3(this.posX, this.posY, this.posZ);
        Vec3 nextPos = new Vec3(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
        MovingObjectPosition movingObjectPosition = this.worldObj.rayTraceBlocks(currentPos, nextPos, false, true, false);
        if (movingObjectPosition != null) {
            nextPos = new Vec3(movingObjectPosition.hitVec.xCoord, movingObjectPosition.hitVec.yCoord, movingObjectPosition.hitVec.zCoord);
        }
        Entity entity = findCollidedEntity(currentPos, nextPos);
        if (entity != null) {
            movingObjectPosition = new MovingObjectPosition(entity);
        }
        if (movingObjectPosition != null) {
            handleCollision(movingObjectPosition);
        }
        if (this.getIsCritical()) {
            spawnCriticalParticles();
        }
        updatePositionAndRotation();
        applyMotionDamping();
    }
    private Entity findCollidedEntity(Vec3 currentPos, Vec3 nextPos) {
        Entity entity = null;
        List<Entity> entities = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox().addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D));
        double closestDistance = 0.0D;

        for (Entity entityCandidate : entities) {
            if (entityCandidate.canBeCollidedWith() && (entityCandidate != this.shootingEntity || this.ticksInAir >= 5)) {
                float f1 = 0.3F;
                AxisAlignedBB axisAlignedBB = entityCandidate.getEntityBoundingBox().expand(f1, f1, f1);
                MovingObjectPosition movingObjectPosition = axisAlignedBB.calculateIntercept(currentPos, nextPos);

                if (movingObjectPosition != null) {
                    double distance = currentPos.squareDistanceTo(movingObjectPosition.hitVec);

                    if (distance < closestDistance || closestDistance == 0.0D) {
                        entity = entityCandidate;
                        closestDistance = distance;
                    }
                }
            }
        }
        return entity;
    }
    private void handleCollision(MovingObjectPosition movingObjectPosition) {
        if (movingObjectPosition.entityHit != null) {
            handleEntityCollision(movingObjectPosition);
        } else {
            handleBlockHit(movingObjectPosition);
        }
    }
    private void handleEntityCollision(MovingObjectPosition movingObjectPosition) {
        float velocityMagnitude = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
        int damageAmount = MathHelper.ceiling_double_int(velocityMagnitude * this.damage);
        if (this.getIsCritical()) {
            damageAmount += this.rand.nextInt(damageAmount / 2 + 2);
        }
        DamageSource damageSource = (this.shootingEntity == null) ? DamageSource.causeArrowDamage(this, this) : DamageSource.causeArrowDamage(this, this.shootingEntity);
        if (this.isBurning() && !(movingObjectPosition.entityHit instanceof EntityEnderman)) {
            movingObjectPosition.entityHit.setFire(5);
        }
        if (movingObjectPosition.entityHit.attackEntityFrom(damageSource, (float) damageAmount)) {
            if (movingObjectPosition.entityHit instanceof EntityLivingBase) {
                handleLivingEntityHit(movingObjectPosition);
            }
            this.playSound("starlike:revolver.hit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));

            if (!(movingObjectPosition.entityHit instanceof EntityEnderman)) {
                this.setDead();
            }
        } else {
            handleMissedHit();
        }
    }
    private void handleLivingEntityHit(MovingObjectPosition movingObjectPosition) {
        EntityLivingBase entityLivingBase = (EntityLivingBase) movingObjectPosition.entityHit;
        if (!this.worldObj.isRemote) {
            entityLivingBase.setArrowCountInEntity(entityLivingBase.getArrowCountInEntity() + 1);
        }
        if (this.knockbackStrength > 0) {
            applyKnockback(movingObjectPosition, entityLivingBase);
        }
        if (this.shootingEntity instanceof EntityLivingBase) {
            EnchantmentHelper.applyThornEnchantments(entityLivingBase, this.shootingEntity);
            EnchantmentHelper.applyArthropodEnchantments((EntityLivingBase) this.shootingEntity, entityLivingBase);
        }
        if (this.shootingEntity != null && movingObjectPosition.entityHit != this.shootingEntity && movingObjectPosition.entityHit instanceof EntityPlayer && this.shootingEntity instanceof EntityPlayerMP) {
            ((EntityPlayerMP) this.shootingEntity).playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(6, 0.0F));
        }
    }
    private void applyKnockback(MovingObjectPosition movingObjectPosition, EntityLivingBase entityLivingBase) {
        float f7 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);

        if (f7 > 0.0F) {
            movingObjectPosition.entityHit.addVelocity(this.motionX * this.knockbackStrength * 0.6D / f7, 0.1D, this.motionZ * this.knockbackStrength * 0.6D / f7);
        }
    }
    private void handleBlockHit(MovingObjectPosition movingObjectPosition) {
        BlockPos blockPos = movingObjectPosition.getBlockPos();
        this.xTile = blockPos.getX();
        this.yTile = blockPos.getY();
        this.zTile = blockPos.getZ();
        IBlockState blockState = this.worldObj.getBlockState(blockPos);
        this.inTile = blockState.getBlock();
        this.inData = this.inTile.getMetaFromState(blockState);
        this.motionX = (float) (movingObjectPosition.hitVec.xCoord - this.posX);
        this.motionY = (float) (movingObjectPosition.hitVec.yCoord - this.posY);
        this.motionZ = (float) (movingObjectPosition.hitVec.zCoord - this.posZ);
        float motionMagnitude = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
        this.posX -= this.motionX / motionMagnitude * 0.05D;
        this.posY -= this.motionY / motionMagnitude * 0.05D;
        this.posZ -= this.motionZ / motionMagnitude * 0.05D;
        this.playSound("random.revolverhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
        this.inGround = true;
        this.arrowShake = 7;
        this.setIsCritical(false);
        if (this.inTile.getMaterial() != Material.air) {
            this.inTile.onEntityCollidedWithBlock(this.worldObj, blockPos, blockState, this);
        }
    }
    private void handleMissedHit() {
        this.motionX *= -0.1D;
        this.motionY *= -0.1D;
        this.motionZ *= -0.1D;
        this.rotationYaw += 180.0F;
        this.prevRotationYaw += 180.0F;
        this.ticksInAir = 0;
    }
    private void spawnCriticalParticles() {
        for (int k = 0; k < 4; ++k) {
            this.worldObj.spawnParticle(EnumParticleTypes.CRIT, this.posX + this.motionX * k / 4.0D, this.posY + this.motionY * k / 4.0D, this.posZ + this.motionZ * k / 4.0D, -this.motionX, -this.motionY + 0.2D, -this.motionZ, new int[0]);
        }
    }
    private void updatePositionAndRotation() {
        this.posX += this.motionX;
        this.posY += this.motionY;
        this.posZ += this.motionZ;
        float f3 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
        this.rotationYaw = (float) (MathHelper.func_181159_b(this.motionX, this.motionZ) * 180.0D / Math.PI);
        for (this.rotationPitch = (float) (MathHelper.func_181159_b(this.motionY, f3) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F) {
            ;
        }
        while (this.rotationPitch - this.prevRotationPitch >= 180.0F) {
            this.prevRotationPitch += 360.0F;
        }
        while (this.rotationYaw - this.prevRotationYaw < -180.0F) {
            this.prevRotationYaw -= 360.0F;
        }
        while (this.rotationYaw - this.prevRotationYaw >= 180.0F) {
            this.prevRotationYaw += 360.0F;
        }
        this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F;
        this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F;
    }
    private void applyMotionDamping() {
        float f4 = 0.99F;
        float f6 = 0.05F;

        if (this.isInWater()) {
            spawnWaterParticles();
            f4 = 0.6F;
        }

        if (this.isWet()) {
            this.extinguish();
        }
        this.motionX *= f4;
        this.motionZ *= f4;
        this.setPosition(this.posX, this.posY, this.posZ);
        this.doBlockCollisions();
    }
    private void spawnWaterParticles() {
        for (int i1 = 0; i1 < 4; ++i1) {
            float f8 = 0.25F;
            this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX - this.motionX * f8, this.posY - this.motionY * f8, this.posZ - this.motionZ * f8, this.motionX, this.motionY, this.motionZ, new int[0]);
        }
    }
    @Override
    public void readEntityFromNBT(NBTTagCompound nbttagcompound) {
        this.xTile = nbttagcompound.getShort("xTile");
        this.yTile = nbttagcompound.getShort("yTile");
        this.zTile = nbttagcompound.getShort("zTile");
        this.ticksInGround = nbttagcompound.getShort("life");
        this.inTile = nbttagcompound.hasKey("inTile", 8) ? Block.getBlockFromName(nbttagcompound.getString("inTile")) : Block.getBlockById(nbttagcompound.getByte("inTile") & 255);
        this.inData = nbttagcompound.getByte("inData") & 255;
        this.arrowShake = nbttagcompound.getByte("shake") & 255;
        this.inGround = nbttagcompound.getByte("inGround") == 1;
        this.damage = nbttagcompound.hasKey("damage", 99) ? nbttagcompound.getDouble("damage") : DEFAULT_DAMAGE;
        this.canBePickedUp = nbttagcompound.hasKey("pickup", 99) ? nbttagcompound.getByte("pickup") : (nbttagcompound.hasKey("player", 99) ? (nbttagcompound.getBoolean("player") ? 1 : 0) : 0);
    }
    public void setDamage(double damageIn) {
        this.damage = damageIn;
    }
    public void setIsCritical(boolean critical) {
        byte b0 = this.dataWatcher.getWatchableObjectByte(16);
        this.dataWatcher.updateObject(16, Byte.valueOf((byte) (critical ? (b0 | 1) : (b0 & -2))));
    }
    public void setKnockbackStrength(int knockbackStrengthIn) {
        this.knockbackStrength = knockbackStrengthIn;
    }
    @Override
    public void setPositionAndRotation2(double d0, double d1, double d2, float f, float f1, int var9, boolean var10) {
        this.setPosition(d0, d1, d2);
        this.setRotation(f, f1);
    }
    @Override
    public void setThrowableHeading(double x, double y, double z, float velocity, float inaccuracy) {
        float f = MathHelper.sqrt_double(x * x + y * y + z * z);
        x = x / (double) f;
        y = y / (double) f;
        z = z / (double) f;
        x = x + this.rand.nextGaussian() * (this.rand.nextBoolean() ? -1 : 1) * 0.0075D * inaccuracy;
        y = y + this.rand.nextGaussian() * (this.rand.nextBoolean() ? -1 : 1) * 0.0075D * inaccuracy;
        z = z + this.rand.nextGaussian() * (this.rand.nextBoolean() ? -1 : 1) * 0.0075D * inaccuracy;
        x = x * velocity;
        y = y * velocity;
        z = z * velocity;
        this.motionX = x;
        this.motionY = y;
        this.motionZ = z;
        float f1 = MathHelper.sqrt_double(x * x + z * z);
        this.prevRotationYaw = this.rotationYaw = (float) (MathHelper.func_181159_b(x, z) * 180.0D / Math.PI);
        this.prevRotationPitch = this.rotationPitch = (float) (MathHelper.func_181159_b(y, (double) f1) * 180.0D / Math.PI);
        this.ticksInGround = 0;
    }
    @Override
    public void setVelocity(double d0, double d1, double d2) {
        this.motionX = d0;
        this.motionY = d1;
        this.motionZ = d2;
        if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) {
            float f = MathHelper.sqrt_double(d0 * d0 + d2 * d2);
            this.prevRotationYaw = this.rotationYaw = (float) (MathHelper.func_181159_b(d0, d2) * 180.0D / Math.PI);
            this.prevRotationPitch = this.rotationPitch = (float) (MathHelper.func_181159_b(d1, (double) f) * 180.0D / Math.PI);
            this.prevRotationPitch = this.rotationPitch;
            this.prevRotationYaw = this.rotationYaw;
            this.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch);
            this.ticksInGround = 0;
        }
    }
    @Override
    public void writeEntityToNBT(NBTTagCompound nbttagcompound) {
        nbttagcompound.setShort("xTile", (short) this.xTile);
        nbttagcompound.setShort("yTile", (short) this.yTile);
        nbttagcompound.setShort("zTile", (short) this.zTile);
        nbttagcompound.setShort("life", (short) this.ticksInGround);
        ResourceLocation resourceLocation = (ResourceLocation) Block.blockRegistry.getNameForObject(this.inTile);
        nbttagcompound.setString("inTile", resourceLocation == null ? "" : resourceLocation.toString());
        nbttagcompound.setByte("inData", (byte) this.inData);
        nbttagcompound.setByte("shake", (byte) this.arrowShake);
        nbttagcompound.setByte("inGround", (byte) (this.inGround ? 1 : 0));
        nbttagcompound.setByte("pickup", (byte) this.canBePickedUp);
        nbttagcompound.setDouble("damage", this.damage);
    }
}