package com.zivilon.cinder_loe.mixins;

import com.zivilon.cinder_loe.util.*;
import com.zivilon.cinder_loe.world.event.Warband;
import com.zivilon.cinder_loe.world.event.WarbandLocationInfo;
import com.zivilon.cinder_loe.world.event.WarbandTracker;

import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.*;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.effect.EntityLightningBolt;
import net.minecraft.entity.passive.EntityTameable;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.Potion;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.world.World;

import net.minecraftforge.common.ForgeHooks;

import java.lang.reflect.*;
import java.util.List;
import java.util.UUID;

@Mixin(EntityLivingBase.class)
public abstract class MixinEntityLivingBase extends Entity implements IEntityLivingBase {
    private static boolean checked_cauldron = false;
    private static Method cauldron_method;

    public int despawn_timer = -1;
    public UUID warband_uuid = null;

    @Shadow
    protected int entityAge;
    @Shadow
    public int maxHurtTime;
    @Shadow
    public float attackedAtYaw;
    @Shadow
    protected EntityPlayer attackingPlayer;
    @Shadow
    protected int recentlyHit;
    @Shadow
    public int hurtTime;
    @Shadow
    public float prevHealth;
    @Shadow
    protected float lastDamage;
    @Shadow
    public int maxHurtResistantTime;
    @Shadow
    public float limbSwingAmount;

    @Shadow
    protected abstract float getSoundPitch();
    @Shadow
    protected abstract float getSoundVolume();
    @Shadow
    protected abstract String getHurtSound();
    @Shadow
    public abstract void onDeath(DamageSource source);
    @Shadow
    protected abstract String getDeathSound();
    @Shadow
    public abstract float getHealth();
    @Shadow
    public abstract void knockBack(Entity entity, float strength, double direction_x, double direction_z);
    @Shadow
    public abstract void setRevengeTarget(EntityLivingBase p_70604_1_);
    @Shadow
    protected abstract void damageEntity(DamageSource p_70665_1_, float p_70665_2_);
    @Shadow
    public abstract ItemStack getEquipmentInSlot(int p_71124_1_);
    @Shadow
    public abstract boolean isPotionActive(int p_82165_1_);
    @Shadow
    public abstract boolean isPotionActive(Potion p_70644_1_);

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

    /**
    * @author Shinare
    * @reason Adding damage event method
    */
    @Overwrite
    public boolean attackEntityFrom(DamageSource source, float damage) {
        if (ForgeHooks.onLivingAttack((EntityLivingBase)(Object)this, source, damage)) return false;
        if (this.isEntityInvulnerable()) return false;
        if (this.worldObj.isRemote) return false;
        this.entityAge = 0;

        if (this.getHealth() <= 0.0F) return false;
        if (source.isFireDamage() && isPotionActive(Potion.fireResistance)) return false;

        if (!(checked_cauldron)) {
            check_cauldron();
            checked_cauldron = true;
        }

        DamageEvent event = new DamageEvent(source, damage, (EntityLivingBase)(Object)this, source.getEntity());
        Pair<DamageEvent, Boolean> result = DamageEvent.run_events(event);
        if (result.value) return false;
        damage = result.key.damage;

        if (cauldron_method == null) {
            if ((source == DamageSource.anvil || source == DamageSource.fallingBlock) && this.getEquipmentInSlot(4) != null) {
                this.getEquipmentInSlot(4).damageItem((int)(damage * 4.0F + this.rand.nextFloat() * damage * 2.0F), (EntityLivingBase)(Object)this);
                damage *= 0.75F;
            }
        }

        this.limbSwingAmount = 1.5F;
        boolean flag = true;

        if (this.hurtResistantTime > (float)this.maxHurtResistantTime / 2.0F) {
            if (damage <= this.lastDamage) return false;
            if (cauldron_method != null) {
                boolean cauldron_cancel = entitydamage_cauldron(source, damage - this.lastDamage);
                if (!cauldron_cancel) return false;
            } else {
                this.damageEntity(source, damage - this.lastDamage);
            }
            this.lastDamage = damage;
            flag = false;
        } else {
            if (cauldron_method != null) {
                float previousHealth = getHealth();
                boolean cauldron_cancel = entitydamage_cauldron(source, damage);
                if (!cauldron_cancel) return false;

                this.lastDamage = damage;
                this.prevHealth = previousHealth;
                this.hurtResistantTime = this.maxHurtResistantTime;
                this.hurtTime = this.maxHurtTime = 10;
            } else {
                this.lastDamage = damage;
                this.prevHealth = this.getHealth();
                this.hurtResistantTime = this.maxHurtResistantTime;
                this.damageEntity(source, damage);
                this.hurtTime = this.maxHurtTime = 10;
            }
        }

        this.attackedAtYaw = 0.0F;
        Entity entity = source.getEntity();

        if (entity != null) {
            if (entity instanceof EntityLivingBase)
                this.setRevengeTarget((EntityLivingBase)entity);

            if (entity instanceof EntityPlayer) {
                this.recentlyHit = 100;
                this.attackingPlayer = (EntityPlayer)entity;
            } else if (entity instanceof EntityTameable) {
                EntityTameable entitywolf = (EntityTameable)entity;

                if (entitywolf.isTamed()) {
                    this.recentlyHit = 100;
                    this.attackingPlayer = null;
                }
            }
        }

        if (flag) {
            this.worldObj.setEntityState((Entity)this, (byte)2);
            if (source != DamageSource.drown)
                this.setBeenAttacked();

            if (entity != null) {
                double d1 = entity.posX - this.posX;
                double d0;

                for (d0 = entity.posZ - this.posZ; d1 * d1 + d0 * d0 < 1.0E-4D; d0 = (Math.random() - Math.random()) * 0.01D)
                    d1 = (Math.random() - Math.random()) * 0.01D;

                this.attackedAtYaw = (float)(Math.atan2(d0, d1) * 180.0D / Math.PI) - this.rotationYaw;
                this.knockBack(entity, damage, d1, d0);
            } else {
                this.attackedAtYaw = (float)((int)(Math.random() * 2.0D) * 180);
            }
        }

        String sound;

        if (getHealth() <= 0.0F) {
            sound = getDeathSound();

            if (flag && sound != null)
                this.playSound(sound, getSoundVolume(), getSoundPitch());

            onDeath(source);
        } else {
            sound = getHurtSound();

            if (flag && sound != null)
                this.playSound(sound, getSoundVolume(), getSoundPitch());
        }

        return true;
    }

