package net.minecraft.server.level;

import net.minecraft.core.BlockPosition;
import net.minecraft.network.chat.ChatMessage;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutOpenWindow;
import net.minecraft.network.protocol.game.PacketPlayOutOpenSignEditor;
import net.minecraft.network.syncher.DataWatcherObject;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.network.PlayerConnection;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityLiving;
import net.minecraft.world.entity.EntityInsentient;
import net.minecraft.world.entity.player.EntityHuman;
import net.minecraft.world.entity.player.PlayerInventory;
import net.minecraft.world.inventory.ContainerAccess;
import net.minecraft.world.inventory.ContainerAnvil;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.World;

import com.bergerkiller.bukkit.common.bases.IntVector3;
import com.bergerkiller.bukkit.common.wrappers.DataWatcher;
import com.bergerkiller.bukkit.common.wrappers.DataWatcher.Key;
import com.bergerkiller.bukkit.common.wrappers.ChatText;

import com.bergerkiller.generated.com.mojang.authlib.GameProfileHandle;

import com.bergerkiller.generated.net.minecraft.server.level.EntityPlayerHandle;
import com.bergerkiller.generated.net.minecraft.server.network.PlayerConnectionHandle;
import com.bergerkiller.generated.net.minecraft.world.entity.EntityHandle;
import com.bergerkiller.generated.net.minecraft.world.entity.decoration.EntityItemFrameHandle;
import com.bergerkiller.generated.net.minecraft.world.entity.item.EntityItemHandle;
import com.bergerkiller.generated.net.minecraft.world.inventory.ContainerHandle;
import com.bergerkiller.generated.net.minecraft.world.item.ItemStackHandle;
import com.bergerkiller.generated.net.minecraft.world.level.WorldHandle;

class EntityPlayer extends net.minecraft.world.entity.player.EntityHuman {

#if version >= 1.17
    public (PlayerConnectionHandle) PlayerConnection playerConnection:connection;
#else
    public (PlayerConnectionHandle) PlayerConnection playerConnection;
#endif

#if version >= 1.16
    // World dimension on 1.16 and later
    public org.bukkit.World getSpawnWorld() {
  #if version >= 1.18
        ResourceKey spawn = instance.getRespawnDimension();
        World world = MinecraftServer.getServer().getLevel(spawn);
  #else
        ResourceKey spawn = instance.getSpawnDimension();
        World world = MinecraftServer.getServer().getWorldServer(spawn);
  #endif
        return (world == null) ? null : world.getWorld();
    }
    public void setSpawnWorld(org.bukkit.World world) {
  #if version >= 1.17
        #require net.minecraft.server.level.EntityPlayer private net.minecraft.resources.ResourceKey<net.minecraft.world.level.World> spawnDimension:respawnDimension;
  #else
        #require net.minecraft.server.level.EntityPlayer private net.minecraft.resources.ResourceKey<net.minecraft.world.level.World> spawnDimension;
  #endif
  #if version >= 1.18
        ResourceKey key = (world == null) ? World.OVERWORLD : ((WorldServer) ((org.bukkit.craftbukkit.CraftWorld) world).getHandle()).dimension();
  #else
        ResourceKey key = (world == null) ? World.OVERWORLD : ((WorldServer) ((org.bukkit.craftbukkit.CraftWorld) world).getHandle()).getDimensionKey();
  #endif
        instance#spawnDimension = key;
    }
#else
    // World name on 1.15.2 and earlier
    public org.bukkit.World getSpawnWorld() {
        return org.bukkit.Bukkit.getWorld(instance.spawnWorld);
    }
    public void setSpawnWorld(org.bukkit.World world) {
        instance.spawnWorld = (world == null) ? "" : world.getName();
    }
#endif

#if version >= 1.17
    #require net.minecraft.server.level.EntityPlayer private net.minecraft.core.BlockPosition spawnCoord:respawnPosition;
    #require net.minecraft.server.level.EntityPlayer private boolean spawnForced:respawnForced;
#elseif version >= 1.16
    #require net.minecraft.server.level.EntityPlayer private net.minecraft.core.BlockPosition spawnCoord:spawn;
    #require net.minecraft.server.level.EntityPlayer private boolean spawnForced;
#elseif version >= 1.15
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:g;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:bR;
#elseif version >= 1.14.4
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:g;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:bU;
#elseif version >= 1.14
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:f;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:g;
#elseif version >= 1.13
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:e;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:f;
#elseif version >= 1.12
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:d;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:e;
#elseif version >= 1.11
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:e;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:f;
#elseif version >= 1.10.2
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:e;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:f;
#elseif version >= 1.9.4
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:e;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:f; 
#elseif version >= 1.9
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:e;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:f;
#elseif version >= 1.8.3
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:c;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:d;
#else
    #require net.minecraft.world.entity.player.EntityHuman private net.minecraft.core.BlockPosition spawnCoord:c;
    #require net.minecraft.world.entity.player.EntityHuman private boolean spawnForced:d;
#endif

