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

package com.hypixel.hytale.builtin.portals.ui;

import com.hypixel.hytale.codec.builder.BuilderCodec;
import java.util.concurrent.CompletionStage;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig;
import com.hypixel.hytale.server.core.modules.block.BlockModule;
import com.hypixel.hytale.builtin.portals.PortalsPlugin;
import javax.annotation.Nullable;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.asset.type.portalworld.PortalSpawn;
import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider;
import com.hypixel.hytale.server.core.universe.world.spawn.IndividualSpawnProvider;
import java.util.concurrent.CompletableFuture;
import com.hypixel.hytale.server.core.universe.world.WorldConfig;
import java.util.UUID;
import com.hypixel.hytale.builtin.portals.components.PortalDevice;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.World;
import java.util.logging.Level;
import com.hypixel.hytale.logger.HytaleLogger;
import java.util.concurrent.Executor;
import com.hypixel.hytale.builtin.portals.resources.PortalWorld;
import com.hypixel.hytale.builtin.instances.removal.RemovalCondition;
import com.hypixel.hytale.builtin.portals.integrations.PortalRemovalCondition;
import com.hypixel.hytale.builtin.instances.config.InstanceDiscoveryConfig;
import com.hypixel.hytale.builtin.instances.config.InstanceWorldConfig;
import com.hypixel.hytale.builtin.instances.InstancesPlugin;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.universe.world.SoundUtil;
import com.hypixel.hytale.protocol.SoundCategory;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.protocol.packets.interface_.Page;
import java.util.List;
import com.hypixel.hytale.server.core.asset.util.ColorParseUtil;
import com.hypixel.hytale.server.core.asset.type.portalworld.PillTag;
import com.hypixel.hytale.server.core.asset.type.portalworld.PortalDescription;
import com.hypixel.hytale.server.core.asset.type.portalworld.PortalType;
import com.hypixel.hytale.server.core.asset.type.item.config.PortalKey;
import com.hypixel.hytale.builtin.portals.integrations.PortalGameplayConfig;
import com.hypixel.hytale.server.core.ui.builder.EventData;
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import java.util.concurrent.TimeUnit;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.builtin.portals.components.PortalDeviceConfig;
import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage;

public class PortalDeviceSummonPage extends InteractiveCustomUIPage<Data>
{
    private final PortalDeviceConfig config;
    private final Ref<ChunkStore> blockRef;
    private final ItemStack offeredItemStack;
    private static final Transform DEFAULT_WORLDGEN_SPAWN;
    
