// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.universe.world.meta;

import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData;
import com.hypixel.hytale.codec.lookup.ACodecMapCodec;
import com.hypixel.hytale.server.core.universe.world.meta.state.exceptions.NoSuchBlockStateException;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Holder;
import org.bson.BsonValue;
import com.hypixel.hytale.codec.ExtraInfo;
import org.bson.BsonDocument;
import com.hypixel.hytale.math.vector.Vector3d;
import javax.annotation.Nonnull;
import com.google.common.flogger.StackSize;
import java.util.logging.Level;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.vector.Vector3i;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import java.util.concurrent.atomic.AtomicBoolean;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.lookup.CodecMapCodec;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.Component;

@Deprecated(forRemoval = true)
public abstract class BlockState implements Component<ChunkStore>
{
    private static final HytaleLogger LOGGER;
    public static final CodecMapCodec<BlockState> CODEC;
    public static final BuilderCodec<BlockState> BASE_CODEC;
    public static final KeyedCodec<String> TYPE_STRUCTURE;
    public static final String OPEN_WINDOW = "OpenWindow";
    public static final String CLOSE_WINDOW = "CloseWindow";
    final AtomicBoolean initialized;
    @Nullable
    private WorldChunk chunk;
    private Vector3i position;
    protected Ref<ChunkStore> reference;
    
    public BlockState() {
        this.initialized = new AtomicBoolean(false);
    }
    
    public void setReference(final Ref<ChunkStore> reference) {
        if (this.reference != null && this.reference.isValid()) {
            throw new IllegalArgumentException("Entity already has a valid EntityReference: " + String.valueOf(this.reference) + " new reference " + String.valueOf(reference));
        }
        this.reference = reference;
    }
    
    public Ref<ChunkStore> getReference() {
        return this.reference;
    }
    
    public void unloadFromWorld() {
        if (this.reference != null && this.reference.isValid()) {
            throw new IllegalArgumentException("Tried to unlock used block state");
        }
        this.chunk = null;
    }
    
    public boolean initialize(final BlockType blockType) {
        return true;
    }
    
    public void onUnload() {
    }
    
    public void validateInitialized() {
        if (!this.initialized.get()) {
            throw new IllegalArgumentException(String.valueOf(this));
        }
    }
    
    public int getIndex() {
        return ChunkUtil.indexBlockInColumn(this.position.x, this.position.y, this.position.z);
    }
    
    public void setPosition(final WorldChunk chunk, @Nullable final Vector3i position) {
        this.chunk = chunk;
        if (position != null) {
            position.assign(position.getX() & 0x1F, position.getY(), position.getZ() & 0x1F);
            if (position.equals(Vector3i.ZERO)) {
                BlockState.LOGGER.at(Level.WARNING).withStackTrace(StackSize.FULL).log("BlockState position set to (0,0,0): %s", this);
            }
            this.position = position;
        }
    }
    
    public void setPosition(@Nonnull final Vector3i position) {
        position.assign(position.getX() & 0x1F, position.getY(), position.getZ() & 0x1F);
        if (position.equals(Vector3i.ZERO)) {
            BlockState.LOGGER.at(Level.WARNING).withStackTrace(StackSize.FULL).log("BlockState position set to (0,0,0): %s", this);
        }
        this.position = position;
    }
    
    @Nonnull
    public Vector3i getPosition() {
        return this.position.clone();
    }
    
    public Vector3i __internal_getPosition() {
        return this.position;
    }
    
    public void clearPositionForSerialization() {
        this.position = null;
    }
    
    public int getBlockX() {
        return this.chunk.getX() << 5 | this.position.getX();
    }
    
    public int getBlockY() {
        return this.position.y;
    }
    
    public int getBlockZ() {
        return this.chunk.getZ() << 5 | this.position.getZ();
    }
    
    @Nonnull
    public Vector3i getBlockPosition() {
        return new Vector3i(this.getBlockX(), this.getBlockY(), this.getBlockZ());
    }
    
    @Nonnull
    public Vector3d getCenteredBlockPosition() {
        final BlockType blockType = this.getBlockType();
        final Vector3d blockCenter = new Vector3d(0.0, 0.0, 0.0);
        blockType.getBlockCenter(this.getRotationIndex(), blockCenter);
        return blockCenter.add(this.getBlockX(), this.getBlockY(), this.getBlockZ());
    }
    
    @Nullable
    public WorldChunk getChunk() {
        return this.chunk;
    }
    
    @Nullable
    public BlockType getBlockType() {
        return this.getChunk().getBlockType(this.position);
    }
    
    public int getRotationIndex() {
        return this.getChunk().getRotationIndex(this.position.x, this.position.y, this.position.z);
    }
    
    public void invalidate() {
    }
    
    public void markNeedsSave() {
        this.getChunk().markNeedsSaving();
    }
    
    public BsonDocument saveToDocument() {
        return BlockState.CODEC.encode((T)this).asDocument();
    }
    
    @Nullable
    @Override
    public Component<ChunkStore> clone() {
        final BsonDocument document = BlockState.CODEC.encode((T)this, ExtraInfo.THREAD_LOCAL.get()).asDocument();
        return BlockState.CODEC.decode(document, ExtraInfo.THREAD_LOCAL.get());
    }
    
