package me.km.overrides;

import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import me.km.Server;
import me.km.utils.ReflectionUtils;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.play.server.SChangeGameStatePacket;
import net.minecraft.network.play.server.SPlaySoundEffectPacket;
import net.minecraft.network.play.server.SRespawnPacket;
import net.minecraft.network.play.server.SServerDifficultyPacket;
import net.minecraft.network.play.server.SSetExperiencePacket;
import net.minecraft.network.play.server.SWorldSpawnChangedPacket;
import net.minecraft.server.dedicated.DedicatedPlayerList;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.management.DemoPlayerInteractionManager;
import net.minecraft.server.management.PlayerInteractionManager;
import net.minecraft.server.management.PlayerList;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.registry.DynamicRegistries;
import net.minecraft.util.text.ChatType;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.biome.BiomeManager;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.storage.IWorldInfo;
import net.minecraft.world.storage.PlayerData;

public final class ModPlayerList extends DedicatedPlayerList {
    private final DedicatedServer server;
    private final List<ServerPlayerEntity> players;
    private final Map<UUID, ServerPlayerEntity> uuidToPlayerMap;

    @SuppressWarnings("unchecked")
    public ModPlayerList(DedicatedServer server, DynamicRegistries.Impl registries, PlayerData pd) {
        super(server, registries, pd);
        this.server = server;
        this.players = getPlayers();
        this.uuidToPlayerMap =
                ReflectionUtils.getFieldValue(Map.class, PlayerList.class, this, "field_177454_f");
    }

    @Override
    public ServerPlayerEntity createPlayerForUser(GameProfile profile) {
        UUID uuid = PlayerEntity.getUUID(profile);
        List<ServerPlayerEntity> list = Lists.newArrayList();

        for(int i = 0; i < this.players.size(); ++i) {
            ServerPlayerEntity serverplayerentity = this.players.get(i);
            if(serverplayerentity.getUniqueID().equals(uuid)) {
                list.add(serverplayerentity);
            }
        }

        ServerPlayerEntity p = this.uuidToPlayerMap.get(profile.getId());
        if(p != null && !list.contains(p)) {
            list.add(p);
        }

        for(ServerPlayerEntity serverplayerentity1 : list) {
            serverplayerentity1.connection.disconnect(
                    new TranslationTextComponent("multiplayer.disconnect.duplicate_login"));
        }

        ServerWorld w = this.server.func_241755_D_();
        PlayerInteractionManager manager;
        if(this.server.isDemo()) {
            manager = new DemoPlayerInteractionManager(w);
        } else {
            manager = new PlayerInteractionManager(w);
        }

        return new ModEntityPlayerMP(this.server, w, profile, manager);
    }

