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.addMusic(instr, amount))
                .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> res = getClosestAcceptor(world, x, y, z, range, range)
                .filter(acc -> acc instanceof TileTotemBase)
                .map(acc -> (TileTotemBase) acc)
                .filter(TileTotemBase::canSelect);
        res.ifPresent(totem -> totem.addSelector(entity, instr));
        return res.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);
    }
    @Override
    public void addMusic(MusicAcceptor tile, @Nullable Entity entity, MusicInstrument instr, int musicAmount)
    {
        TotemUtil.addMusic(tile, entity, instr, musicAmount);
    }
}

@@ -198,32 +198,77 @@
 ity.TileEntity;%0A
+import net.minecraft.util.EnumParticleTypes;%0A
 import net.minec
@@ -326,16 +326,56 @@
 .World;%0A
+import net.minecraft.world.WorldServer;%0A
 import p
@@ -1571,22 +1571,35 @@
         
-return
+MusicAcceptor acc =
  getClos
@@ -1625,37 +1625,76 @@
 x, y, z, range, 
-range
+amount).orElse(null);%0A        if(acc != null
 )%0A              
@@ -1678,32 +1678,35 @@
 != null)%0A       
+ %7B%0A
          .map(ac
@@ -1698,27 +1698,32 @@
         
-.map(acc -%3E
+   boolean ret =
  acc.add
@@ -1746,39 +1746,461 @@
 unt)
-)%0A                .orElse(false
+;%0A            spawnMusicParticles(world, acc, ret ? EnumParticleTypes.NOTE : EnumParticleTypes.CLOUD);%0A            return ret;%0A        %7D%0A        else%0A            return false;%0A    %7D%0A%0A    private void spawnMusicParticles(World world, MusicAcceptor acc, EnumParticleTypes type)%0A    %7B%0A        BlockPos pos = ((TileEntity) acc).getPos();%0A        ((WorldServer) world).spawnParticle(type, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 6, 0.5, 0.5, 0.5, 0.0
 );%0A 
@@ -3064,19 +3064,21 @@
 emBase%3E 
-res
+totem
  = getCl
@@ -3290,19 +3290,21 @@
         
-res
+totem
 .ifPrese
@@ -3311,21 +3311,13 @@
 nt(t
-otem
  -%3E t
-otem
 .add
@@ -3357,19 +3357,21 @@
  return 
-res
+totem
 .isPrese

Fix particles on playMusic