    public (IntVector3) BlockPosition getSpawnCoord() {
        return instance#spawnCoord;
    }

    public void setSpawnCoord((IntVector3) BlockPosition coord) {
        instance#spawnCoord = coord;
    }

    public boolean isSpawnForced() {
        return instance#spawnForced;
    }

    public void setSpawnForced(boolean forced) {
        instance#spawnForced = forced;
    }

#if version >= 1.17
    #require net.minecraft.server.level.EntityPlayer private float spawnAngle:respawnAngle;
#elseif version >= 1.16.2
    #require net.minecraft.server.level.EntityPlayer private float spawnAngle;
#endif

#if version >= 1.16.2
    public float getSpawnAngle() {
        return instance#spawnAngle;
    }

    public void setSpawnAngle(float angle) {
        instance#spawnAngle = angle;
    }
#else
    public float getSpawnAngle() {
        return 0.0f;
    }

    public void setSpawnAngle(float angle) {
    }
#endif

    public int getPing() {
#if version >= 1.20.2
        return instance.connection.latency();
#elseif version >= 1.17
        return instance.latency;
#else
        return instance.ping;
#endif
    }

#if version >= 1.17
    public readonly boolean viewingCredits:wonGame;
#else
    public readonly boolean viewingCredits;
#endif

    // Seen credits getter/setter
#if version >= 1.12
    // Since Minecraft 1.12 this is just a boolean property of the player
  #if version >= 1.17
    #require net.minecraft.server.level.EntityPlayer private boolean seenCredits;
  #elseif version >= 1.16.2
    #require net.minecraft.server.level.EntityPlayer private boolean seenCredits:cd;
  #elseif version >= 1.16.1
    #require net.minecraft.server.level.EntityPlayer private boolean seenCredits:ck;
  #elseif version >= 1.15
    #require net.minecraft.server.level.EntityPlayer private boolean seenCredits:cm;
  #elseif version >= 1.14
    #require net.minecraft.server.level.EntityPlayer private boolean seenCredits:cp;
  #elseif version >= 1.13.1
    #require net.minecraft.server.level.EntityPlayer private boolean seenCredits:cx;
  #elseif version >= 1.13
    #require net.minecraft.server.level.EntityPlayer private boolean seenCredits:cy;
  #else
    #require net.minecraft.server.level.EntityPlayer private boolean seenCredits:cq;
  #endif

    public boolean hasSeenCredits() {
        return instance#seenCredits;
    }

    public void setHasSeenCredits(boolean hasSeen) {
        instance#seenCredits = hasSeen;
    }
#else
    // Older versions of Minecraft used the achievement list for this
    public boolean hasSeenCredits() {
  #if version >= 1.9
        return instance.a(AchievementList.D);
  #else
        return instance.getStatisticManager().hasAchievement(AchievementList.D);
  #endif
    }

