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

package com.hypixel.hytale.builtin.creativehub;

import java.io.IOException;
import java.util.concurrent.CompletionStage;
import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.builtin.instances.config.WorldReturnPoint;
import com.hypixel.hytale.builtin.instances.config.InstanceEntityConfig;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.event.events.player.AddPlayerToWorldEvent;
import com.hypixel.hytale.server.core.universe.world.events.RemoveWorldEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.builtin.creativehub.interactions.HubPortalInteraction;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.builtin.creativehub.command.HubCommand;
import java.util.stream.Stream;
import java.nio.file.Path;
import java.util.function.Function;
import com.hypixel.hytale.common.util.FormatUtil;
import com.hypixel.hytale.sneakythrow.SneakyThrow;
import java.nio.file.CopyOption;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.LinkOption;
import java.nio.file.Files;
import com.hypixel.hytale.server.core.util.io.FileUtil;
import com.hypixel.hytale.builtin.instances.config.InstanceWorldConfig;
import com.hypixel.hytale.server.core.universe.world.WorldConfig;
import com.hypixel.hytale.server.core.universe.Universe;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
import java.util.logging.Level;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.builtin.instances.InstancesPlugin;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubWorldConfig;
import java.util.concurrent.ConcurrentHashMap;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubEntityConfig;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.World;
import java.util.UUID;
import java.util.Map;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class CreativeHubPlugin extends JavaPlugin
{
    @Nonnull
    private static final Message MESSAGE_HUB_RETURN_HINT;
    private static CreativeHubPlugin instance;
    private final Map<UUID, World> activeHubInstances;
    private ComponentType<EntityStore, CreativeHubEntityConfig> creativeHubEntityConfigComponentType;
    
    public static CreativeHubPlugin get() {
        return CreativeHubPlugin.instance;
    }
    
    public CreativeHubPlugin(@Nonnull final JavaPluginInit init) {
        super(init);
        this.activeHubInstances = new ConcurrentHashMap<UUID, World>();
        CreativeHubPlugin.instance = this;
    }
    
    @Nonnull
    public World getOrSpawnHubInstance(@Nonnull final World parentWorld, @Nonnull final CreativeHubWorldConfig hubConfig, @Nonnull final Transform returnPoint) {
        final UUID parentUuid = parentWorld.getWorldConfig().getUuid();
        return this.activeHubInstances.compute(parentUuid, (uuid, existingInstance) -> {
            if (existingInstance != null && existingInstance.isAlive()) {
                return existingInstance;
            }
            else {
                try {
                    return InstancesPlugin.get().spawnInstance(hubConfig.getStartupInstance(), parentWorld, returnPoint).join();
                }
                catch (final Exception e) {
                    this.getLogger().at(Level.SEVERE).withCause(e).log("Failed to spawn hub instance");
                    throw new RuntimeException("Failed to spawn hub instance", e);
                }
            }
        });
    }
    
    @Nullable
    public World getActiveHubInstance(@Nonnull final World parentWorld) {
        final World hubInstance = this.activeHubInstances.get(parentWorld.getWorldConfig().getUuid());
        if (hubInstance != null && hubInstance.isAlive()) {
            return hubInstance;
        }
        return null;
    }
    
    public void clearHubInstance(@Nonnull final UUID parentWorldUuid) {
        this.activeHubInstances.remove(parentWorldUuid);
    }
    
    @Nonnull
    public CompletableFuture<World> spawnPermanentWorldFromTemplate(@Nonnull final String instanceAssetName, @Nonnull final String permanentWorldName) {
        final Universe universe = Universe.get();
        final World existingWorld = universe.getWorld(permanentWorldName);
        if (existingWorld != null) {
            return CompletableFuture.completedFuture(existingWorld);
        }
        if (universe.isWorldLoadable(permanentWorldName)) {
            return universe.loadWorld(permanentWorldName);
        }
        final Path assetPath = InstancesPlugin.getInstanceAssetPath(instanceAssetName);
        final Path worldPath = universe.getPath().resolve("worlds").resolve(permanentWorldName);
        return WorldConfig.load(assetPath.resolve("instance.bson")).thenApplyAsync((Function<? super WorldConfig, ?>)SneakyThrow.sneakyFunction(config -> {
            config.setUuid(UUID.randomUUID());
            config.setDeleteOnRemove(false);
            config.setDisplayName(WorldConfig.formatDisplayName(instanceAssetName));
            config.getPluginConfig().remove(InstanceWorldConfig.class);
            config.markChanged();
            final long start = System.nanoTime();
            this.getLogger().at(Level.INFO).log("Copying instance template %s to permanent world %s", instanceAssetName, permanentWorldName);
            try (final Stream<Path> files = Files.walk(assetPath, FileUtil.DEFAULT_WALK_TREE_OPTIONS_ARRAY)) {
                files.forEach(SneakyThrow.sneakyConsumer(filePath -> {
                    final Path rel = assetPath.relativize(filePath);
                    final Path toPath = worldPath.resolve(rel.toString());
                    if (Files.isDirectory(filePath, new LinkOption[0])) {
                        Files.createDirectories(toPath, (FileAttribute<?>[])new FileAttribute[0]);
                        return;
                    }
                    else {
                        if (Files.isRegularFile(filePath, new LinkOption[0])) {
                            Files.copy(filePath, toPath, new CopyOption[0]);
                        }
                        return;
                    }
                }));
            }
            this.getLogger().at(Level.INFO).log("Completed copying instance template %s to permanent world %s in %s", instanceAssetName, permanentWorldName, FormatUtil.nanosToString(System.nanoTime() - start));
            return config;
        })).thenCompose(config -> universe.makeWorld(permanentWorldName, worldPath, config));
    }
    
    @Nonnull
    public ComponentType<EntityStore, CreativeHubEntityConfig> getCreativeHubEntityConfigComponentType() {
        return this.creativeHubEntityConfigComponentType;
    }
    
    @Override
    protected void setup() {
        this.getCommandRegistry().registerCommand(new HubCommand());
        this.getCodecRegistry(Interaction.CODEC).register("HubPortal", HubPortalInteraction.class, HubPortalInteraction.CODEC);
        this.getCodecRegistry(WorldConfig.PLUGIN_CODEC).register(CreativeHubWorldConfig.class, "CreativeHub", CreativeHubWorldConfig.CODEC);
        this.creativeHubEntityConfigComponentType = this.getEntityStoreRegistry().registerComponent(CreativeHubEntityConfig.class, "CreativeHub", CreativeHubEntityConfig.CODEC);
        this.getEventRegistry().registerGlobal(PlayerConnectEvent.class, CreativeHubPlugin::onPlayerConnect);
        this.getEventRegistry().registerGlobal(RemoveWorldEvent.class, CreativeHubPlugin::onWorldRemove);
        this.getEventRegistry().registerGlobal(AddPlayerToWorldEvent.class, CreativeHubPlugin::onPlayerAddToWorld);
    }
    
    private static void onWorldRemove(@Nonnull final RemoveWorldEvent event) {
        final World world = event.getWorld();
        final UUID worldUuid = world.getWorldConfig().getUuid();
        get().activeHubInstances.entrySet().removeIf(entry -> {
            final World hubInstance = entry.getValue();
            return hubInstance != null && hubInstance.getWorldConfig().getUuid().equals(worldUuid);
        });
    }
    
    private static void onPlayerConnect(@Nonnull final PlayerConnectEvent event) {
        World targetWorld = event.getWorld();
        final Holder<EntityStore> holder = event.getHolder();
        final CreativeHubEntityConfig existingHubConfig = CreativeHubEntityConfig.get(holder);
        if (existingHubConfig != null && existingHubConfig.getParentHubWorldUuid() != null) {
            final World parentWorld = Universe.get().getWorld(existingHubConfig.getParentHubWorldUuid());
            if (parentWorld != null) {
                final CreativeHubWorldConfig parentHubConfig = CreativeHubWorldConfig.get(parentWorld.getWorldConfig());
                if (parentHubConfig != null && parentHubConfig.getStartupInstance() != null && targetWorld == null) {
                    event.setWorld(parentWorld);
                    targetWorld = parentWorld;
                    holder.removeComponent(TransformComponent.getComponentType());
                }
            }
        }
        if (targetWorld == null) {
            return;
        }
        final WorldConfig worldConfig = targetWorld.getWorldConfig();
        final CreativeHubWorldConfig hubConfig = CreativeHubWorldConfig.get(worldConfig);
        if (hubConfig == null || hubConfig.getStartupInstance() == null) {
            return;
        }
        final PlayerRef playerRef = event.getPlayerRef();
        final ISpawnProvider spawnProvider = worldConfig.getSpawnProvider();
        final Transform returnPoint = (spawnProvider != null) ? spawnProvider.getSpawnPoint(targetWorld, playerRef.getUuid()) : new Transform();
        try {
            final World hubInstance = get().getOrSpawnHubInstance(targetWorld, hubConfig, returnPoint);
            final InstanceEntityConfig instanceConfig = InstanceEntityConfig.ensureAndGet(holder);
            instanceConfig.setReturnPoint(new WorldReturnPoint(targetWorld.getWorldConfig().getUuid(), returnPoint, false));
            final CreativeHubEntityConfig hubEntityConfig = CreativeHubEntityConfig.ensureAndGet(holder);
            hubEntityConfig.setParentHubWorldUuid(targetWorld.getWorldConfig().getUuid());
            event.setWorld(hubInstance);
        }
        catch (final Exception e) {
            get().getLogger().at(Level.SEVERE).withCause(e).log("Failed to get/spawn hub instance for player %s, falling back to default world", playerRef.getUuid());
        }
    }
    
    private static void onPlayerAddToWorld(@Nonnull final AddPlayerToWorldEvent event) {
        final Holder<EntityStore> holder = event.getHolder();
        final World world = event.getWorld();
        final CreativeHubEntityConfig hubEntityConfig = holder.getComponent(CreativeHubEntityConfig.getComponentType());
        if (hubEntityConfig == null || hubEntityConfig.getParentHubWorldUuid() == null) {
            return;
        }
        final World parentWorld = Universe.get().getWorld(hubEntityConfig.getParentHubWorldUuid());
        if (parentWorld == null) {
            return;
        }
        final World hubInstance = get().getActiveHubInstance(parentWorld);
        if (hubInstance != null && world.equals(hubInstance)) {
            return;
        }
        final PlayerRef playerRef = holder.getComponent(PlayerRef.getComponentType());
        if (playerRef != null) {
            world.execute(() -> playerRef.sendMessage(CreativeHubPlugin.MESSAGE_HUB_RETURN_HINT));
        }
    }
    
    static {
        MESSAGE_HUB_RETURN_HINT = Message.translation("server.creativehub.portal.returnHint");
    }
}
