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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.util.Supplier;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.CrashReportCategory;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.BlockPos;
import javax.annotation.Nullable;
import net.minecraft.world.level.Level;
import org.apache.logging.log4j.Logger;

public abstract class BlockEntity {
    private static final Logger LOGGER;
    private final BlockEntityType<?> type;
    @Nullable
    protected Level level;
    protected BlockPos worldPosition;
    protected boolean remove;
    @Nullable
    private BlockState blockState;
    private boolean hasLoggedInvalidStateBefore;
    
    public BlockEntity(final BlockEntityType<?> cdm) {
        this.worldPosition = BlockPos.ZERO;
        this.type = cdm;
    }
    
    @Nullable
    public Level getLevel() {
        return this.level;
    }
    
    public void setLevelAndPosition(final Level bqb, final BlockPos fu) {
        this.level = bqb;
        this.worldPosition = fu.immutable();
    }
    
    public boolean hasLevel() {
        return this.level != null;
    }
    
    public void load(final BlockState cfj, final CompoundTag le) {
        this.worldPosition = new BlockPos(le.getInt("x"), le.getInt("y"), le.getInt("z"));
    }
    
    public CompoundTag save(final CompoundTag le) {
        return this.saveMetadata(le);
    }
    
    private CompoundTag saveMetadata(final CompoundTag le) {
        final ResourceLocation uh3 = BlockEntityType.getKey(this.getType());
        if (uh3 == null) {
            throw new RuntimeException(this.getClass() + " is missing a mapping! This is a bug!");
        }
        le.putString("id", uh3.toString());
        le.putInt("x", this.worldPosition.getX());
        le.putInt("y", this.worldPosition.getY());
        le.putInt("z", this.worldPosition.getZ());
        return le;
    }
    
    @Nullable
    public static BlockEntity loadStatic(final BlockState cfj, final CompoundTag le) {
        final String string3 = le.getString("id");
        final Object o;
        final Object o2;
        return Registry.BLOCK_ENTITY_TYPE.getOptional(new ResourceLocation(string3)).map(cdm -> {
            try {
                return cdm.create();
            }
            catch (Throwable throwable3) {
                BlockEntity.LOGGER.error("Failed to create block entity {}", o, throwable3);
                return null;
            }
        }).<BlockEntity>map(cdl -> {
            try {
                cdl.load(cfj, le);
                return cdl;
            }
            catch (Throwable throwable4) {
                BlockEntity.LOGGER.error("Failed to load data for block entity {}", o2, throwable4);
                return null;
            }
        }).orElseGet(() -> {
            BlockEntity.LOGGER.warn("Skipping BlockEntity with id {}", string3);
            return null;
        });
    }
    
    public void setChanged() {
        if (this.level != null) {
            this.blockState = this.level.getBlockState(this.worldPosition);
            this.level.blockEntityChanged(this.worldPosition, this);
            if (!this.blockState.isAir()) {
                this.level.updateNeighbourForOutputSignal(this.worldPosition, this.blockState.getBlock());
            }
        }
    }
    
    public double getViewDistance() {
        return 64.0;
    }
    
    public BlockPos getBlockPos() {
        return this.worldPosition;
    }
    
    public BlockState getBlockState() {
        if (this.blockState == null) {
            this.blockState = this.level.getBlockState(this.worldPosition);
        }
        return this.blockState;
    }
    
    @Nullable
    public ClientboundBlockEntityDataPacket getUpdatePacket() {
        return null;
    }
    
    public CompoundTag getUpdateTag() {
        return this.saveMetadata(new CompoundTag());
    }
    
    public boolean isRemoved() {
        return this.remove;
    }
    
    public void setRemoved() {
        this.remove = true;
    }
    
    public void clearRemoved() {
        this.remove = false;
    }
    
    public boolean triggerEvent(final int integer1, final int integer2) {
        return false;
    }
    
    public void clearCache() {
        this.blockState = null;
    }
    
    public void fillCrashReportCategory(final CrashReportCategory k) {
        k.setDetail("Name", () -> Registry.BLOCK_ENTITY_TYPE.getKey(this.getType()) + " // " + this.getClass().getCanonicalName());
        if (this.level == null) {
            return;
        }
        CrashReportCategory.populateBlockDetails(k, this.worldPosition, this.getBlockState());
        CrashReportCategory.populateBlockDetails(k, this.worldPosition, this.level.getBlockState(this.worldPosition));
    }
    
    public void setPosition(final BlockPos fu) {
        this.worldPosition = fu.immutable();
    }
    
    public boolean onlyOpCanSetNbt() {
        return false;
    }
    
    public void rotate(final Rotation cap) {
    }
    
    public void mirror(final Mirror bzj) {
    }
    
    public BlockEntityType<?> getType() {
        return this.type;
    }
    
    public void logInvalidState() {
        if (this.hasLoggedInvalidStateBefore) {
            return;
        }
        this.hasLoggedInvalidStateBefore = true;
        BlockEntity.LOGGER.warn("Block entity invalid: {} @ {}", new Supplier[] { () -> Registry.BLOCK_ENTITY_TYPE.getKey(this.getType()), this::getBlockPos });
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
