package net.minecraft.world.level.block.entity;

import net.minecraft.core.BlockPosition;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.world.level.World;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.level.storage.ValueInput;

import com.bergerkiller.bukkit.common.bases.IntVector3;
import com.bergerkiller.bukkit.common.nbt.CommonTagCompound;
import com.bergerkiller.bukkit.common.protocol.CommonPacket;
import com.bergerkiller.bukkit.common.wrappers.BlockData;
import com.bergerkiller.bukkit.common.wrappers.ChatText;

import com.bergerkiller.generated.net.minecraft.core.BlockPositionHandle;
import com.bergerkiller.generated.net.minecraft.world.item.ItemStackHandle;
import com.bergerkiller.generated.net.minecraft.world.level.WorldHandle;
import com.bergerkiller.generated.net.minecraft.world.level.block.entity.TileEntitySignHandle;

class TileEntity {
#if version >= 1.17
    protected (org.bukkit.World) World world_field:level;
#else
    protected (org.bukkit.World) World world_field:world;
#endif

#if version >= 1.17
    protected (IntVector3) BlockPosition position_field:worldPosition;
#else
    protected (IntVector3) BlockPosition position_field:position;
#endif

#if version >= 1.18
    public (WorldHandle) World getWorld:getLevel();
    public (BlockPositionHandle) BlockPosition getPosition:getBlockPos();
#else
    public (WorldHandle) World getWorld();
    public (BlockPositionHandle) BlockPosition getPosition();
#endif

    public (BlockData) IBlockData getBlockDataIfCached() {
#if version >= 1.17
        #require net.minecraft.world.level.block.entity.TileEntity private IBlockData cachedBlockData:blockState;
        return instance#cachedBlockData;
#elseif version >= 1.14
        #require net.minecraft.world.level.block.entity.TileEntity private IBlockData cachedBlockData:c;
        return instance#cachedBlockData;
#elseif version >= 1.13
        #require net.minecraft.world.level.block.entity.TileEntity private IBlockData cachedBlockData:f;
        return instance#cachedBlockData;
#else
  #if version >= 1.11
        #require net.minecraft.world.level.block.entity.TileEntity private int cachedBlockDataValue:g;
        #require net.minecraft.world.level.block.entity.TileEntity protected Block cachedBlockDataBlock:e;
  #else
        #require net.minecraft.world.level.block.entity.TileEntity private int cachedBlockDataValue:h;
        #require net.minecraft.world.level.block.entity.TileEntity protected Block cachedBlockDataBlock:e;
  #endif

        Block block = instance#cachedBlockDataBlock;
        if (block == null) {
            return null;
        }
        int value = instance#cachedBlockDataValue;
        if (value == -1) {
            return null;
        }
        return block.fromLegacyData(value);
#endif
    }

#if version >= 1.21.6
    public void load((BlockData) IBlockData blockData, (CommonTagCompound) NBTTagCompound nbttagcompound) {
        com.bergerkiller.bukkit.common.internal.logic.ScopedProblemReporter reporter = #createScopedProblemReporter();
        try {
            ValueInput input = #createTagValueInput(reporter, instance.getLevel().registryAccess(), nbttagcompound);
            instance.loadWithComponents(input);
        } finally {
            reporter.close();
        }
    }
#elseif version >= 1.20.5
    public void load((BlockData) IBlockData blockData, (CommonTagCompound) NBTTagCompound nbttagcompound) {
        instance.loadWithComponents(nbttagcompound, instance.getLevel().registryAccess());
    }
#elseif version >= 1.17
    public void load((BlockData) IBlockData blockData, (CommonTagCompound) NBTTagCompound nbttagcompound) {
        instance.load(nbttagcompound);
    }
#elseif version >= 1.16
    public void load((BlockData) IBlockData blockData, (CommonTagCompound) NBTTagCompound nbttagcompound);
#elseif version >= 1.12.1
    public void load((BlockData) IBlockData blockData, (CommonTagCompound) NBTTagCompound nbttagcompound) {
        instance.load(nbttagcompound);
    }
#else
    public void load((BlockData) IBlockData blockData, (CommonTagCompound) NBTTagCompound nbttagcompound) {
        instance.a(nbttagcompound);
    }
#endif

#if version >= 1.21.6
    public (CommonTagCompound) NBTTagCompound save() {
        // Note: if ever removed, the same ProblemReporter crap as load()
        return instance.saveWithFullMetadata(instance.getLevel().registryAccess());
    }
#elseif version >= 1.20.5
    public (CommonTagCompound) NBTTagCompound save() {
        return instance.saveWithFullMetadata(instance.getLevel().registryAccess());
    }
#elseif version >= 1.18
    public final (CommonTagCompound) NBTTagCompound save:saveWithFullMetadata();
#else
    public (CommonTagCompound) NBTTagCompound save() {
        NBTTagCompound nbt = new NBTTagCompound();
  #if version >= 1.9.4
        return instance.save(nbt);
  #elseif version >= 1.9
        instance.save(nbt);
        return nbt;
  #else
        instance.b(nbt);
        return nbt;
  #endif
    }
#endif

#if version >= 1.18
    public (BlockData) IBlockData getBlockData:getBlockState();
    public (Object) IBlockData getRawBlockData:getBlockState();

