package com.tiedup.remake.compat.wildfire.physics;

import com.wildfire.api.IGenderArmor;
import com.wildfire.main.GenderPlayer;
import com.wildfire.main.WildfireHelper;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.phys.Vec3;

/**
 * Re-implementation of Wildfire's BreastPhysics adapted for LivingEntity (NPCs).
 * The original class strictly requires Player, which crashes for EntityDamsel.
 */
public class NpcBreastPhysics {

    private float bounceVel = 0,
        targetBounceY = 0,
        velocity = 0,
        wfg_femaleBreast,
        wfg_preBounce;
    private float bounceRotVel = 0,
        targetRotVel = 0,
        rotVelocity = 0,
        wfg_bounceRotation,
        wfg_preBounceRotation;
    private float bounceVelX = 0,
        targetBounceX = 0,
        velocityX = 0,
        wfg_femaleBreastX,
        wfg_preBounceX;

    private boolean justSneaking = false; // alreadySleeping logic removed (mobs rarely sleep in beds)

    private float breastSize = 0,
        preBreastSize = 0;

    private Vec3 prePos;
    private int lastTick = -1;
    private final GenderPlayer genderPlayer;

    public NpcBreastPhysics(GenderPlayer genderPlayer) {
        this.genderPlayer = genderPlayer;
    }

    public void update(LivingEntity entity, IGenderArmor armor) {
        // Run physics only once per tick to ensure correct motion delta calculation
        if (entity.tickCount == this.lastTick) {
            return;
        }
        this.lastTick = entity.tickCount;

        this.wfg_preBounce = this.wfg_femaleBreast;
        this.wfg_preBounceX = this.wfg_femaleBreastX;
        this.wfg_preBounceRotation = this.wfg_bounceRotation;
        this.preBreastSize = this.breastSize;

        if (this.prePos == null) {
            this.prePos = entity.position();
            return;
        }

        // Logic copied and adapted from BreastPhysics.update
        float breastWeight = genderPlayer.getBustSize() * 1.25f;
        float targetBreastSize = genderPlayer.getBustSize();

        if (!genderPlayer.getGender().canHaveBreasts()) {
            targetBreastSize = 0;
        } else if (!genderPlayer.getArmorPhysicsOverride()) {
            float tightness = Mth.clamp(armor.tightness(), 0, 1);
            targetBreastSize *= 1 - 0.15F * tightness;
        }

        if (breastSize < targetBreastSize) {
            breastSize += Math.abs(breastSize - targetBreastSize) / 2f;
        } else {
            breastSize -= Math.abs(breastSize - targetBreastSize) / 2f;
        }

        Vec3 motion = entity.position().subtract(this.prePos);
        this.prePos = entity.position();

        float bounceIntensity =
            (targetBreastSize * 3f) * genderPlayer.getBounceMultiplier();
        if (!genderPlayer.getArmorPhysicsOverride()) {
            float resistance = Mth.clamp(armor.physicsResistance(), 0, 1);
            bounceIntensity *= 1 - resistance;
        }

        if (!genderPlayer.getBreasts().isUniboob()) {
            bounceIntensity =
                bounceIntensity * WildfireHelper.randFloat(0.5f, 1.5f);
        }

        // Falling logic
        if (entity.fallDistance > 0) {
            // Random bounce removed for NPCs to avoid jitter, simplified falling
        }

        this.targetBounceY = (float) motion.y * bounceIntensity;
        this.targetBounceY += breastWeight;

        this.targetRotVel =
            -((entity.yBodyRot - entity.yBodyRotO) / 15f) * bounceIntensity;

        // Walking bounce simulation
        float f = (float) entity.getDeltaMovement().lengthSqr() / 0.2F;
        f = f * f * f;
        if (f < 1.0F) {
            f = 1.0F;
        }

        // Use walkAnimation position directly from LivingEntity
        this.targetBounceY +=
            (Mth.cos(
                    entity.walkAnimation.position() * 0.6662F + (float) Math.PI
                ) *
                0.5F *
                entity.walkAnimation.speed() *
                0.5F) /
            f;

        // Sneaking logic
        if (entity.getPose() == Pose.CROUCHING && !this.justSneaking) {
            this.justSneaking = true;
            this.targetBounceY += bounceIntensity;
        }
        if (entity.getPose() != Pose.CROUCHING && this.justSneaking) {
            this.justSneaking = false;
            this.targetBounceY += bounceIntensity;
        }

        // Physics calculation
        float percent = genderPlayer.getFloppiness();
        float bounceAmount = 0.45f * (1f - percent) + 0.15f;
        bounceAmount = Mth.clamp(bounceAmount, 0.15f, 0.6f);
        float delta = 2.25f - bounceAmount;

        float distanceFromMin = Math.abs(bounceVel + 0.5f) * 0.5f;
        float distanceFromMax = Math.abs(bounceVel - 2.65f) * 0.5f;

        if (bounceVel < -0.5f) {
            targetBounceY += distanceFromMin;
        }
        if (bounceVel > 2.5f) {
            targetBounceY -= distanceFromMax;
        }
        if (targetBounceY < -1.5f) targetBounceY = -1.5f;
        if (targetBounceY > 2.5f) targetBounceY = 2.5f;
        if (targetRotVel < -25f) targetRotVel = -25f;
        if (targetRotVel > 25f) targetRotVel = 25f;

        this.velocity = Mth.lerp(
            bounceAmount,
            this.velocity,
            (this.targetBounceY - this.bounceVel) * delta
        );
        this.bounceVel += this.velocity * percent * 1.1625f;

        // X
        this.velocityX = Mth.lerp(
            bounceAmount,
            this.velocityX,
            (this.targetBounceX - this.bounceVelX) * delta
        );
        this.bounceVelX += this.velocityX * percent;

        this.rotVelocity = Mth.lerp(
            bounceAmount,
            this.rotVelocity,
            (this.targetRotVel - this.bounceRotVel) * delta
        );
        this.bounceRotVel += this.rotVelocity * percent;

        this.wfg_bounceRotation = this.bounceRotVel;
        this.wfg_femaleBreastX = this.bounceVelX;
        this.wfg_femaleBreast = this.bounceVel;
    }

    public float getBreastSize(float partialTicks) {
        return Mth.lerp(partialTicks, preBreastSize, breastSize);
    }

    public float getPreBounceY() {
        return this.wfg_preBounce;
    }

    public float getBounceY() {
        return this.wfg_femaleBreast;
    }

    public float getPreBounceX() {
        return this.wfg_preBounceX;
    }

    public float getBounceX() {
        return this.wfg_femaleBreastX;
    }

    public float getPreBounceRotation() {
        return this.wfg_preBounceRotation;
    }

    public float getBounceRotation() {
        return this.wfg_bounceRotation;
    }
}
