// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.universe.world;

import com.hypixel.hytale.protocol.BlockPosition;
import com.hypixel.hytale.protocol.packets.world.UpdateBlockDamage;
import com.hypixel.hytale.protocol.Position;
import com.hypixel.hytale.protocol.packets.world.SpawnBlockParticleSystem;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.protocol.BlockParticleEvent;
import com.hypixel.hytale.server.core.modules.entity.player.ChunkTracker;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import com.hypixel.hytale.protocol.Packet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.math.util.ChunkUtil;
import java.util.Objects;
import com.hypixel.hytale.server.core.universe.world.meta.state.SendableBlockState;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.function.Predicate;
import com.hypixel.hytale.server.core.universe.world.meta.BlockState;
import javax.annotation.Nonnull;

public class WorldNotificationHandler
{
    @Nonnull
    private final World world;
    
    public WorldNotificationHandler(@Nonnull final World world) {
        this.world = world;
    }
    
    public void updateState(final int x, final int y, final int z, final BlockState state, final BlockState oldState) {
        this.updateState(x, y, z, state, oldState, null);
    }
    
    public void updateState(final int x, final int y, final int z, final BlockState state, final BlockState oldState, @Nullable final Predicate<PlayerRef> skip) {
        if (y < 0 || y >= 320) {
            throw new IllegalArgumentException("Y value is outside the world! " + x + ", " + y + ", " + z);
        }
        Consumer<List<Packet>> removeOldState = null;
        Predicate<PlayerRef> canPlayerSeeOld = null;
        Label_0086: {
            if (oldState instanceof final SendableBlockState sendableBlockState) {
                if (state != oldState) {
                    final SendableBlockState obj = sendableBlockState;
                    Objects.requireNonNull(obj);
                    removeOldState = (Consumer<List<Packet>>)obj::unloadFrom;
                    final SendableBlockState obj2 = sendableBlockState;
                    Objects.requireNonNull(obj2);
                    canPlayerSeeOld = obj2::canPlayerSee;
                    break Label_0086;
                }
            }
            removeOldState = null;
            canPlayerSeeOld = null;
        }
        Consumer<List<Packet>> updateBlockState;
        Predicate<PlayerRef> canPlayerSee;
        if (state instanceof SendableBlockState) {
            final SendableBlockState obj3;
            final SendableBlockState sendableBlockState2 = obj3 = (SendableBlockState)state;
            Objects.requireNonNull(obj3);
            updateBlockState = (Consumer<List<Packet>>)obj3::sendTo;
            final SendableBlockState obj4 = sendableBlockState2;
            Objects.requireNonNull(obj4);
            canPlayerSee = obj4::canPlayerSee;
        }
        else {
            updateBlockState = null;
            canPlayerSee = null;
        }
        if (removeOldState != null || updateBlockState != null) {
            final long indexChunk = ChunkUtil.indexChunkFromBlock(x, z);
            final List<Packet> packets = new ObjectArrayList<Packet>();
            for (final PlayerRef playerRef : this.world.getPlayerRefs()) {
                final ChunkTracker chunkTracker = playerRef.getChunkTracker();
                if (!chunkTracker.isLoaded(indexChunk)) {
                    continue;
                }
                if (skip != null && skip.test(playerRef)) {
                    continue;
                }
                if (removeOldState != null && canPlayerSeeOld.test(playerRef)) {
                    removeOldState.accept(packets);
                }
                if (updateBlockState != null && canPlayerSee.test(playerRef)) {
                    updateBlockState.accept(packets);
                }
                for (final Packet packet : packets) {
                    playerRef.getPacketHandler().write(packet);
                }
                packets.clear();
            }
        }
    }
    
    public void updateChunk(final long indexChunk) {
        for (final PlayerRef playerRef : this.world.getPlayerRefs()) {
            playerRef.getChunkTracker().removeForReload(indexChunk);
        }
    }
    
