package com.zivilon.cinder_loe.mixins;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Overwrite;

import lotr.common.item.LOTRWeaponStats;
import lotr.common.enchant.LOTREnchantmentHelper;
import lotr.common.entity.projectile.LOTREntityProjectileBase;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IProjectile;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.monster.EntityEnderman;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.S2BPacketChangeGameState;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.DamageSource;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;

import java.util.List;

@Mixin(LOTREntityProjectileBase.class)
public abstract class MixinLOTREntityProjectileBase extends Entity {

    public MixinLOTREntityProjectileBase(World world) {
        super(world);
    }

    @Shadow(remap = false)
    public abstract void setIsCritical(boolean p_70243_1_);
    @Shadow(remap = false)
    public abstract boolean getIsCritical();
    @Shadow(remap = false)
    public abstract float getSpeedReduction();
    @Shadow(remap = false)
    public abstract String getImpactSound();
    @Shadow(remap = false)
    protected abstract void onCollideWithTarget(Entity entity);
    @Shadow(remap = false)
    protected abstract float getKnockbackFactor();
    @Shadow(remap = false)
    public abstract ItemStack getProjectileItem();
    @Shadow(remap = false)
    public abstract DamageSource getDamageSource();
    @Shadow(remap = false)
    public abstract int maxTicksInGround();
    @Shadow(remap = false)
    public abstract float getBaseImpactDamage(Entity paramEntity, ItemStack paramItemStack);

    @Shadow(remap = false)
    private int xTile = -1;
    @Shadow(remap = false)
    private int yTile = -1;
    @Shadow(remap = false)
    private int zTile = -1;
    @Shadow(remap = false)
    private Block inTile;
    @Shadow(remap = false)
    private int inData;
    @Shadow(remap = false)
    private boolean inGround;
    @Shadow(remap = false)
    public int shake;
    @Shadow(remap = false)
    public Entity shootingEntity;
    @Shadow(remap = false)
    private int ticksInGround;
    @Shadow(remap = false)
    private int ticksInAir;
    @Shadow(remap = false)
    private int knockbackStrength;