    @Override
    public ServerPlayerEntity func_232644_a_(ServerPlayerEntity pIn, boolean p_232644_2_) {
        Server.scriptEvents.onPlayerPreRespawn(pIn);
        this.removePlayer(pIn);
        pIn.getServerWorld().removePlayer(pIn, true); // Forge: keep data until copyFrom called
        BlockPos blockpos = pIn.func_241140_K_();
        float f = pIn.func_242109_L();
        boolean flag = pIn.func_241142_M_();
        ServerWorld serverworld = server.getWorld(pIn.func_241141_L_());
        Optional<Vector3d> optional;
        if(serverworld != null && blockpos != null) {
            optional = PlayerEntity.func_242374_a(serverworld, blockpos, f, flag, p_232644_2_);
        } else {
            optional = Optional.empty();
        }

        ServerWorld w =
                serverworld != null && optional.isPresent() ? serverworld : server.func_241755_D_();
        PlayerInteractionManager manager;
        if(this.server.isDemo()) {
            manager = new DemoPlayerInteractionManager(w);
        } else {
            manager = new PlayerInteractionManager(w);
        }

        ServerPlayerEntity p = new ModEntityPlayerMP(server, w, pIn.getGameProfile(), manager,
                (ModEntityPlayerMP) pIn);
        p.connection = pIn.connection;
        p.copyFrom(pIn, p_232644_2_);
        pIn.remove(false); // Forge: clone event had a chance to see old data, now discard it
        p.setEntityId(pIn.getEntityId());
        p.setPrimaryHand(pIn.getPrimaryHand());

        for(String s : pIn.getTags()) {
            p.addTag(s);
        }

        this.setPlayerGameTypeBasedOnOther(p, pIn, w);
        boolean flag2 = false;
        if(optional.isPresent()) {
            BlockState blockstate = w.getBlockState(blockpos);
            boolean flag1 = blockstate.isIn(Blocks.RESPAWN_ANCHOR);
            Vector3d vector3d = optional.get();
            float f1;
            if(!blockstate.isIn(BlockTags.BEDS) && !flag1) {
                f1 = f;
            } else {
                Vector3d vector3d1 =
                        Vector3d.copyCenteredHorizontally(blockpos).subtract(vector3d).normalize();
                f1 = (float) MathHelper.wrapDegrees(MathHelper.atan2(vector3d1.z, vector3d1.x)
                        * (double) (180F / (float) Math.PI) - 90.0D);
            }

            p.setLocationAndAngles(vector3d.x, vector3d.y, vector3d.z, f1, 0.0F);
            p.func_242111_a(w.getDimensionKey(), blockpos, f, flag, false);
            flag2 = !p_232644_2_ && flag1;
        } else if(blockpos != null) {
            p.connection.sendPacket(
                    new SChangeGameStatePacket(SChangeGameStatePacket.field_241764_a_, 0.0F));
        }

        while(!w.hasNoCollisions(p) && p.getPosY() < 256.0D) {
            p.setPosition(p.getPosX(), p.getPosY() + 1.0D, p.getPosZ());
        }

        IWorldInfo iworldinfo = p.world.getWorldInfo();
        p.connection.sendPacket(new SRespawnPacket(p.world.getDimensionType(),
                p.world.getDimensionKey(), BiomeManager.getHashedSeed(p.getServerWorld().getSeed()),
                p.interactionManager.getGameType(), p.interactionManager.func_241815_c_(),
                p.getServerWorld().isDebug(), p.getServerWorld().func_241109_A_(), p_232644_2_));
        p.connection.setPlayerLocation(p.getPosX(), p.getPosY(), p.getPosZ(), p.rotationYaw,
                p.rotationPitch);
        p.connection.sendPacket(new SWorldSpawnChangedPacket(w.getSpawnPoint(), w.func_242107_v()));
        p.connection.sendPacket(new SServerDifficultyPacket(iworldinfo.getDifficulty(),
                iworldinfo.isDifficultyLocked()));
        p.connection.sendPacket(
                new SSetExperiencePacket(p.experience, p.experienceTotal, p.experienceLevel));
        this.sendWorldInfo(p, w);
        this.updatePermissionLevel(p);
        w.addRespawnedPlayer(p);
        this.addPlayer(p);
        this.uuidToPlayerMap.put(p.getUniqueID(), p);
        p.addSelfToInternalCraftingInventory();
        p.setHealth(p.getHealth());
        net.minecraftforge.fml.hooks.BasicEventHooks.firePlayerRespawnEvent(p, p_232644_2_);
        if(flag2) {
            p.connection
                    .sendPacket(new SPlaySoundEffectPacket(SoundEvents.BLOCK_RESPAWN_ANCHOR_DEPLETE,
                            SoundCategory.BLOCKS, (double) blockpos.getX(),
                            (double) blockpos.getY(), (double) blockpos.getZ(), 1.0F, 1.0F));
        }

        return p;
    }

    private void setPlayerGameTypeBasedOnOther(ServerPlayerEntity target, ServerPlayerEntity source,
            ServerWorld w) {
        ReflectionUtils.setPlayerGameTypeBasedOnOther(this, target, source, w);
    }

    @Override
    public void func_232641_a_(ITextComponent message, ChatType chat, UUID uuid) {
        if(message instanceof TranslationTextComponent) {
            TranslationTextComponent translation = (TranslationTextComponent) message;
            switch (translation.getKey()) {
                case "multiplayer.player.left":
                case "multiplayer.player.joined":
                case "multiplayer.player.joined.renamed":
                    return;
            }
        }
        super.func_232641_a_(message, chat, uuid);
    }

}