    public void setHasSeenCredits(boolean hasSeen) {
        instance.getStatisticManager().setStatistic(instance, (Statistic) AchievementList.D, hasSeen ? 1 : 0);
    }
#endif

#if forge_nms_obfuscated
    public void sendMessage:a((ChatText) IChatBaseComponent ichatbasecomponent);
#elseif version >= 1.19.1
    public void sendMessage:sendSystemMessage((ChatText) IChatBaseComponent ichatbasecomponent);
#elseif version >= 1.19
    public void sendMessage((ChatText) IChatBaseComponent ichatbasecomponent) {
        net.minecraft.network.chat.PlayerChatMessage playerchatmessage = net.minecraft.network.chat.PlayerChatMessage.unsigned(ichatbasecomponent);
        IChatBaseComponent systemDisplayName = IChatBaseComponent.literal("");
        net.minecraft.network.chat.ChatSender chatsender = net.minecraft.network.chat.ChatSender.system(systemDisplayName);
        net.minecraft.resources.ResourceKey chatMessageType = net.minecraft.network.chat.ChatMessageType.SYSTEM;
        instance.sendChatMessage(playerchatmessage, chatsender, chatMessageType);
    }
#elseif version >= 1.17
    public void sendMessage((ChatText) IChatBaseComponent ichatbasecomponent) {
        instance.sendMessage(ichatbasecomponent, net.minecraft.SystemUtils.NIL_UUID);
    }
#elseif version >= 1.16
    public void sendMessage((ChatText) IChatBaseComponent ichatbasecomponent) {
        instance.sendMessage(ichatbasecomponent, SystemUtils.b);
    }
#else
    public void sendMessage((ChatText) IChatBaseComponent ichatbasecomponent);
#endif

#if exists net.minecraft.server.level.EntityPlayer public final java.util.List<Integer> removeQueue;
    public optional Collection<Integer> getRemoveQueue() {
        return instance.removeQueue;
    }
#elseif exists net.minecraft.server.level.EntityPlayer public final java.util.Deque<Integer> removeQueue;
    public optional Collection<Integer> getRemoveQueue() {
        return instance.removeQueue;
    }
#else
    public optional Collection<Integer> getRemoveQueue:###();
#endif

    public int getCurrentWindowId() {
#if version >= 1.17
        return ((EntityHuman) instance).containerMenu.containerId;
#else
        return instance.activeContainer.windowId;
#endif
    }