    /**
    * @author Shinare
    * @reason Having full knockbackResistance attribute now negates bonus knockback as well
    */
    @Overwrite(remap = false)
    public void func_70071_h_() {
        super.onUpdate();
        if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) {
            float f = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
            this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI);
            this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(this.motionY, f) * 180.0D / Math.PI);
        }
        Block block = this.worldObj.getBlock(this.xTile, this.yTile, this.zTile);
        if (block != Blocks.air) {
            block.setBlockBoundsBasedOnState((IBlockAccess)this.worldObj, this.xTile, this.yTile, this.zTile);
            AxisAlignedBB axisalignedbb = block.getCollisionBoundingBoxFromPool(this.worldObj, this.xTile, this.yTile, this.zTile);
            if (axisalignedbb != null && axisalignedbb.isVecInside(Vec3.createVectorHelper(this.posX, this.posY, this.posZ)))
                this.inGround = true;
        }
        if (this.shake > 0)
            this.shake--;
        if (this.inGround) {
            Block j = this.worldObj.getBlock(this.xTile, this.yTile, this.zTile);
            int k = this.worldObj.getBlockMetadata(this.xTile, this.yTile, this.zTile);
            if (j == this.inTile && k == this.inData) {
                this.ticksInGround++;
                if (this.ticksInGround >= maxTicksInGround())
                    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;
            }
        } else {
            this.ticksInAir++;
            Vec3 vec3d = Vec3.createVectorHelper(this.posX, this.posY, this.posZ);
            Vec3 vec3d1 = Vec3.createVectorHelper(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
            MovingObjectPosition movingobjectposition = this.worldObj.func_147447_a(vec3d, vec3d1, false, true, false);
            vec3d = Vec3.createVectorHelper(this.posX, this.posY, this.posZ);
            vec3d1 = Vec3.createVectorHelper(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
            if (movingobjectposition != null)
                vec3d1 = Vec3.createVectorHelper(movingobjectposition.hitVec.xCoord, movingobjectposition.hitVec.yCoord, movingobjectposition.hitVec.zCoord);
            Entity entity = null;
            List<Entity> list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D));
            double d = 0.0D;
            int l;
            for (l = 0; l < list.size(); l++) {
                Entity entity1 = list.get(l);
                if (entity1.canBeCollidedWith() && (entity1 != this.shootingEntity || this.ticksInAir >= 5)) {
                    float f5 = 0.3F;
                    AxisAlignedBB axisalignedbb1 = entity1.boundingBox.expand(f5, f5, f5);
                    MovingObjectPosition movingobjectposition1 = axisalignedbb1.calculateIntercept(vec3d, vec3d1);
                    if (movingobjectposition1 != null) {
                        double d1 = vec3d.distanceTo(movingobjectposition1.hitVec);
                        if (d1 < d || d == 0.0D) {
                            entity = entity1;
                            d = d1;
                        }
                    }
                }
            }
            if (entity != null)
                movingobjectposition = new MovingObjectPosition(entity);
            if (movingobjectposition != null && movingobjectposition.entityHit instanceof EntityPlayer) {
                EntityPlayer entityplayer = (EntityPlayer)movingobjectposition.entityHit;
                if (entityplayer.capabilities.disableDamage || (this.shootingEntity instanceof EntityPlayer && !((EntityPlayer)this.shootingEntity).canAttackPlayer(entityplayer)))
                    movingobjectposition = null;
            }
            if (movingobjectposition != null) {
                Entity hitEntity = movingobjectposition.entityHit;
                if (hitEntity != null) {
                    ItemStack itemstack = getProjectileItem();
                    int damageInt = MathHelper.ceiling_double_int(getBaseImpactDamage(hitEntity, itemstack));
                    int fireAspect = 0;
                    if (itemstack != null)
                        if (this.shootingEntity instanceof EntityLivingBase && hitEntity instanceof EntityLivingBase) {
                            this.knockbackStrength += EnchantmentHelper.getKnockbackModifier((EntityLivingBase)this.shootingEntity, (EntityLivingBase)hitEntity);
                        } else {
                            this.knockbackStrength += LOTRWeaponStats.getTotalKnockback(itemstack);
                        }
                    if (getIsCritical())
                        damageInt += this.rand.nextInt(damageInt / 2 + 2);
                    double[] prevMotion = { hitEntity.motionX, hitEntity.motionY, hitEntity.motionZ };
                    DamageSource damagesource = getDamageSource();
                    if (hitEntity.attackEntityFrom(damagesource, damageInt)) {
                        double[] newMotion = { hitEntity.motionX, hitEntity.motionY, hitEntity.motionZ };
                        float kbf = getKnockbackFactor();
                        hitEntity.motionX = prevMotion[0] + (newMotion[0] - prevMotion[0]) * kbf;
                        hitEntity.motionY = prevMotion[1] + (newMotion[1] - prevMotion[1]) * kbf;
                        hitEntity.motionZ = prevMotion[2] + (newMotion[2] - prevMotion[2]) * kbf;
                        if (isBurning())
                            hitEntity.setFire(5);
                        if (hitEntity instanceof EntityLivingBase) {
                            EntityLivingBase hitEntityLiving = (EntityLivingBase)hitEntity;
                            double knockback_resistance = hitEntityLiving.getEntityAttribute(SharedMonsterAttributes.knockbackResistance).getAttributeValue();
                            if (this.knockbackStrength > 0 && knockback_resistance < 1.0D) {
                                float knockback = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
                                if (knockback > 0.0F)
                                    hitEntityLiving.addVelocity(this.motionX * this.knockbackStrength * 0.6D / knockback, 0.1D, this.motionZ * this.knockbackStrength * 0.6D / knockback);
                            }
                            if (fireAspect > 0)
                                hitEntityLiving.setFire(fireAspect * 4);
                            if (this.shootingEntity instanceof EntityLivingBase) {
                                EnchantmentHelper.func_151384_a(hitEntityLiving, this.shootingEntity);
                                EnchantmentHelper.func_151385_b((EntityLivingBase)this.shootingEntity, (Entity)hitEntityLiving);
                            }
                            if (this.shootingEntity instanceof EntityPlayerMP && hitEntityLiving instanceof EntityPlayer)
                                ((EntityPlayerMP)this.shootingEntity).playerNetServerHandler.sendPacket((Packet)new S2BPacketChangeGameState(6, 0.0F));
                        }
                        this.worldObj.playSoundAtEntity(this, getImpactSound(), 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
                        onCollideWithTarget(hitEntity);
                    } else {
                        this.motionX *= -0.1D;
                        this.motionY *= -0.1D;
                        this.motionZ *= -0.1D;
                        this.rotationYaw += 180.0F;
                        this.prevRotationYaw += 180.0F;
                        this.ticksInAir = 0;
                    }
                } else {
                    this.xTile = movingobjectposition.blockX;
                    this.yTile = movingobjectposition.blockY;
                    this.zTile = movingobjectposition.blockZ;
                    this.inTile = this.worldObj.getBlock(this.xTile, this.yTile, this.zTile);
                    this.inData = this.worldObj.getBlockMetadata(this.xTile, this.yTile, this.zTile);
                    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 f2 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
                    this.posX -= this.motionX / f2 * 0.05D;
                    this.posY -= this.motionY / f2 * 0.05D;
                    this.posZ -= this.motionZ / f2 * 0.05D;
                    this.worldObj.playSoundAtEntity(this, getImpactSound(), 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
                    this.inGround = true;
                    this.shake = 7;
                    setIsCritical(false);
                    if (this.inTile.getMaterial() != Material.air)
                        this.inTile.onEntityCollidedWithBlock(this.worldObj, this.xTile, this.yTile, this.zTile, this);
                }
            }
            if (getIsCritical())
                for (l = 0; l < 4; l++)
                    this.worldObj.spawnParticle("crit", this.posX + this.motionX * l / 4.0D, this.posY + this.motionY * l / 4.0D, this.posZ + this.motionZ * l / 4.0D, -this.motionX, -this.motionY + 0.2D, -this.motionZ);
            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)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI);
            for (this.rotationPitch = (float)(Math.atan2(this.motionY, f3) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F);
            for (; this.rotationPitch - this.prevRotationPitch >= 180.0F; this.prevRotationPitch += 360.0F);
            for (; this.rotationYaw - this.prevRotationYaw < -180.0F; this.prevRotationYaw -= 360.0F);
            for (; 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;
            float f4 = getSpeedReduction();
            if (isInWater()) {
                for (int k1 = 0; k1 < 4; k1++) {
                    float f7 = 0.25F;
                    this.worldObj.spawnParticle("bubble", this.posX - this.motionX * f7, this.posY - this.motionY * f7, this.posZ - this.motionZ * f7, this.motionX, this.motionY, this.motionZ);
                }
                f4 = 0.8F;
            }
            this.motionX *= f4;
            this.motionY *= f4;
            this.motionZ *= f4;
            this.motionY -= 0.05000000074505806D;
            setPosition(this.posX, this.posY, this.posZ);
            func_145775_I();
        }
    }
}
