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

package com.hypixel.hytale.builtin.instances.interactions;

import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.protocol.BlockPosition;
import com.hypixel.hytale.math.Axis;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import java.util.UUID;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.Ref;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.builtin.instances.blocks.ConfigurableInstanceBlock;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.builtin.instances.InstancesPlugin;
import com.hypixel.hytale.server.core.modules.entity.teleport.PendingTeleport;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
import com.hypixel.hytale.math.vector.Vector3i;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.protocol.WaitForDataFrom;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction;

public class TeleportConfigInstanceInteraction extends SimpleBlockInteraction
{
    @Nonnull
    private static final Message MESSAGE_GENERAL_INTERACTION_CONFIGURE_INSTANCE_NO_INSTANCE_NAME;
    @Nonnull
    public static final BuilderCodec<TeleportConfigInstanceInteraction> CODEC;
    private static final int SET_BLOCK_SETTINGS = 256;
    
    @Nonnull
    @Override
    public WaitForDataFrom getWaitForDataFrom() {
        return WaitForDataFrom.Server;
    }
    
    @Override
    protected void interactWithBlock(@Nonnull final World world, @Nonnull final CommandBuffer<EntityStore> commandBuffer, @Nonnull final InteractionType type, @Nonnull final InteractionContext context, @Nullable final ItemStack itemInHand, @Nonnull final Vector3i targetBlock, @Nonnull final CooldownHandler cooldownHandler) {
        final Ref<EntityStore> ref = context.getEntity();
        final Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType());
        if (playerComponent == null || playerComponent.isWaitingForClientReady()) {
            return;
        }
        final Archetype<EntityStore> archetype = commandBuffer.getArchetype(ref);
        if (archetype.contains(Teleport.getComponentType()) || archetype.contains(PendingTeleport.getComponentType())) {
            return;
        }
        final InstancesPlugin module = InstancesPlugin.get();
        final Universe universe = Universe.get();
        final ChunkStore chunkStore = world.getChunkStore();
        final Ref<ChunkStore> chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z));
        if (chunkRef == null || !chunkRef.isValid()) {
            return;
        }
        final BlockComponentChunk blockComponentChunk = chunkStore.getStore().getComponent(chunkRef, BlockComponentChunk.getComponentType());
        assert blockComponentChunk != null;
        final Ref<ChunkStore> blockRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(targetBlock.x, targetBlock.y, targetBlock.z));
        if (blockRef == null || !blockRef.isValid()) {
            return;
        }
        final ConfigurableInstanceBlock configurableInstanceBlock = chunkStore.getStore().getComponent(blockRef, ConfigurableInstanceBlock.getComponentType());
        if (configurableInstanceBlock == null) {
            return;
        }
        if (configurableInstanceBlock.getInstanceName() == null) {
            playerComponent.sendMessage(TeleportConfigInstanceInteraction.MESSAGE_GENERAL_INTERACTION_CONFIGURE_INSTANCE_NO_INSTANCE_NAME);
            return;
        }
        CompletableFuture<World> targetWorldFuture = null;
        Transform returnPoint = null;
        World targetWorld;
        if (configurableInstanceBlock.getInstanceKey() != null) {
            targetWorld = universe.getWorld(configurableInstanceBlock.getInstanceKey());
            if (targetWorld == null) {
                returnPoint = makeReturnPoint(configurableInstanceBlock, context, commandBuffer);
                targetWorldFuture = module.spawnInstance(configurableInstanceBlock.getInstanceName(), configurableInstanceBlock.getInstanceKey(), world, returnPoint);
            }
        }
        else {
            final UUID worldUuid = configurableInstanceBlock.getWorldUUID();
            targetWorldFuture = configurableInstanceBlock.getWorldFuture();
            targetWorld = ((worldUuid != null) ? universe.getWorld(worldUuid) : null);
            if (targetWorld == null && targetWorldFuture == null) {
                returnPoint = makeReturnPoint(configurableInstanceBlock, context, commandBuffer);
                targetWorldFuture = module.spawnInstance(configurableInstanceBlock.getInstanceName(), world, returnPoint);
                configurableInstanceBlock.setWorldFuture(targetWorldFuture);
                targetWorldFuture.thenAccept(instanceWorld -> {
                    if (!blockRef.isValid()) {
                        return;
                    }
                    else {
                        configurableInstanceBlock.setWorldFuture(null);
                        configurableInstanceBlock.setWorldUUID(instanceWorld.getWorldConfig().getUuid());
                        blockComponentChunk.markNeedsSaving();
                        return;
                    }
                });
            }
        }
        if (targetWorldFuture != null) {
            final Transform personalReturnPoint = getPersonalReturnPoint(configurableInstanceBlock, context, returnPoint, commandBuffer);
            InstancesPlugin.teleportPlayerToLoadingInstance(ref, commandBuffer, targetWorldFuture, personalReturnPoint);
        }
        else if (targetWorld != null) {
            final Transform personalReturnPoint = getPersonalReturnPoint(configurableInstanceBlock, context, returnPoint, commandBuffer);
            InstancesPlugin.teleportPlayerToInstance(ref, commandBuffer, targetWorld, personalReturnPoint);
        }
        final double removeBlockAfter = configurableInstanceBlock.getRemoveBlockAfter();
        if (removeBlockAfter >= 0.0) {
            if (removeBlockAfter == 0.0) {
                final long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z);
                final WorldChunk worldChunk = world.getChunk(chunkIndex);
                worldChunk.setBlock(targetBlock.x, targetBlock.y, targetBlock.z, 0, 256);
            }
            else {
                final int block = world.getBlock(targetBlock);
                new CompletableFuture<Object>().completeOnTimeout(null, (long)(removeBlockAfter * 1.0E9), TimeUnit.NANOSECONDS).thenRunAsync(() -> {
                    if (world.getBlock(targetBlock) == block) {
                        final long chunkIndex2 = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z);
                        final WorldChunk worldChunk2 = world.getChunk(chunkIndex2);
                        worldChunk2.setBlock(targetBlock.x, targetBlock.y, targetBlock.z, 0, 256);
                    }
                }, (Executor)world);
            }
        }
    }
    
    @Override
    protected void simulateInteractWithBlock(@Nonnull final InteractionType type, @Nonnull final InteractionContext context, @Nullable final ItemStack itemInHand, @Nonnull final World world, @Nonnull final Vector3i targetBlock) {
    }
    
    @Nullable
    private static Transform getPersonalReturnPoint(@Nonnull final ConfigurableInstanceBlock state, @Nonnull final InteractionContext context, @Nullable final Transform returnPoint, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (!state.isPersonalReturnPoint()) {
            return null;
        }
        if (returnPoint == null) {
            return makeReturnPoint(state, context, componentAccessor);
        }
        return returnPoint;
    }
    
    @Nonnull
    private static Transform makeReturnPoint(@Nonnull final ConfigurableInstanceBlock state, @Nonnull final InteractionContext context, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final BlockPosition targetBlock = context.getTargetBlock();
        if (targetBlock == null) {
            throw new IllegalArgumentException("Can't use OriginSource.BLOCK without a target block");
        }
        final World world = componentAccessor.getExternalData().getWorld();
        final ChunkStore chunkStore = world.getChunkStore();
        final Store<ChunkStore> chunkComponentStore = chunkStore.getStore();
        final long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z);
        final Ref<ChunkStore> chunkRef = chunkStore.getChunkReference(chunkIndex);
        if (chunkRef == null || !chunkRef.isValid()) {
            throw new IllegalArgumentException("Chunk not loaded");
        }
        final BlockChunk blockChunkComponent = chunkComponentStore.getComponent(chunkRef, BlockChunk.getComponentType());
        assert blockChunkComponent != null;
        final WorldChunk worldChunkComponent = chunkComponentStore.getComponent(chunkRef, WorldChunk.getComponentType());
        assert worldChunkComponent != null;
        final BlockType blockType = worldChunkComponent.getBlockType(targetBlock.x, targetBlock.y, targetBlock.z);
        if (blockType == null) {
            throw new IllegalArgumentException("Block type not found");
        }
        final IndexedLookupTableAssetMap<String, BlockBoundingBoxes> hitboxAssetMap = BlockBoundingBoxes.getAssetMap();
        final BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.y);
        final int rotationIndex = section.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z);
        final RotationTuple rotation = RotationTuple.get(rotationIndex);
        final Box hitbox = hitboxAssetMap.getAsset(blockType.getHitboxTypeIndex()).get(rotationIndex).getBoundingBox();
        final Vector3d vector3d;
        final Vector3d position = vector3d = ((state.getPositionOffset() != null) ? rotation.rotate(state.getPositionOffset()) : new Vector3d());
        vector3d.x += hitbox.middleX() + targetBlock.x;
        final Vector3d vector3d2 = position;
        vector3d2.y += hitbox.middleY() + targetBlock.y;
        final Vector3d vector3d3 = position;
        vector3d3.z += hitbox.middleZ() + targetBlock.z;
        Vector3f rotationOutput = Vector3f.NaN;
        if (state.getRotation() != null) {
            rotationOutput = state.getRotation().clone();
            rotationOutput.addRotationOnAxis(Axis.Y, rotation.yaw().getDegrees());
            rotationOutput.addRotationOnAxis(Axis.X, rotation.pitch().getDegrees());
        }
        return new Transform(position, rotationOutput);
    }
    
    static {
        MESSAGE_GENERAL_INTERACTION_CONFIGURE_INSTANCE_NO_INSTANCE_NAME = Message.translation("server.general.interaction.configureInstance.noInstanceName");
        CODEC = BuilderCodec.builder(TeleportConfigInstanceInteraction.class, TeleportConfigInstanceInteraction::new, SimpleBlockInteraction.CODEC).documentation("""
                                                                                                                                                                  Teleports the **Player** to the named instance, creating it if required.
                                                                                                                                                                  
                                                                                                                                                                  This is configured via a UI instead of inside the interaction. This interaction just executes that set configuration.""").build();
    }
}
