package tc.oc.pgm.physics;

import java.util.Optional;
import javax.inject.Inject;

import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.util.Vector;
import tc.oc.commons.bukkit.event.targeted.TargetedEventHandler;
import tc.oc.pgm.damage.DamageDisplayPlayerFacet;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.match.MatchPlayer;
import tc.oc.pgm.match.MatchPlayerFacet;

public class KnockbackPlayerFacet implements MatchPlayerFacet, Listener {

    private final Optional<KnockbackSettings> knockback;
    private final Match match;
    private final Player victim;

    @Inject KnockbackPlayerFacet(Optional<KnockbackSettings> knockback, Match match, Player victim) {
        this.knockback = knockback;
        this.match = match;
        this.victim = victim;
    }

    @TargetedEventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onKnockback(PlayerKnockbackEvent event) {
        if(knockback.isPresent() &&
           victim.equals(event.getPlayer()) &&
           event.getDamager() instanceof LivingEntity) {

            event.setCancelled(true);
        }
    }

    @TargetedEventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onMelee(EntityDamageByEntityEvent event) {
        if(knockback.isPresent() &&
           victim.equals(event.getEntity()) &&
           event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK &&
           event.getDamager() instanceof LivingEntity &&
           victim.getNoDamageTicks() <= 0) {

            applyImpulses((LivingEntity) event.getDamager());
        }
    }

    private void applyImpulses(LivingEntity attacker) {
        final KnockbackSettings knockback = this.knockback.get();

        final Vector normal = victim.getLocation().subtract(attacker.getLocation()).toVector();
        if(normal.isZero()) return;
        normal.normalize();

        final Vector victimNormal = knockback.pitchedNormal(normal);
        final Vector attackerNormal = normal.times(-1);

        if(victimNormal.isZero() || attackerNormal.isZero()) return;

        final boolean ground = attacker.isOnGround();
        final double attackSpeed = Math.max(0, attacker.getPredictedVelocity().dot(normal));
        final boolean sprint = ground && attackSpeed > knockback.sprintThreshold;

        victim.applyImpulse(victimNormal.multiply(knockback.power(sprint)), true);
        attacker.applyImpulse(attackerNormal.multiply(knockback.recoil(ground) * attackSpeed), true);

        final MatchPlayer matchAttacker = match.getPlayer(attacker);
        if(matchAttacker != null) {
            matchAttacker.facet(DamageDisplayPlayerFacet.class).showKnockback(victim, sprint);
        }
    }
}
