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<?> bwm) {
        this.worldPosition = BlockPos.ZERO;
        this.type = bwm;
    }
    
    @Nullable
    public Level getLevel() {
        return this.level;
    }
    
    public void setLevelAndPosition(final Level bjw, final BlockPos fk) {
        this.level = bjw;
        this.worldPosition = fk.immutable();
    }
    
    public boolean hasLevel() {
        return this.level != null;
    }
    
    public void load(final CompoundTag jt) {
        this.worldPosition = new BlockPos(jt.getInt("x"), jt.getInt("y"), jt.getInt("z"));
    }
    
    public CompoundTag save(final CompoundTag jt) {
        return this.saveMetadata(jt);
    }
    
    private CompoundTag saveMetadata(final CompoundTag jt) {
        final ResourceLocation sm3 = BlockEntityType.getKey(this.getType());
        if (sm3 == null) {
            throw new RuntimeException(this.getClass() + " is missing a mapping! This is a bug!");
        }
        jt.putString("id", sm3.toString());
        jt.putInt("x", this.worldPosition.getX());
        jt.putInt("y", this.worldPosition.getY());
        jt.putInt("z", this.worldPosition.getZ());
        return jt;
    }
    
    @Nullable
    public static BlockEntity loadStatic(final CompoundTag jt) {
        final String string2 = jt.getString("id");
        final Object o;
        final Object o2;
        return Registry.BLOCK_ENTITY_TYPE.getOptional(new ResourceLocation(string2)).map(bwm -> {
            try {
                return bwm.create();
            }
            catch (Throwable throwable3) {
                BlockEntity.LOGGER.error("Failed to create block entity {}", o, throwable3);
                return null;
            }
        }).<BlockEntity>map(bwl -> {
            try {
                bwl.load(jt);
                return bwl;
            }
            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 {}", string2);
            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 distanceToSqr(final double double1, final double double2, final double double3) {
        final double double4 = this.worldPosition.getX() + 0.5 - double1;
        final double double5 = this.worldPosition.getY() + 0.5 - double2;
        final double double6 = this.worldPosition.getZ() + 0.5 - double3;
        return double4 * double4 + double5 * double5 + double6 * double6;
    }
    
    public double getViewDistance() {
        return 4096.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 i) {
        i.setDetail("Name", () -> Registry.BLOCK_ENTITY_TYPE.getKey(this.getType()) + " // " + this.getClass().getCanonicalName());
        if (this.level == null) {
            return;
        }
        CrashReportCategory.populateBlockDetails(i, this.worldPosition, this.getBlockState());
        CrashReportCategory.populateBlockDetails(i, this.worldPosition, this.level.getBlockState(this.worldPosition));
    }
    
    public void setPosition(final BlockPos fk) {
        this.worldPosition = fk.immutable();
    }
    
    public boolean onlyOpCanSetNbt() {
        return false;
    }
    
    public void rotate(final Rotation btu) {
    }
    
    public void mirror(final Mirror bsu) {
    }
    
    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();
    }
}
