package me.km.entities;

import me.km.Server;
import me.km.overrides.ModEntityPlayerMP;
import net.minecraft.entity.Entity;
import net.minecraft.entity.CreatureEntity;
import net.minecraft.entity.EntitySize;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.Pose;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.goal.LookAtGoal;
import net.minecraft.entity.ai.goal.LookRandomlyGoal;
import net.minecraft.entity.ai.goal.SwimGoal;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraft.util.SoundEvents;

public class EntityHuman extends CreatureEntity {
    private static final DataParameter<String> SKIN_NAME = EntityDataManager.createKey(EntityHuman.class, DataSerializers.STRING);
    private static final DataParameter<Float> SCALE = EntityDataManager.createKey(EntityHuman.class, DataSerializers.FLOAT);
    private static final DataParameter<Boolean> SLIM = EntityDataManager.createKey(EntityHuman.class, DataSerializers.BOOLEAN);

    private final float originalWidth;
    private final float originalHeight;
    private EntitySize size;

    public EntityHuman(EntityType<EntityHuman> type, World w) {
        super(type, w);
        this.originalWidth = getWidth();
        this.originalHeight = getHeight();
        size = ModEntities.HUMAN.getSize();
    }

    @Override
    protected void registerData() {
        super.registerData();
        this.getDataManager().register(SKIN_NAME, "Steve");
        this.getDataManager().register(SCALE, 1.0f);
        this.getDataManager().register(SLIM, false);
    }

    // -------------------------------------------------------------------------
    // size modifier
    // -------------------------------------------------------------------------
    public boolean isSlim() {
        return this.getDataManager().get(SLIM);
    }

    public void setSlim(boolean slim) {
        this.getDataManager().set(SLIM, slim);
    }

    public final float getScale() {
        return this.getDataManager().get(SCALE);
    }

    public final void setScale(float scale) {
        this.getDataManager().set(SCALE, scale);
        updateSize(scale);
    }

    private void updateSize(float scale) {
        this.setSize(originalWidth * scale, originalHeight * scale);
    }

    public void setSkinName(String name) {
        this.getDataManager().set(SKIN_NAME, name);
    }

    public String getSkinName() {
        return this.getDataManager().get(SKIN_NAME);
    }

    // -------------------------------------------------------------------------
    // texture stuff
    // -------------------------------------------------------------------------
    @OnlyIn(Dist.CLIENT)
    public ResourceLocation getTexture() {
        return HumanSkinLoader.INSTANCE.getTexture(getSkinName());
    }

    @Override
    public void readAdditional(CompoundNBT com) {
        super.readAdditional(com);
        if(com.contains("HumanName")) {
            setSkinName(com.getString("HumanName"));
        }
        if(com.contains("Scale")) {
            setScale(com.getFloat("Scale"));
        }
        if(com.contains("Slim")) {
            setSlim(com.getBoolean("Slim"));
        }
    }

    @Override
    public void writeAdditional(CompoundNBT com) {
        super.writeAdditional(com);
        com.putString("HumanName", getSkinName());
        com.putFloat("Scale", getScale());
        com.putBoolean("Slim", isSlim());
    }

    @Override
    public void addVelocity(double x, double y, double z) {
    }

    @Override
    public void knockBack(Entity entityIn, float strength, double xRatio, double zRatio) {
    }

    @Override
    public void applyEntityCollision(Entity entityIn) {
    }

    @Override
    public boolean attackEntityFrom(DamageSource source, float amount) {
        if(source == DamageSource.OUT_OF_WORLD) {
            this.remove();
            return false;
        }
        if(!world.isRemote) {
            Server.scriptEvents.onHumanHurt(source.getTrueSource(), this);
        }
        return false;
    }

    @Override
    protected void registerGoals() {
        this.goalSelector.addGoal(0, new SwimGoal(this));
        this.goalSelector.addGoal(1, new LookAtGoal(this, PlayerEntity.class, 6.0F));
        this.goalSelector.addGoal(2, new LookRandomlyGoal(this));
    }

    @Override
    protected void registerAttributes() {
        super.registerAttributes();
        this.getAttributes().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(1);
        this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.1d);
        this.getAttributes().registerAttribute(SharedMonsterAttributes.ATTACK_SPEED);
    }

    @Override
    protected SoundEvent getSwimSound() {
        return SoundEvents.ENTITY_PLAYER_SWIM;
    }

    @Override
    protected SoundEvent getSplashSound() {
        return SoundEvents.ENTITY_PLAYER_SPLASH;
    }

    @Override
    public SoundCategory getSoundCategory() {
        return SoundCategory.NEUTRAL;
    }

    @Override
    protected SoundEvent getHurtSound(DamageSource ds) {
        if(ds == DamageSource.ON_FIRE) {
            return SoundEvents.ENTITY_PLAYER_HURT_ON_FIRE;
        } else {
            return ds == DamageSource.DROWN ? SoundEvents.ENTITY_PLAYER_HURT_DROWN : SoundEvents.ENTITY_PLAYER_HURT;
        }
    }

    @Override
    protected SoundEvent getDeathSound() {
        return SoundEvents.ENTITY_PLAYER_DEATH;
    }

    @Override
    public double getYOffset() {
        return -0.35D;
    }

    @Override
    public float getAIMoveSpeed() {
        return (float) this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getValue();
    }

    @Override
    protected SoundEvent getFallSound(int heightIn) {
        return heightIn > 4 ? SoundEvents.ENTITY_PLAYER_BIG_FALL : SoundEvents.ENTITY_PLAYER_SMALL_FALL;
    }

    @OnlyIn(Dist.CLIENT)
    @Override
    public boolean getAlwaysRenderNameTagForRender() {
        return true;
    }

    @Override
    public ITextComponent getDisplayName() {
        return getName();
    }

    @Override
    public float getEyeHeight(Pose p_213307_1_) {
        float f = getDefaultEyeHeight();
        if(getHeight() == 1.65F) {
            f -= 0.08F;
        }
        f *= getScale();
        return f;
    }

    public float getDefaultEyeHeight() {
        return 1.62F;
    }

    @Override
    public boolean canDespawn(double distanceToClosestPlayer) {
        return false;
    }

    @Override
    public EntitySize getSize(Pose poseIn) {
        return size;
    }

    private void setSize(float w, float h) {
        size = EntitySize.flexible(w, h);
        recalculateSize();
    }

    @Override
    public void notifyDataManagerChange(DataParameter<?> key) {
        super.notifyDataManagerChange(key);
        if(SCALE.equals(key)) {
            updateSize(getScale());
        }
    }
}