    public org.bukkit.inventory.InventoryView openAnvilWindow(ChatText titleText) {
        IChatBaseComponent title;
        if (titleText != null) {
            title = (IChatBaseComponent) titleText.getRawHandle();
        } else {
            String titleStr = (String) com.bergerkiller.generated.net.minecraft.world.level.block.BlockHandle.T.getTitle.invoke(Blocks.ANVIL);
#if version >= 1.19
            title = (IChatBaseComponent) IChatBaseComponent.translatable(titleStr);
#else
            title = (IChatBaseComponent) (new ChatMessage(titleStr, new Object[0]));
#endif
        }

#if version >= 1.17
        PlayerInventory playerInventory = instance.getInventory();
#else
        PlayerInventory playerInventory = instance.inventory;
#endif
#if version >= 1.20
        World world = instance.level();
        BlockPosition position = instance.blockPosition();
#elseif version >= 1.18
        World world = instance.getLevel();
        BlockPosition position = instance.blockPosition();
#else
        World world = instance.getWorld();
        BlockPosition position = instance.getChunkCoordinates();
#endif

#if exists net.minecraft.server.level.EntityPlayer public int nextContainerCounter();
        int windowId = instance.nextContainerCounter();
#elseif exists net.minecraft.server.level.EntityPlayer public int nextContainerCounterInt();
        int windowId = instance.nextContainerCounterInt();
#elseif exists net.minecraft.server.level.EntityPlayer public void nextContainerCounter();
        // void on forge, requires field reflection to get the counter value
        instance.nextContainerCounter();
        #require net.minecraft.server.level.EntityPlayer private int containerCounter;
        int windowId = instance#containerCounter;
#else
        // use #require so it logs the methods that do exist
        #require net.minecraft.server.level.EntityPlayer public int nextContainerCounter();
        int windowId;
        windowId = instance#nextContainerCounter();
#endif

#if version >= 1.18
        ContainerAccess access = ContainerAccess.create(world, position);
        ContainerAnvil container = new ContainerAnvil(windowId, playerInventory, access);
        container.setTitle(title);
#elseif version >= 1.14
        ContainerAccess access = ContainerAccess.at(world, position);
        ContainerAnvil container = new ContainerAnvil(windowId, playerInventory, access);
        container.setTitle(title);
#else
        ContainerAnvil container = new ContainerAnvil(playerInventory, world, position, (EntityHuman) instance);
        container.windowId = windowId;
#endif
        container.checkReachable = false;

        // Hook required so we can track when the text changes
#if version < 1.9
        com.bergerkiller.bukkit.common.internal.hooks.LegacyContainerAnvilHook hook;
        hook = new com.bergerkiller.bukkit.common.internal.hooks.LegacyContainerAnvilHook();
        container = (ContainerAnvil) hook.hook(container);
#endif

#if version >= 1.17
        ((EntityHuman) instance).containerMenu = container;
#else
        instance.activeContainer = container;
#endif

#if version >= 1.18
        instance.connection.send((Packet) new PacketPlayOutOpenWindow(windowId, net.minecraft.world.inventory.Containers.ANVIL, title));
#elseif version >= 1.17
        instance.connection.sendPacket((Packet) new PacketPlayOutOpenWindow(windowId, net.minecraft.world.inventory.Containers.ANVIL, title));
#elseif version >= 1.14
        instance.playerConnection.sendPacket((Packet) new PacketPlayOutOpenWindow(windowId, net.minecraft.world.inventory.Containers.ANVIL, title));
#else
        instance.playerConnection.sendPacket((Packet) new PacketPlayOutOpenWindow(windowId, "minecraft:anvil", title));
#endif

#if version >= 1.17
        instance.initMenu((net.minecraft.world.inventory.Container) container);
#else
        container.addSlotListener((net.minecraft.world.inventory.ICrafting) instance);
#endif

        return container.getBukkitView();
    }

    <code>
    public void closeSignEditWindow() {
        // Tell client to close the dialog by editing a sign far away in a chunk that 100% guaranteed is not loaded
        openSignEditWindow(IntVector3.of(Integer.MAX_VALUE, 0, Integer.MAX_VALUE));
    }

    public void openSignEditWindow(IntVector3 signPosition) {
        openSignEditWindow(signPosition, true);
    }
    </code>

    public void openSignEditWindow((IntVector3) BlockPosition signPosition, boolean isFrontText) {
#if version >= 1.20
        instance.connection.send((Packet) new PacketPlayOutOpenSignEditor(signPosition, isFrontText));
#else
        if (!isFrontText) {
            throw new UnsupportedOperationException("Back text not supported on this version of Minecraft");
        }

  #if version >= 1.18
        instance.connection.send((Packet) new PacketPlayOutOpenSignEditor(signPosition));
  #elseif version >= 1.17
        instance.connection.sendPacket((Packet) new PacketPlayOutOpenSignEditor(signPosition));
  #else
        instance.playerConnection.sendPacket((Packet) new PacketPlayOutOpenSignEditor(signPosition));
  #endif
#endif
    }

    <code>
    public static EntityPlayerHandle fromBukkit(org.bukkit.entity.Player player) {
        return createHandle(com.bergerkiller.bukkit.common.conversion.type.HandleConversion.toEntityHandle(player));
    }
    </code>
}