    public (org.bukkit.Material) Block getType() {
        return instance.getBlockState().getBlock();
    }

    public optional int getLegacyData:###();

    public (CommonPacket) Packet<net.minecraft.network.protocol.game.PacketListenerPlayOut> getUpdatePacket();
#elseif version >= 1.13
    public (BlockData) IBlockData getBlockData:getBlock();
    public (Object) IBlockData getRawBlockData:getBlock();

    public (org.bukkit.Material) Block getType() {
        return instance.getBlock().getBlock();
    }

    public optional int getLegacyData:###();

    public (CommonPacket) PacketPlayOutTileEntityData getUpdatePacket();
#else

    #require TileEntity public IBlockData getBlockDataImpl() {
        Block block;
        int rawData;
  #if version >= 1.11
        rawData = instance.v();
  #else
        rawData = instance.u();
  #endif
  #if version >= 1.9
        block = instance.getBlock();
  #else
        block = instance.w();
  #endif
        return block.fromLegacyData(rawData);
    }

    public (BlockData) IBlockData getBlockData() {
        return instance#getBlockDataImpl();
    }
    public (Object) IBlockData getRawBlockData() {
        return instance#getBlockDataImpl();
    }

    #if version >= 1.11
        public optional int getLegacyData:v();
    #else
        public optional int getLegacyData:u();
    #endif

    #if version >= 1.12.1
        public (org.bukkit.Material) Block getType:getBlock();
        public (CommonPacket) PacketPlayOutTileEntityData getUpdatePacket();
    #elseif version >= 1.9.4
        public (org.bukkit.Material) Block getType:getBlock();
        public (CommonPacket) PacketPlayOutTileEntityData getUpdatePacket();
    #elseif version >= 1.9
        public (org.bukkit.Material) Block getType:getBlock();
        public (CommonPacket) net.minecraft.network.protocol.Packet<?> getUpdatePacket();
    #else
        public (org.bukkit.Material) Block getType:w();
        public (CommonPacket) net.minecraft.network.protocol.Packet getUpdatePacket();
    #endif
#endif

#select version >=
#case 1.14:  public boolean isRemoved();
#case 1.13:  public boolean isRemoved:x();
#case 1.11:  public boolean isRemoved() { return instance.y() || !instance.u(); } // Must also validate world isn't null
#case else:  public boolean isRemoved() { return instance.x() || !instance.t(); } // Must also validate world isn't null
#endselect

    <code>
    @SuppressWarnings("deprecation")
    public org.bukkit.block.BlockState toBukkit() {
        return com.bergerkiller.bukkit.common.conversion.blockstate.BlockStateConversion.INSTANCE.tileEntityToBlockState(getRaw());
    }

    public static TileEntityHandle fromBukkit(org.bukkit.block.BlockState blockState) {
        return createHandle(com.bergerkiller.bukkit.common.conversion.blockstate.BlockStateConversion.INSTANCE.blockStateToTileEntity(blockState));
    }
    </code>
}

