package me.km.entities;

import me.km.Server;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.IProjectile;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.projectile.ProjectileHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.DamageSource;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.EntityRayTraceResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;

public class EntityItemProjectile extends ItemEntity implements IProjectile {
    private LivingEntity owner;
    private boolean hit = false;
    private final float damage;
    private Entity ignoreEntity = null;
    private int ignoreTime = 0;

    public EntityItemProjectile(LivingEntity thrower, ItemStack stack, float damage) {
        super(thrower.world, thrower.getPosX(), thrower.getPosY() + thrower.getEyeHeight() - 0.1d, thrower.getPosZ(), stack);
        this.owner = thrower;
        super.setInfinitePickupDelay();
        this.damage = damage;
    }

    public EntityItemProjectile(LivingEntity thrower, ItemStack stack) {
        this(thrower, stack, -1);
    }

    public LivingEntity getItemThrower() {
        return owner;
    }

    public void setHeadingFromThrower(Entity thrower, float rotationPitchIn, float rotationYawIn, float pitchOffset, float velocity, float inaccuracy) {
        float f = -MathHelper.sin(rotationYawIn * ((float) Math.PI / 180.0f)) * MathHelper.cos(rotationPitchIn * ((float) Math.PI / 180.0f));
        float f1 = -MathHelper.sin((rotationPitchIn + pitchOffset) * ((float) Math.PI / 180.0f));
        float f2 = MathHelper.cos(rotationYawIn * ((float) Math.PI / 180.0f)) * MathHelper.cos(rotationPitchIn * ((float) Math.PI / 180.0f));
        shoot(f, f1, f2, velocity, inaccuracy);
        Vec3d vec3d = thrower.getMotion();
        this.setMotion(this.getMotion().add(vec3d.x, thrower.onGround ? 0.0D : vec3d.y, vec3d.z));
    }

    @Override
    public void shoot(double x, double y, double z, float velocity, float inaccuracy) {
        Vec3d vec3d = new Vec3d(x, y, z)
                .normalize()
                .add(rand.nextGaussian() * 0.0075 * inaccuracy, rand.nextGaussian() * 0.0075 * inaccuracy, rand.nextGaussian() * 0.0075 * inaccuracy)
                .scale(velocity);
        setMotion(vec3d);
        float f1 = MathHelper.sqrt(horizontalMag(vec3d));
        this.rotationYaw = (float) (MathHelper.atan2(x, z) * (180.0 / Math.PI));
        this.rotationPitch = (float) (MathHelper.atan2(y, f1) * (180.0 / Math.PI));
        this.prevRotationYaw = this.rotationYaw;
        this.prevRotationPitch = this.rotationPitch;
    }

    private void onImpact(RayTraceResult ray) {
        if(damage == -1) {
            switch(ray.getType()) {
                case ENTITY:
                    Server.scriptEvents.onEntityItemProjectileHit(this, owner, this.getItem(), ((EntityRayTraceResult) ray).getEntity(), null);
                    break;
                case BLOCK:
                    Server.scriptEvents.onEntityItemProjectileHit(this, owner, this.getItem(), null, ((BlockRayTraceResult) ray).getPos());
                    break;
            }
        } else if(ray.getType() == RayTraceResult.Type.ENTITY) {
            ((EntityRayTraceResult) ray).getEntity().attackEntityFrom(DamageSource.causeThrownDamage(this, owner), damage);
        }
    }

    @Override
    public void tick() {
        super.tick();
        if(hit) {
            return;
        }

        AxisAlignedBB axisalignedbb = this.getBoundingBox().expand(this.getMotion()).grow(2.0D);

        for(Entity entity : this.world.getEntitiesInAABBexcluding(this, axisalignedbb, (p) -> !p.isSpectator() && p.canBeCollidedWith())) {
            if(entity == this.ignoreEntity) {
                this.ignoreTime++;
                break;
            }

            if(this.owner != null && this.ticksExisted < 2 && this.ignoreEntity == null) {
                this.ignoreEntity = entity;
                this.ignoreTime = 3;
                break;
            }
        }

        RayTraceResult ray = ProjectileHelper.rayTrace(this, axisalignedbb,
                (p) -> !p.isSpectator() && p.canBeCollidedWith() && p != this.ignoreEntity, RayTraceContext.BlockMode.OUTLINE, true);
        if(this.ignoreEntity != null && this.ignoreTime-- <= 0) {
            this.ignoreEntity = null;
        }

        if(ray.getType() != RayTraceResult.Type.MISS) {
            if(ray.getType() == RayTraceResult.Type.BLOCK && this.world.getBlockState(((BlockRayTraceResult) ray).getPos()).getBlock() == Blocks.NETHER_PORTAL) {
                this.setPortal(((BlockRayTraceResult) ray).getPos());
            } else {
                this.onImpact(ray);
                hit = true;
                this.setDefaultPickupDelay();
            }
        }

        Vec3d vec3d = this.getMotion();
        setRawPosition(getPosX() + vec3d.x, getPosY() + vec3d.y, getPosZ() + vec3d.z);
        float f = MathHelper.sqrt(horizontalMag(vec3d));
        this.rotationYaw = (float) (MathHelper.atan2(vec3d.x, vec3d.z) * (double) (180F / (float) Math.PI));

        for(this.rotationPitch = (float) (MathHelper.atan2(vec3d.y, (double) f) * (double) (180F / (float) 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 = MathHelper.lerp(0.2F, this.prevRotationPitch, this.rotationPitch);
        this.rotationYaw = MathHelper.lerp(0.2F, this.prevRotationYaw, this.rotationYaw);
        float f1;
        if(this.isInWater()) {
            for(int i = 0; i < 4; ++i) {
                this.world.addParticle(ParticleTypes.BUBBLE, getPosX() - vec3d.x * 0.25D, getPosY() - vec3d.y * 0.25D, getPosZ() - vec3d.z * 0.25D, vec3d.x, vec3d.y, vec3d.z);
            }
            f1 = 0.8F;
        } else {
            f1 = 0.99F;
        }

        this.setMotion(vec3d.scale((double) f1));
        if(!this.hasNoGravity()) {
            Vec3d vec3d1 = this.getMotion();
            this.setMotion(vec3d1.x, vec3d1.y - 0.03, vec3d1.z);
        }

        this.setPosition(getPosX(), getPosY(), getPosZ());
    }
}
