package net.minecraft.world.entity.raid;

import java.util.AbstractList;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.CompoundTag;
import javax.annotation.Nullable;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.stats.Stats;
import net.minecraft.world.entity.player.Player;
import net.minecraft.network.protocol.Packet;
import net.minecraft.world.entity.Entity;
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.entity.ai.village.poi.PoiType;
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
import java.util.List;
import net.minecraft.server.level.ServerPlayer;
import java.util.Iterator;
import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.world.level.GameRules;
import com.google.common.collect.Maps;
import net.minecraft.server.level.ServerLevel;
import java.util.Map;
import net.minecraft.world.level.saveddata.SavedData;

public class Raids extends SavedData {
    private final Map<Integer, Raid> raidMap;
    private final ServerLevel level;
    private int nextAvailableID;
    private int tick;
    
    public Raids(final ServerLevel zd) {
        super(getFileId(zd.dimensionType()));
        this.raidMap = Maps.newHashMap();
        this.level = zd;
        this.nextAvailableID = 1;
        this.setDirty();
    }
    
    public Raid get(final int integer) {
        return this.raidMap.get(integer);
    }
    
    public void tick() {
        ++this.tick;
        final Iterator<Raid> iterator2 = this.raidMap.values().iterator();
        while (iterator2.hasNext()) {
            final Raid bfh3 = iterator2.next();
            if (this.level.getGameRules().getBoolean(GameRules.RULE_DISABLE_RAIDS)) {
                bfh3.stop();
            }
            if (bfh3.isStopped()) {
                iterator2.remove();
                this.setDirty();
            }
            else {
                bfh3.tick();
            }
        }
        if (this.tick % 200 == 0) {
            this.setDirty();
        }
        DebugPackets.sendRaids(this.level, this.raidMap.values());
    }
    
    public static boolean canJoinRaid(final Raider bfi, final Raid bfh) {
        return bfi != null && bfh != null && bfh.getLevel() != null && bfi.isAlive() && bfi.canJoinRaid() && bfi.getNoActionTime() <= 2400 && bfi.level.dimensionType() == bfh.getLevel().dimensionType();
    }
    
    @Nullable
    public Raid createOrExtendRaid(final ServerPlayer ze) {
        if (ze.isSpectator()) {
            return null;
        }
        if (this.level.getGameRules().getBoolean(GameRules.RULE_DISABLE_RAIDS)) {
            return null;
        }
        final DimensionType cif3 = ze.level.dimensionType();
        if (!cif3.hasRaids()) {
            return null;
        }
        final BlockPos fu4 = ze.blockPosition();
        final List<PoiRecord> list6 = this.level.getPoiManager().getInRange(PoiType.ALL, fu4, 64, PoiManager.Occupancy.IS_OCCUPIED).collect(Collectors.toList());
        int integer7 = 0;
        Vec3 dem8 = Vec3.ZERO;
        for (final PoiRecord aya10 : list6) {
            final BlockPos fu5 = aya10.getPos();
            dem8 = dem8.add(fu5.getX(), fu5.getY(), fu5.getZ());
            ++integer7;
        }
        BlockPos fu6;
        if (integer7 > 0) {
            dem8 = dem8.scale(1.0 / integer7);
            fu6 = new BlockPos(dem8);
        }
        else {
            fu6 = fu4;
        }
        final Raid bfh9 = this.getOrCreateRaid(ze.getLevel(), fu6);
        boolean boolean10 = false;
        if (!bfh9.isStarted()) {
            if (!this.raidMap.containsKey(bfh9.getId())) {
                this.raidMap.put(bfh9.getId(), bfh9);
            }
            boolean10 = true;
        }
        else if (bfh9.getBadOmenLevel() < bfh9.getMaxBadOmenLevel()) {
            boolean10 = true;
        }
        else {
            ze.removeEffect(MobEffects.BAD_OMEN);
            ze.connection.send(new ClientboundEntityEventPacket(ze, (byte)43));
        }
        if (boolean10) {
            bfh9.absorbBadOmen(ze);
            ze.connection.send(new ClientboundEntityEventPacket(ze, (byte)43));
            if (!bfh9.hasFirstWaveSpawned()) {
                ze.awardStat(Stats.RAID_TRIGGER);
                CriteriaTriggers.BAD_OMEN.trigger(ze);
            }
        }
        this.setDirty();
        return bfh9;
    }
    
    private Raid getOrCreateRaid(final ServerLevel zd, final BlockPos fu) {
        final Raid bfh4 = zd.getRaidAt(fu);
        return (bfh4 != null) ? bfh4 : new Raid(this.getUniqueId(), zd, fu);
    }
    
    @Override
    public void load(final CompoundTag le) {
        this.nextAvailableID = le.getInt("NextAvailableID");
        this.tick = le.getInt("Tick");
        final ListTag lk3 = le.getList("Raids", 10);
        for (int integer4 = 0; integer4 < lk3.size(); ++integer4) {
            final CompoundTag le2 = lk3.getCompound(integer4);
            final Raid bfh6 = new Raid(this.level, le2);
            this.raidMap.put(bfh6.getId(), bfh6);
        }
    }
    
    @Override
    public CompoundTag save(final CompoundTag le) {
        le.putInt("NextAvailableID", this.nextAvailableID);
        le.putInt("Tick", this.tick);
        final ListTag lk3 = new ListTag();
        for (final Raid bfh5 : this.raidMap.values()) {
            final CompoundTag le2 = new CompoundTag();
            bfh5.save(le2);
            ((AbstractList<CompoundTag>)lk3).add(le2);
        }
        le.put("Raids", lk3);
        return le;
    }
    
    public static String getFileId(final DimensionType cif) {
        return "raids" + cif.getFileSuffix();
    }
    
    private int getUniqueId() {
        return ++this.nextAvailableID;
    }
    
    @Nullable
    public Raid getNearbyRaid(final BlockPos fu, final int integer) {
        Raid bfh4 = null;
        double double5 = integer;
        for (final Raid bfh5 : this.raidMap.values()) {
            final double double6 = bfh5.getCenter().distSqr(fu);
            if (!bfh5.isActive()) {
                continue;
            }
            if (double6 >= double5) {
                continue;
            }
            bfh4 = bfh5;
            double5 = double6;
        }
        return bfh4;
    }
}
