package pokefenn.totemic.apiimpl.music;

import java.util.Comparator;
import java.util.Optional;

import javax.annotation.Nullable;

import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import pokefenn.totemic.api.music.MusicAPI;
import pokefenn.totemic.api.music.MusicAcceptor;
import pokefenn.totemic.api.music.MusicInstrument;
import pokefenn.totemic.tileentity.totem.TileTotemBase;
import pokefenn.totemic.util.EntityUtil;
import pokefenn.totemic.util.TotemUtil;

public class MusicApiImpl implements MusicAPI
{
    @Override
    public boolean playMusic(World world, double x, double y, double z, @Nullable Entity entity, MusicInstrument instr)
    {
        return playMusic0(world, x, y, z, entity, instr, DEFAULT_RANGE, instr.getBaseOutput());
    }

    @Override
    public boolean playMusic(Entity entity, MusicInstrument instr)
    {
        return playMusic0(entity.world, entity.posX, entity.posY, entity.posZ, entity, instr, DEFAULT_RANGE, instr.getBaseOutput());
    }

    @Override
    public boolean playMusic(World world, BlockPos pos, @Nullable Entity entity, MusicInstrument instr)
    {
        return playMusic(world, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, entity, instr);
    }

    @Override
    public boolean playMusic0(World world, double x, double y, double z, @Nullable Entity entity, MusicInstrument instr, int range, int amount)
    {
        return getClosestAcceptor(world, x, y, z, range, range)
                .map(acc -> acc.acceptMusic(instr, amount, x, y, z, entity))
                .orElse(false);
    }

    @Override
    public boolean playSelector(World world, double x, double y, double z, @Nullable Entity entity, MusicInstrument instr)
    {
        return playSelector(world, x, y, z, entity, instr, DEFAULT_RANGE);
    }

    @Override
    public boolean playSelector(Entity entity, MusicInstrument instr)
    {
        return playSelector(entity.world, entity.posX, entity.posY, entity.posZ, entity, instr, DEFAULT_RANGE);
    }

    @Override
    public boolean playSelector(World world, BlockPos pos, @Nullable Entity entity, MusicInstrument instr)
    {
        return playSelector(world, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, entity, instr);
    }

    @Override
    public boolean playSelector(World world, double x, double y, double z, @Nullable Entity entity, MusicInstrument instr, int range)
    {
        Optional<TileTotemBase> totem = getClosestAcceptor(world, x, y, z, range, range)
                .filter(acc -> acc instanceof TileTotemBase)
                .map(acc -> (TileTotemBase) acc)
                .filter(TileTotemBase::canSelect);
        totem.ifPresent(t -> t.addSelector(entity, instr));
        return totem.isPresent();
    }

    @Override
    public Optional<MusicAcceptor> getClosestAcceptor(World world, double x, double y, double z, int horizontalRadius, int verticalRadius)
    {
        return EntityUtil.getTileEntitiesInRange(TileEntity.class, world, new BlockPos(x, y, z), horizontalRadius, verticalRadius).stream()
                .filter(te -> te instanceof MusicAcceptor)
                .min(Comparator.comparing(te -> te.getDistanceSq(x, y, z)))
                .map(te -> (MusicAcceptor) te);
    }

    //Overrides of deprecated methods below
    @SuppressWarnings("deprecation")
    @Override
    public void playMusicForSelector(World world, double x, double y, double z, @Nullable Entity entity, MusicInstrument instr, int bonusRadius)
    {
        playSelector(world, x, y, z, entity, instr, instr.getBaseRange() + bonusRadius);
    }

    @SuppressWarnings("deprecation")
    @Override
    public void playMusicForSelector(Entity entity, MusicInstrument instr, int bonusRadius)
    {
        playSelector(entity.world, entity.posX, entity.posY, entity.posZ, entity, instr, instr.getBaseRange() + bonusRadius);
    }

    @SuppressWarnings("deprecation")
    @Override
    public void playMusic(World world, double x, double y, double z, @Nullable Entity entity, MusicInstrument instr, int bonusRadius, int bonusMusicAmount)
    {
        playMusic0(world, x, y, z, entity, instr, instr.getBaseRange() + bonusRadius, instr.getBaseOutput() + bonusMusicAmount);
    }

    @SuppressWarnings("deprecation")
    @Override
    public void playMusic(Entity entity, MusicInstrument instr, int bonusRadius, int bonusMusicAmount)
    {
        playMusic0(entity.world, entity.posX, entity.posY, entity.posZ, entity, instr, instr.getBaseRange() + bonusRadius, instr.getBaseOutput() + bonusMusicAmount);
    }

    @Deprecated
    @Override
    public void addMusic(MusicAcceptor tile, @Nullable Entity entity, MusicInstrument instr, int musicAmount)
    {
        TotemUtil.addMusic(tile, entity, instr, musicAmount);
    }
}

@@ -89,16 +89,48 @@
 ptional;
+%0Aimport java.util.stream.Stream;
 %0A%0Aimport
@@ -158,16 +158,61 @@
 lable;%0A%0A
+import org.apache.commons.lang3.tuple.Pair;%0A%0A
 import n
@@ -324,16 +324,16 @@
 ockPos;%0A
-
 import n
@@ -358,16 +358,65 @@
 .World;%0A
+import pokefenn.totemic.api.TotemicCapabilities;%0A
 import p
@@ -3113,32 +3113,159 @@
 alRadius)%0A    %7B%0A
+        //Once we remove support for directly implementing MusicAcceptor, we will be able to simplify this (filter, min, map).%0A
         return E
@@ -3410,47 +3410,778 @@
   .f
-ilter(te -%3E te instanceof MusicAcceptor
+latMap(tile -%3E %7B%0A                    //flatMap into a stream containing a pair of the tile entity and the corresponding MusicAcceptor,%0A                    //or into an empty stream if we don't have a MusicAcceptor. Basically filter and map in one step.%0A                    //We need the tile entity in the pair to compare distances later.%0A                    if(tile.hasCapability(TotemicCapabilities.MUSIC_ACCEPTOR, null))%0A                        return Stream.of(Pair.of(tile, tile.getCapability(TotemicCapabilities.MUSIC_ACCEPTOR, null)));%0A                    else if(tile instanceof MusicAcceptor)%0A                        return Stream.of(Pair.of(tile, (MusicAcceptor) tile));%0A                    else%0A                        return Stream.empty();%0A                %7D
 )%0A  
@@ -4224,16 +4224,30 @@
 ing(
-te -%3E te
+pair -%3E pair.getLeft()
 .get
@@ -4293,32 +4293,22 @@
 map(
-te -%3E (MusicAcceptor) te
+Pair::getRight
 );%0A 

Change getClosestAcceptor to take capability into account