    public void sendBlockParticle(final double x, final double y, final double z, final int id, @Nonnull final BlockParticleEvent particleType) {
        this.sendPacketIfChunkLoaded(this.getBlockParticlePacket(x, y, z, id, particleType), MathUtil.floor(x), MathUtil.floor(z));
    }
    
    public void sendBlockParticle(@Nonnull final PlayerRef playerRef, final double x, final double y, final double z, final int id, @Nonnull final BlockParticleEvent particleType) {
        this.sendPacketIfChunkLoaded(playerRef, this.getBlockParticlePacket(x, y, z, id, particleType), MathUtil.floor(x), MathUtil.floor(z));
    }
    
    public void updateBlockDamage(final int x, final int y, final int z, final float health, final float healthDelta) {
        this.sendPacketIfChunkLoaded(this.getBlockDamagePacket(x, y, z, health, healthDelta), x, z);
    }
    
    public void updateBlockDamage(final int x, final int y, final int z, final float health, final float healthDelta, @Nullable final Predicate<PlayerRef> filter) {
        this.sendPacketIfChunkLoaded(this.getBlockDamagePacket(x, y, z, health, healthDelta), x, z, filter);
    }
    
    public void sendPacketIfChunkLoaded(@Nonnull final Packet packet, final int x, final int z) {
        final long indexChunk = ChunkUtil.indexChunkFromBlock(x, z);
        this.sendPacketIfChunkLoaded(packet, indexChunk);
    }
    
    public void sendPacketIfChunkLoaded(@Nonnull final Packet packet, final long indexChunk) {
        for (final PlayerRef playerRef : this.world.getPlayerRefs()) {
            if (playerRef.getChunkTracker().isLoaded(indexChunk)) {
                playerRef.getPacketHandler().write(packet);
            }
        }
    }
    
    public void sendPacketIfChunkLoaded(@Nonnull final Packet packet, final int x, final int z, @Nullable final Predicate<PlayerRef> filter) {
        final long indexChunk = ChunkUtil.indexChunkFromBlock(x, z);
        this.sendPacketIfChunkLoaded(packet, indexChunk, filter);
    }
    
    public void sendPacketIfChunkLoaded(@Nonnull final Packet packet, final long indexChunk, @Nullable final Predicate<PlayerRef> filter) {
        for (final PlayerRef playerRef : this.world.getPlayerRefs()) {
            if (filter != null && !filter.test(playerRef)) {
                continue;
            }
            if (!playerRef.getChunkTracker().isLoaded(indexChunk)) {
                continue;
            }
            playerRef.getPacketHandler().write(packet);
        }
    }
    
    private void sendPacketIfChunkLoaded(@Nonnull final PlayerRef player, @Nonnull final Packet packet, final int x, final int z) {
        final long indexChunk = ChunkUtil.indexChunkFromBlock(x, z);
        this.sendPacketIfChunkLoaded(player, packet, indexChunk);
    }
    
    private void sendPacketIfChunkLoaded(@Nonnull final PlayerRef playerRef, @Nonnull final Packet packet, final long indexChunk) {
        if (playerRef.getChunkTracker().isLoaded(indexChunk)) {
            playerRef.getPacketHandler().write(packet);
        }
    }
    
    @Nonnull
    public SpawnBlockParticleSystem getBlockParticlePacket(final double x, final double y, final double z, final int id, @Nonnull final BlockParticleEvent particleType) {
        if (y < 0.0 || y >= 320.0) {
            throw new IllegalArgumentException("Y value is outside the world! " + x + ", " + y + ", " + z);
        }
        return new SpawnBlockParticleSystem(id, particleType, new Position(x, y, z));
    }
    
    @Nonnull
    public UpdateBlockDamage getBlockDamagePacket(final int x, final int y, final int z, final float health, final float healthDelta) {
        if (y < 0 || y >= 320) {
            throw new IllegalArgumentException("Y value is outside the world! " + x + ", " + y + ", " + z);
        }
        return new UpdateBlockDamage(new BlockPosition(x, y, z), health, healthDelta);
    }
}