    @Dynamic
    private void check_cauldron() {
        try {
            Class<?> target_class = EntityLivingBase.class;
            Method[] methods = target_class.getDeclaredMethods();

            for (Method method : methods) {
                if (method.getName().equals("damageEntity_CB")) {
                    cauldron_method = method;
                    return;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return;
    }

    @Dynamic
    public boolean entitydamage_cauldron(DamageSource source, float damage) {
        boolean return_value = true;
        if (cauldron_method != null) {
            try {
                Object result = cauldron_method.invoke(this, source, damage);
                if (result instanceof Boolean) {  // Check if the result is a boolean
                    return_value = (Boolean) result;
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
            }
        }
        return return_value;
    }

    /**
    * Add support for despawn after specified time loaded
    * Add support for following a warband leader
    */
    @Inject(method = "writeEntityToNBT", at = @At("TAIL"))
    private void writeWarbandNBT(NBTTagCompound compound, CallbackInfo ci) {
        compound.setInteger("DespawnTimer", despawn_timer);
        compound.setString("WarbandLeader", warband_uuid != null ? warband_uuid.toString() : "");
    }
    @Inject(method = "readEntityFromNBT", at = @At("TAIL"))
    private void readWarbandNBT(NBTTagCompound compound, CallbackInfo ci) {
        despawn_timer = compound.getInteger("DespawnTimer");
        if (compound.hasKey("WarbandLeader")) {
            String idStr = compound.getString("WarbandLeader");
            if (!idStr.isEmpty()) warband_uuid = UUID.fromString(idStr);
        }
    }

    public void setDead() {
        super.setDead();
        if (this.warband_uuid != null && this.warband_uuid.equals(this.getUniqueID())) {
            WarbandLocationInfo info = WarbandTracker.locations.get(this.warband_uuid);
            String faction_name = "";
            if (info != null && info.warband != null && info.warband.faction != null) {
                faction_name = info.warband.faction.faction.untranslatedFactionName();
            }
            ChatComponentTranslation message;
            if (!faction_name.equals("")) {
                message = new ChatComponentTranslation(
                    "warband.defeated.faction",
                    new ChatComponentTranslation(faction_name)
                );
            } else {
                message = new ChatComponentTranslation(
                    "warband.defeated.no_faction"
                );
            }
            message.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.GRAY));
            MinecraftServer.getServer().getConfigurationManager().sendChatMsg(message);
            WarbandTracker.remove(this.warband_uuid);
        }
    }

    @Inject(method = "onUpdate", at = @At("TAIL"))
    private void onUpdate(CallbackInfo ci) {
        if (despawn_timer > -1) {
            despawn_timer--;
            if (despawn_timer == 0) {
                ((EntityLivingBase)(Object)this).setDead();
            }
        }
    }
    @Override
    public void set_despawn_timer(int ticks) {
        this.despawn_timer = ticks;
    }
    @Override
    public void set_warband_uuid(UUID uuid) {
        this.warband_uuid = uuid;
    }
    @Override
    public UUID get_warband_uuid() {
        return this.warband_uuid;
    }
}
