package tc.oc.pgm.portals;

import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import tc.oc.commons.bukkit.chat.BukkitSound;
import tc.oc.pgm.features.FeatureDefinition;
import tc.oc.pgm.features.FeatureInfo;
import tc.oc.pgm.filters.Filter;
import tc.oc.pgm.filters.FilterMatchModule;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.match.MatchPlayer;

@FeatureInfo(name = "portal")
public interface Portal extends FeatureDefinition {}

class PortalImpl extends FeatureDefinition.Impl implements Portal {
    private static final BukkitSound USE_SOUND = new BukkitSound(Sound.ENTITY_ENDERMEN_TELEPORT, 1f, 1f);

    private final @Inspect Filter trigger;
    private final @Inspect Filter participantFilter, observerFilter;
    private final @Inspect PortalTransform transform;
    private final @Inspect boolean sound;
    private final @Inspect boolean smooth;

    public PortalImpl(Filter trigger,
                      PortalTransform transform,
                      Filter participantFilter,
                      Filter observerFilter,
                      boolean sound,
                      boolean smooth) {

        this.transform = transform;
        this.trigger = trigger;
        this.participantFilter = participantFilter;
        this.observerFilter = observerFilter;
        this.sound = sound;
        this.smooth = smooth;
    }

    private boolean canUse(MatchPlayer player) {
        return (player.isParticipating() ? participantFilter : observerFilter).query(player).isAllowed();
    }

    @Override
    public void load(Match match) {
        final FilterMatchModule fmm = match.needMatchModule(FilterMatchModule.class);
        fmm.onRise(MatchPlayer.class, trigger, player -> {
            if(canUse(player) && !player.facet(PortalPlayerFacet.class).teleport()) {
                teleportPlayer(player, player.getBukkit().getEntityLocation());
            }
        });
    }

    private void teleportPlayer(final MatchPlayer player, final EntityLocation from) {
        final EntityLocation to = transform.apply(from);
        final Player bukkit = player.getBukkit();
        final Match match = player.getMatch();

        if(sound) {
            // Don't play the sound for the teleporting player at the entering portal,
            // because they will instantly teleport away and hear the one at the exit.
            for(MatchPlayer listener : match.getPlayers()) {
                if(!player.equals(listener) && listener.getBukkit().canSee(player.getBukkit())) {
                    listener.playSound(USE_SOUND, from);
                }
            }
        }

        // Use ENDER_PEARL as the cause so that this teleport is treated
        // as an "in-game" movement
        if(smooth) {
            bukkit.teleportRelative(to.toVector().subtract(from.toVector()),
                                    to.getYaw() - from.getYaw(),
                                    to.getPitch() - from.getPitch(),
                                    PlayerTeleportEvent.TeleportCause.ENDER_PEARL);
        } else {
            bukkit.teleport(to, PlayerTeleportEvent.TeleportCause.ENDER_PEARL);
        }
        // Reset fall distance
        bukkit.setFallDistance(0);

        if(sound) {
            for(MatchPlayer listener : match.getPlayers()) {
                if(listener.getBukkit().canSee(player.getBukkit())) {
                    listener.playSound(USE_SOUND, to);
                }
            }
        }
    }
}