    public PortalDeviceSummonPage(@Nonnull final PlayerRef playerRef, final PortalDeviceConfig config, final Ref<ChunkStore> blockRef, final ItemStack offeredItemStack) {
        super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, Data.CODEC);
        this.config = config;
        this.blockRef = blockRef;
        this.offeredItemStack = offeredItemStack;
    }
    
    @Override
    public void build(@Nonnull final Ref<EntityStore> ref, @Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder, @Nonnull final Store<EntityStore> store) {
        final Player playerComponent = store.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        final State state = this.computeState(playerComponent, store);
        if (state == Error.INVALID_BLOCK) {
            return;
        }
        Label_0303: {
            if (state instanceof final CanSpawnPortal canSpawn) {
                break Label_0303;
            }
            commandBuilder.append("Pages/PortalDeviceError.ui");
            if (state == Error.NOTHING_OFFERED || state == Error.NOT_A_PORTAL_KEY) {
                commandBuilder.set("#UsageErrorTitle.Text", Message.translation("server.customUI.portalDevice.needPortalKey"));
                commandBuilder.set("#UsageErrorLabel.Text", Message.translation("server.customUI.portalDevice.nothingHeld"));
                return;
            }
            if (state == Error.PORTAL_INSIDE_PORTAL) {
                commandBuilder.set("#UsageErrorLabel.Text", Message.translation("server.customUI.portalDevice.portalInsidePortal"));
                return;
            }
            if (state == Error.MAX_ACTIVE_PORTALS) {
                commandBuilder.set("#UsageErrorLabel.Text", Message.translation("server.customUI.portalDevice.maxFragments").param("max", 4));
                return;
            }
            Label_0215: {
                if (!(state instanceof InstanceKeyNotFound)) {
                    break Label_0215;
                }
                final InstanceKeyNotFound instanceKeyNotFound = (InstanceKeyNotFound)state;
                try {
                    final String instanceId = instanceKeyNotFound.instanceId();
                    commandBuilder.set("#UsageErrorLabel.Text", "The instance id '" + instanceId + "' does not exist, this is a developer error with the portaltype.");
                    return;
                    final CanSpawnPortal canSpawn;
                    PortalGameplayConfig gameplayConfig = null;
                    long minutesBreach;
                    long totalTimeLimit = 0L;
                    long exploMinutes;
                    String portalTypeId;
                    PortalKey portalKey;
                    PortalType portalType;
                    PortalDescription portalDesc;
                    String[] objectivesKeys;
                    String[] wisdomKeys;
                    Label_0586:Block_12_Outer:
                    while (true) {
                        while (true) {
                            minutesBreach = TimeUnit.SECONDS.toMinutes(gameplayConfig.getVoidEvent().getDurationSeconds());
                            exploMinutes = totalTimeLimit - minutesBreach;
                            commandBuilder.set("#ExplorationTimeText.TextSpans", Message.translation("server.customUI.portalDevice.minutesToExplore").param("time", exploMinutes));
                            commandBuilder.set("#BreachTimeBullet.Visible", true);
                            commandBuilder.set("#BreachTimeText.TextSpans", Message.translation("server.customUI.portalDevice.minutesVoidInvasion").param("time", minutesBreach));
                            break Label_0586;
                            Label_0278: {
                                commandBuilder.set("#UsageErrorLabel.Text", Message.translation("server.customUI.portalDevice.unknownError").param("state", state.toString()));
                            }
                            return;
                            eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#SummonButton", EventData.of("Action", "SummonActivated"), false);
                            eventBuilder.addEventBinding(CustomUIEventBindingType.MouseEntered, "#SummonButton", EventData.of("Action", "SummonMouseEntered"), false);
                            eventBuilder.addEventBinding(CustomUIEventBindingType.MouseExited, "#SummonButton", EventData.of("Action", "SummonMouseExited"), false);
                            return;
                            iftrue(Label_0258:)(!(state instanceof PortalTypeNotFound));
                            portalTypeId = ((PortalTypeNotFound)state).portalTypeId();
                            commandBuilder.set("#UsageErrorLabel.Text", "The portaltype id '" + portalTypeId + "' does not exist, this is a developer error with the portal key.");
                            return;
                            commandBuilder.append("Pages/PortalDeviceSummon.ui");
                            portalKey = canSpawn.portalKey();
                            portalType = canSpawn.portalType();
                            portalDesc = portalType.getDescription();
                            commandBuilder.set("#Artwork.Background", "Pages/Portals/" + portalDesc.getSplashImageFilename());
                            commandBuilder.set("#Title0.TextSpans", portalDesc.getDisplayName());
                            commandBuilder.set("#FlavorLabel.TextSpans", portalDesc.getFlavorText());
                            updateCustomPills(commandBuilder, portalType);
                            objectivesKeys = portalDesc.getObjectivesKeys();
                            wisdomKeys = portalDesc.getWisdomKeys();
                            commandBuilder.set("#Objectives.Visible", objectivesKeys.length > 0);
                            commandBuilder.set("#Tips.Visible", wisdomKeys.length > 0);
                            updateBulletList(commandBuilder, "#ObjectivesList", objectivesKeys);
                            updateBulletList(commandBuilder, "#TipsList", wisdomKeys);
                            gameplayConfig = portalType.getGameplayConfig().getPluginConfig().get((Class<? extends PortalGameplayConfig>)PortalGameplayConfig.class);
                            totalTimeLimit = TimeUnit.SECONDS.toMinutes(portalKey.getTimeLimitSeconds());
                            iftrue(Label_0564:)(!portalType.isVoidInvasionEnabled());
                            continue Block_12_Outer;
                        }
                        while (true) {
                            commandBuilder.set("#UsageErrorLabel.Text", "The gameplay config set on the PortalType set in the key does not have a Portal plugin configuration, this is a developer error.");
                            return;
                            Label_0258: {
                                iftrue(Label_0278:)(state != Error.BOTCHED_GAMEPLAY_CONFIG);
                            }
                            continue;
                        }
                        Label_0564: {
                            commandBuilder.set("#ExplorationTimeText.TextSpans", Message.translation("server.customUI.portalDevice.durationMins").param("time", totalTimeLimit));
                        }
                        continue Label_0586;
                    }
                }
                catch (final Throwable cause) {
                    throw new MatchException(cause.toString(), cause);
                }
            }
        }
    }
    
    private static void updateCustomPills(final UICommandBuilder commandBuilder, final PortalType portalType) {
        final List<PillTag> pills = portalType.getDescription().getPillTags();
        for (int i = 0; i < pills.size(); ++i) {
            final PillTag pillTag = pills.get(i);
            final String child = "#Pills[" + i;
            commandBuilder.append("#Pills", "Pages/Portals/Pill.ui");
            commandBuilder.set(child + ".Background.Color", ColorParseUtil.colorToHexString(pillTag.getColor()));
            commandBuilder.set(child + " #Label.TextSpans", pillTag.getMessage());
        }
    }
    
    private static void updateBulletList(final UICommandBuilder commandBuilder, final String selector, final String[] messageKeys) {
        for (int i = 0; i < messageKeys.length; ++i) {
            final String messageKey = messageKeys[i];
            final String child = selector + "[" + i;
            commandBuilder.append(selector, "Pages/Portals/BulletPoint.ui");
            commandBuilder.set(child + " #Label.TextSpans", Message.translation(messageKey));
        }
    }
    
    public static Message createDescription(final PortalType portalType, final int timeLimitSeconds) {
        final Message msg = Message.empty();
        final Message durationMsg = formatDurationCrudely(timeLimitSeconds);
        msg.insert(Message.translation("server.customUI.portalDevice.timeLimit").param("limit", durationMsg.color("#f9cb13")));
        return msg;
    }
    
    private static Message formatDurationCrudely(final int seconds) {
        if (seconds < 0) {
            return Message.translation("server.customUI.portalDevice.durationUnlimited");
        }
        if (seconds >= 120) {
            final int minutes = seconds / 60;
            return Message.translation("server.customUI.portalDevice.durationMinutes").param("duration", minutes);
        }
        return Message.translation("server.customUI.portalDevice.durationSeconds").param("duration", seconds);
    }
    
    @Override
    public void handleDataEvent(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store, @Nonnull final Data data) {
        final Player playerComponent = store.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        final State state = this.computeState(playerComponent, store);
        if (!(state instanceof CanSpawnPortal)) {
            return;
        }
        final CanSpawnPortal canSpawn = (CanSpawnPortal)state;
        if ("SummonMouseEntered".equals(data.action)) {
            final UICommandBuilder commandBuilder = new UICommandBuilder();
            commandBuilder.set("#Vignette.Visible", true);
            this.sendUpdate(commandBuilder, null, false);
            return;
        }
        if ("SummonMouseExited".equals(data.action)) {
            final UICommandBuilder commandBuilder = new UICommandBuilder();
            commandBuilder.set("#Vignette.Visible", false);
            this.sendUpdate(commandBuilder, null, false);
            return;
        }
        playerComponent.getPageManager().setPage(ref, store, Page.None);
        final World originWorld = store.getExternalData().getWorld();
        final int index = canSpawn.blockState().getIndex();
        final int x = ChunkUtil.xFromBlockInColumn(index);
        final int y = ChunkUtil.yFromBlockInColumn(index);
        final int z = ChunkUtil.zFromBlockInColumn(index);
        final WorldChunk worldChunk = canSpawn.worldChunk();
        final PortalKey portalKey = canSpawn.portalKey();
        final PortalDevice portalDevice = canSpawn.portalDevice();
        final BlockType blockType = worldChunk.getBlockType(x, y, z);
        if (blockType != portalDevice.getBaseBlockType()) {
            return;
        }
        if (!this.config.areBlockStatesValid(blockType)) {
            return;
        }
        final int rotation = worldChunk.getRotationIndex(x, y, z);
        final BlockType spawningType = blockType.getBlockForState(this.config.getSpawningState());
        final BlockType onType = blockType.getBlockForState(this.config.getOnState());
        final BlockType offType = blockType.getBlockForState(this.config.getOffState());
        final int setting = 6;
        worldChunk.setBlock(x, y, z, BlockType.getAssetMap().getIndex(spawningType.getId()), spawningType, rotation, 0, 6);
        final double worldX = ChunkUtil.worldCoordFromLocalCoord(worldChunk.getX(), x) + 0.5;
        final double worldY = y + 0.5;
        final double worldZ = ChunkUtil.worldCoordFromLocalCoord(worldChunk.getZ(), z) + 0.5;
        if (spawningType.getInteractionSoundEventIndex() != 0) {
            SoundUtil.playSoundEvent3d(spawningType.getInteractionSoundEventIndex(), SoundCategory.SFX, worldX, worldY, worldZ, store);
        }
        decrementItemInHand(playerComponent.getInventory(), 1);
        final Transform transform = new Transform(x + 0.5, y + 1.0, z + 0.5);
        final UUIDComponent uuidComponent = store.getComponent(ref, UUIDComponent.getComponentType());
        assert uuidComponent != null;
        final PortalType portalType = canSpawn.portalType;
        final UUID playerUUID = uuidComponent.getUuid();
        final PortalGameplayConfig gameplayConfig = canSpawn.portalGameplayConfig;
        InstancesPlugin.get().spawnInstance(portalType.getInstanceId(), originWorld, transform).thenCompose(spawnedWorld -> {
            final WorldConfig worldConfig = spawnedWorld.getWorldConfig();
            worldConfig.setDeleteOnUniverseStart(true);
            worldConfig.setDeleteOnRemove(true);
            worldConfig.setGameplayConfig(portalType.getGameplayConfigId());
            final InstanceWorldConfig instanceConfig = InstanceWorldConfig.ensureAndGet(worldConfig);
            if (instanceConfig.getDiscovery() == null) {
                final InstanceDiscoveryConfig discoveryConfig = new InstanceDiscoveryConfig();
                discoveryConfig.setTitleKey(portalType.getDescription().getDisplayNameKey());
                discoveryConfig.setSubtitleKey("server.portals.discoverySubtitle");
                discoveryConfig.setDisplay(true);
                discoveryConfig.setAlwaysDisplay(true);
                instanceConfig.setDiscovery(discoveryConfig);
            }
            final PortalRemovalCondition portalRemoval = new PortalRemovalCondition(portalKey.getTimeLimitSeconds());
            instanceConfig.setRemovalConditions(portalRemoval);
            final PortalWorld portalWorld = spawnedWorld.getEntityStore().getStore().getResource(PortalWorld.getResourceType());
            portalWorld.init(portalType, portalKey.getTimeLimitSeconds(), portalRemoval, gameplayConfig);
            final String returnBlockType = portalDevice.getConfig().getReturnBlock();
            if (returnBlockType == null) {
                throw new RuntimeException("Return block type on PortalDevice is misconfigured");
            }
            else {
                return spawnReturnPortal(spawnedWorld, portalWorld, playerUUID, returnBlockType);
            }
        }).thenAcceptAsync(spawnedWorld -> {
            portalDevice.setDestinationWorld(spawnedWorld);
            worldChunk.setBlock(x, y, z, BlockType.getAssetMap().getIndex(onType.getId()), onType, rotation, 0, 6);
        }, (Executor)originWorld).exceptionallyAsync(t -> {
            playerComponent.sendMessage(Message.translation("server.portals.device.internalErrorSpawning"));
            HytaleLogger.getLogger().at(Level.SEVERE).withCause(t).log("Error creating instance for Portal Device " + String.valueOf(portalKey), (Object)t);
            worldChunk.setBlock(x, y, z, BlockType.getAssetMap().getIndex(offType.getId()), offType, rotation, 0, 6);
            return null;
        }, (Executor)originWorld);
    }
    
    private static CompletableFuture<World> spawnReturnPortal(final World world, final PortalWorld portalWorld, final UUID sampleUuid, final String portalBlockType) {
        final PortalSpawn portalSpawn = portalWorld.getPortalType().getPortalSpawn();
        return getSpawnTransform(world, sampleUuid, portalSpawn).thenCompose(spawnTransform -> {
            final Vector3d spawnPoint = spawnTransform.getPosition();
            return world.getChunkAsync(ChunkUtil.indexChunkFromBlock((int)spawnPoint.x, (int)spawnPoint.z)).thenAccept(chunk -> {
                for (int dy = 0; dy < 3; ++dy) {
                    for (int dx = -1; dx <= 1; ++dx) {
                        for (int dz = -1; dz <= 1; ++dz) {
                            chunk.setBlock((int)spawnPoint.x + dx, (int)spawnPoint.y + dy, (int)spawnPoint.z + dz, BlockType.EMPTY);
                        }
                    }
                }
                chunk.setBlock((int)spawnPoint.x, (int)spawnPoint.y, (int)spawnPoint.z, portalBlockType);
                portalWorld.setSpawnPoint(spawnTransform);
                world.getWorldConfig().setSpawnProvider(new IndividualSpawnProvider(spawnTransform));
                HytaleLogger.getLogger().at(Level.INFO).log("Spawned return portal for " + world.getName() + " at " + (int)spawnPoint.x + ", " + (int)spawnPoint.y + ", " + (int)spawnPoint.z);
            }).thenApply(nothing -> world);
        });
    }
    
    private static CompletableFuture<Transform> getSpawnTransform(final World world, final UUID sampleUuid, @Nullable final PortalSpawn portalSpawn) {
        final ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider();
        if (spawnProvider == null) {
            return CompletableFuture.completedFuture((Transform)null);
        }
        final Transform worldSpawnPoint = spawnProvider.getSpawnPoint(world, sampleUuid);
        if (!PortalDeviceSummonPage.DEFAULT_WORLDGEN_SPAWN.equals(worldSpawnPoint) || portalSpawn == null) {
            final Transform uppedSpawnPoint = worldSpawnPoint.clone();
            uppedSpawnPoint.getPosition().add(0.0, 0.5, 0.0);
            return CompletableFuture.completedFuture(uppedSpawnPoint);
        }
        return CompletableFuture.supplyAsync(() -> {
            final Transform computedSpawn = PortalSpawnFinder.computeSpawnTransform(world, portalSpawn);
            return (computedSpawn == null) ? worldSpawnPoint : computedSpawn;
        }, world);
    }
    
    private State computeState(@Nonnull final Player player, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        if (!this.blockRef.isValid()) {
            return Error.INVALID_BLOCK;
        }
        final int activeFragments = PortalsPlugin.getInstance().countActiveFragments();
        if (activeFragments >= 4) {
            return Error.MAX_ACTIVE_PORTALS;
        }
        final Store<ChunkStore> chunkStore = this.blockRef.getStore();
        final BlockModule.BlockStateInfo blockStateInfo = chunkStore.getComponent(this.blockRef, BlockModule.BlockStateInfo.getComponentType());
        final PortalDevice portalDevice = chunkStore.getComponent(this.blockRef, PortalDevice.getComponentType());
        if (blockStateInfo == null || portalDevice == null) {
            return Error.INVALID_BLOCK;
        }
        final Ref<ChunkStore> chunkRef = blockStateInfo.getChunkRef();
        if (chunkRef == null || !chunkRef.isValid()) {
            return Error.INVALID_BLOCK;
        }
        final WorldChunk worldChunk = chunkStore.getComponent(chunkRef, WorldChunk.getComponentType());
        if (worldChunk == null) {
            return Error.INVALID_BLOCK;
        }
        final World existingDestinationWorld = portalDevice.getDestinationWorld();
        if (existingDestinationWorld != null) {
            return Error.INVALID_DESTINATION;
        }
        if (this.offeredItemStack == null) {
            return Error.NOTHING_OFFERED;
        }
        final ItemStack inHand = player.getInventory().getItemInHand();
        if (!this.offeredItemStack.equals(inHand)) {
            return Error.OFFERED_IS_NOT_HELD;
        }
        final Item offeredItem = this.offeredItemStack.getItem();
        final PortalKey portalKey = offeredItem.getPortalKey();
        if (portalKey == null) {
            return Error.NOT_A_PORTAL_KEY;
        }
        final String portalTypeId = portalKey.getPortalTypeId();
        final PortalType portalType = PortalType.getAssetMap().getAsset(portalTypeId);
        if (portalType == null) {
            return new PortalTypeNotFound(portalTypeId);
        }
        final String instanceId = portalType.getInstanceId();
        InstancesPlugin.get();
        final boolean instanceExists = InstancesPlugin.doesInstanceAssetExist(instanceId);
        if (!instanceExists) {
            return new InstanceKeyNotFound(instanceId);
        }
        final PortalWorld insidePortalWorld = componentAccessor.getResource(PortalWorld.getResourceType());
        if (insidePortalWorld.exists()) {
            return Error.PORTAL_INSIDE_PORTAL;
        }
        final String gameplayConfigId = portalType.getGameplayConfigId();
        final GameplayConfig gameplayConfig = GameplayConfig.getAssetMap().getAsset(gameplayConfigId);
        final PortalGameplayConfig portalGameplayConfig = (gameplayConfig == null) ? null : gameplayConfig.getPluginConfig().get((Class<? extends PortalGameplayConfig>)PortalGameplayConfig.class);
        if (portalGameplayConfig == null) {
            return Error.BOTCHED_GAMEPLAY_CONFIG;
        }
        return new CanSpawnPortal(portalKey, portalType, worldChunk, blockStateInfo, portalDevice, portalGameplayConfig);
    }
    
    private static void decrementItemInHand(final Inventory inventory, final int amount) {
        if (inventory.usingToolsItem()) {
            return;
        }
        final byte hotbarSlot = inventory.getActiveHotbarSlot();
        if (hotbarSlot == -1) {
            return;
        }
        final ItemContainer hotbar = inventory.getHotbar();
        final ItemStack inHand = hotbar.getItemStack(hotbarSlot);
        if (inHand == null) {
            return;
        }
        hotbar.removeItemStackFromSlot(hotbarSlot, inHand, amount, false, true);
    }
    
    static {
        DEFAULT_WORLDGEN_SPAWN = new Transform(0.0, 140.0, 0.0);
    }
    
    record CanSpawnPortal(PortalKey portalKey, PortalType portalType, WorldChunk worldChunk, BlockModule.BlockStateInfo blockState, PortalDevice portalDevice, PortalGameplayConfig portalGameplayConfig) implements State {}
    
    record PortalTypeNotFound(String portalTypeId) implements State {}
    
    record InstanceKeyNotFound(String instanceId) implements State {}
    
    private enum Error implements State
    {
        NOTHING_OFFERED, 
        OFFERED_IS_NOT_HELD, 
        NOT_A_PORTAL_KEY, 
        INVALID_BLOCK, 
        INVALID_DESTINATION, 
        PORTAL_INSIDE_PORTAL, 
        BOTCHED_GAMEPLAY_CONFIG, 
        MAX_ACTIVE_PORTALS;
    }
    
    protected static class Data
    {
        private static final String KEY_ACTION = "Action";
        public static final BuilderCodec<Data> CODEC;
        private String action;
        
        static {
            // 
            // This method could not be decompiled.
            // 
            // Original Bytecode:
            // 
            //     2: invokedynamic   BootstrapMethod #0, get:()Ljava/util/function/Supplier;
            //     7: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    10: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    13: dup            
            //    14: ldc             "Action"
            //    16: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
            //    19: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    22: invokedynamic   BootstrapMethod #1, accept:()Ljava/util/function/BiConsumer;
            //    27: invokedynamic   BootstrapMethod #2, apply:()Ljava/util/function/Function;
            //    32: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //    35: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //    38: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    41: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //    44: putstatic       com/hypixel/hytale/builtin/portals/ui/PortalDeviceSummonPage$Data.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //    47: return         
            // 
            // The error that occurred was:
            // 
            // java.lang.UnsupportedOperationException: The requested operation is not supported.
            //     at com.strobel.util.ContractUtils.unsupported(ContractUtils.java:27)
            //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:284)
            //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:279)
            //     at com.strobel.assembler.metadata.TypeReference.makeGenericType(TypeReference.java:154)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:225)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
            //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:211)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
            //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitMethod(TypeSubstitutionVisitor.java:314)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2611)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2483)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2483)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:684)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypesForVariables(TypeAnalysis.java:593)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:405)
            //     at com.strobel.decompiler.ast.TypeAnalysis.run(TypeAnalysis.java:95)
            //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:109)
            //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
            //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:206)
            //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:93)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:868)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:761)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:638)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:662)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:162)
            //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:137)
            //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
            //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
            //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
            //     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
            //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
            // 
            throw new IllegalStateException("An error occurred while decompiling this method.");
        }
    }
    
    private sealed interface State permits CanSpawnPortal, Error, InstanceKeyNotFound, PortalTypeNotFound
    {
    }
}