class TileEntityFurnace extends TileEntity {
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();

#if version >= 1.21.2
    public static int fuelTime((ItemStackHandle) ItemStack itemstack) {
        return net.minecraft.server.MinecraftServer.getServer().fuelValues().burnDuration(itemstack);
    }
#elseif version >= 1.14
    public static int fuelTime((ItemStackHandle) ItemStack itemstack) {
        if (itemstack.isEmpty()) {
            return 0;
        } else {
            Item item = itemstack.getItem();
  #if version >= 1.18
            Integer fuelTime = (Integer) TileEntityFurnace.getFuel().get(item);
  #else
            Integer fuelTime = (Integer) TileEntityFurnace.f().get(item);
  #endif
            if (fuelTime == null) {
                return 0;
            } else {
                return fuelTime.intValue();
            }
        }
    }
#elseif version >= 1.13
    private static int fuelTime((ItemStackHandle) ItemStack itemstack);
#else
    public static int fuelTime((ItemStackHandle) ItemStack itemstack);
#endif
}

class TileEntityHopper extends TileEntity {
#if version >= 1.18
    public static boolean suckItems:suckInItems((org.bukkit.World) World world, (Object) IHopper ihopper);
#elseif version >= 1.17
    public static boolean suckItems:a((org.bukkit.World) World world, (Object) IHopper ihopper);
#else
    public static boolean suckItems((org.bukkit.World) World world, (Object) IHopper ihopper) {
        return TileEntityHopper.a(ihopper);
    }
#endif
}

class TileEntitySign extends TileEntity {
    public Object[] getRawFrontLines() {
#if version >= 1.20
        return instance.getFrontText().getMessages(false);
#elseif version >= 1.17
        return (Object[]) instance.messages;
#else
        return (Object[]) instance.lines;
#endif
    }

    public String[] getMessageFrontLines() {
#if version >= 1.20
        IChatBaseComponent[] lines = instance.getFrontText().getMessages(false);
#elseif version >= 1.17
        IChatBaseComponent[] lines = instance.messages;
#else
        IChatBaseComponent[] lines = instance.lines;
#endif
        return #formatLinesAsLegacyStrings(lines);
    }

<code>
    public static final Object[] ALL_EMPTY_RAW_LINES;
    public static final String[] ALL_EMPTY_STRING_LINES;
    static {
        Object raw_empty = ChatText.empty().getRawHandle();
        ALL_EMPTY_RAW_LINES = new Object[] {raw_empty, raw_empty, raw_empty, raw_empty};
        ALL_EMPTY_STRING_LINES = new String[] { "", "", "", "" };
    }
</code>

    public Object[] getRawBackLines() {
#if version >= 1.20
        return instance.getBackText().getMessages(false);
#else
        return TileEntitySignHandle.ALL_EMPTY_RAW_LINES;
#endif
    }

    public String[] getMessageBackLines() {
#if version >= 1.20
        IChatBaseComponent[] lines = instance.getBackText().getMessages(false);
        return #formatLinesAsLegacyStrings(lines);
#else
        return TileEntitySignHandle.ALL_EMPTY_STRING_LINES;
#endif
    }

    public void setFormattedFrontLine(int index, (ChatText) IChatBaseComponent text) {
#if version >= 1.20
        instance.setText(instance.getFrontText().setMessage(index, text), true);
#elseif version >= 1.18
        instance.setMessage(index, text);
        instance.setChanged();
#elseif version >= 1.17
        instance.a(index, text);
        instance.update();
#else
        instance.lines[index] = text;
        instance.update();
#endif
    }

    public void setFormattedBackLine(int index, (ChatText) IChatBaseComponent text) {
#if version >= 1.20
        instance.setText(instance.getBackText().setMessage(index, text), false);
#endif
    }

    <code>
    @Override
    public org.bukkit.block.Sign toBukkit() {
        return (org.bukkit.block.Sign) super.toBukkit();
    }

    public static TileEntitySignHandle fromBukkit(org.bukkit.block.Sign sign) {
        return createHandle(com.bergerkiller.bukkit.common.conversion.blockstate.BlockStateConversion.INSTANCE.blockStateToTileEntity(sign));
    }
    </code>
}