    @Nonnull
    public Holder<ChunkStore> toHolder() {
        if (this.reference != null && this.reference.isValid() && this.chunk != null) {
            final Holder<ChunkStore> holder = ChunkStore.REGISTRY.newHolder();
            final Store<ChunkStore> componentStore = this.chunk.getWorld().getChunkStore().getStore();
            final Archetype<ChunkStore> archetype = componentStore.getArchetype(this.reference);
            for (int i = archetype.getMinIndex(); i < archetype.length(); ++i) {
                final ComponentType componentType = archetype.get(i);
                if (componentType != null) {
                    holder.addComponent(componentType, (Component)componentStore.getComponent(this.reference, (ComponentType<ChunkStore, T>)componentType));
                }
            }
            return holder;
        }
        final Holder<ChunkStore> holder = ChunkStore.REGISTRY.newHolder();
        final ComponentType<ChunkStore, ? extends BlockState> componentType2 = BlockStateModule.get().getComponentType(this.getClass());
        if (componentType2 == null) {
            throw new IllegalArgumentException("Unable to find component type for: " + String.valueOf(this));
        }
        holder.addComponent((ComponentType<ChunkStore, BlockState>)componentType2, this);
        return holder;
    }
    
    @Nullable
    public static BlockState load(final BsonDocument doc, @Nonnull final WorldChunk chunk, @Nonnull final Vector3i pos) throws NoSuchBlockStateException {
        return load(doc, chunk, pos, chunk.getBlockType(pos.getX(), pos.getY(), pos.getZ()));
    }
    
    @Nullable
    public static BlockState load(final BsonDocument doc, @Nullable final WorldChunk chunk, final Vector3i pos, final BlockType blockType) throws NoSuchBlockStateException {
        BlockState blockState;
        try {
            blockState = BlockState.CODEC.decode(doc);
        }
        catch (final ACodecMapCodec.UnknownIdException e) {
            throw new NoSuchBlockStateException(e);
        }
        blockState.setPosition(chunk, pos);
        if (chunk != null) {
            if (!blockState.initialize(blockType)) {
                return null;
            }
            blockState.initialized.set(true);
        }
        return blockState;
    }
    
    @Nullable
    @Deprecated
    public static BlockState ensureState(@Nonnull final WorldChunk worldChunk, final int x, final int y, final int z) {
        final BlockType blockType = worldChunk.getBlockType(x, y, z);
        if (blockType == null || blockType.isUnknown()) {
            return null;
        }
        final StateData state = blockType.getState();
        if (state == null || state.getId() == null) {
            return null;
        }
        final Vector3i position = new Vector3i(x, y, z);
        final BlockState blockState = BlockStateModule.get().createBlockState(state.getId(), worldChunk, position, blockType);
        if (blockState != null) {
            worldChunk.setState(x, y, z, blockState);
        }
        return blockState;
    }
    
    @Deprecated
    public static BlockState getBlockState(@Nullable final Ref<ChunkStore> reference, @Nonnull final ComponentAccessor<ChunkStore> componentAccessor) {
        if (reference == null) {
            return null;
        }
        final ComponentType<ChunkStore, BlockState> componentType = findComponentType(componentAccessor.getArchetype(reference), BlockState.class);
        if (componentType == null) {
            return null;
        }
        return componentAccessor.getComponent(reference, componentType);
    }
    
    @Nullable
    @Deprecated
    public static BlockState getBlockState(final int index, @Nonnull final ArchetypeChunk<ChunkStore> archetypeChunk) {
        final ComponentType<ChunkStore, BlockState> componentType = findComponentType(archetypeChunk.getArchetype(), BlockState.class);
        if (componentType == null) {
            return null;
        }
        return archetypeChunk.getComponent(index, componentType);
    }
    
    @Nullable
    @Deprecated
    public static BlockState getBlockState(@Nonnull final Holder<ChunkStore> holder) {
        final ComponentType<ChunkStore, BlockState> componentType = findComponentType(holder.getArchetype(), BlockState.class);
        if (componentType == null) {
            return null;
        }
        return holder.getComponent(componentType);
    }
    
    @Nullable
    private static <C extends Component<ChunkStore>, T extends C> ComponentType<ChunkStore, T> findComponentType(@Nonnull final Archetype<ChunkStore> archetype, @Nonnull final Class<C> entityClass) {
        for (int i = archetype.getMinIndex(); i < archetype.length(); ++i) {
            final ComponentType<ChunkStore, ? extends Component<ChunkStore>> componentType = (ComponentType<ChunkStore, ? extends Component<ChunkStore>>)archetype.get(i);
            if (componentType != null) {
                if (entityClass.isAssignableFrom(componentType.getTypeClass())) {
                    return (ComponentType<ChunkStore, T>)componentType;
                }
            }
        }
        return null;
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
        CODEC = new CodecMapCodec<BlockState>("Type");
        BASE_CODEC = BuilderCodec.abstractBuilder(BlockState.class).addField(new KeyedCodec<Vector3i>("Position", Vector3i.CODEC), (entity, o) -> entity.position = o, entity -> {
            if (entity.position == null || Vector3i.ZERO.equals(entity.position)) {
                return null;
            }
            else {
                return entity.position;
            }
        }).build();
        TYPE_STRUCTURE = new KeyedCodec<String>("Type", Codec.STRING);
    }
}
