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

package com.hypixel.hytale.server.core.modules.singleplayer;

import com.hypixel.hytale.server.core.modules.interaction.InteractionModule;
import com.hypixel.hytale.server.core.auth.PlayerAuthentication;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.UUID;
import com.hypixel.hytale.server.core.ShutdownReason;
import com.hypixel.hytale.server.core.util.ProcessUtil;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.event.IEventDispatcher;
import java.net.SocketAddress;
import java.util.Iterator;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.serveraccess.RequestServerAccess;
import com.hypixel.hytale.server.core.universe.Universe;
import io.netty.channel.Channel;
import com.hypixel.hytale.server.core.io.ServerManager;
import java.util.concurrent.TimeUnit;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.HytaleServer;
import java.util.logging.Level;
import com.hypixel.hytale.server.core.Options;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.modules.singleplayer.commands.PlayCommand;
import com.hypixel.hytale.server.core.modules.accesscontrol.provider.AccessProvider;
import com.hypixel.hytale.server.core.modules.accesscontrol.provider.ClientDelegatingProvider;
import com.hypixel.hytale.server.core.modules.accesscontrol.AccessControlModule;
import com.hypixel.hytale.server.core.Constants;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import java.net.InetSocketAddress;
import java.util.List;
import com.hypixel.hytale.protocol.packets.serveraccess.Access;
import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class SingleplayerModule extends JavaPlugin
{
    public static final PluginManifest MANIFEST;
    private static SingleplayerModule instance;
    private Access access;
    private Access requestedAccess;
    private List<InetSocketAddress> publicAddresses;
    
    public static SingleplayerModule get() {
        return SingleplayerModule.instance;
    }
    
    public SingleplayerModule(@Nonnull final JavaPluginInit init) {
        super(init);
        this.publicAddresses = new CopyOnWriteArrayList<InetSocketAddress>();
        SingleplayerModule.instance = this;
    }
    
    @Override
    protected void setup() {
        if (Constants.SINGLEPLAYER) {
            AccessControlModule.get().registerAccessProvider(new ClientDelegatingProvider());
        }
        this.getCommandRegistry().registerCommand(new PlayCommand(this));
    }
    
    @Override
    protected void start() {
        final Integer pid = Options.getOptionSet().valueOf(Options.CLIENT_PID);
        if (pid != null) {
            this.getLogger().at(Level.INFO).log("Client PID: %d", pid);
            HytaleServer.SCHEDULED_EXECUTOR.scheduleWithFixedDelay(() -> {
                try {
                    checkClientPid();
                }
                catch (final Exception e) {
                    this.getLogger().at(Level.SEVERE).withCause(e).log("Failed to check client PID!");
                }
            }, 60L, 60L, TimeUnit.SECONDS);
        }
    }
    
    public Access getAccess() {
        return this.access;
    }
    
    public Access getRequestedAccess() {
        return this.requestedAccess;
    }
    
    public void requestServerAccess(final Access access) {
        if (!Constants.SINGLEPLAYER) {
            throw new IllegalArgumentException("Server access can only be modified in singleplayer!");
        }
        final ServerManager serverManager = ServerManager.get();
        short externalPort = 0;
        if (access != Access.Private) {
            if (!serverManager.bind(new InetSocketAddress(0))) {
                this.requestServerAccess(Access.Private);
                return;
            }
            try {
                final InetSocketAddress boundAddress = serverManager.getNonLoopbackAddress();
                if (boundAddress != null) {
                    externalPort = (short)boundAddress.getPort();
                }
            }
            catch (final Exception e) {
                this.getLogger().at(Level.WARNING).withCause(e).log("Failed to get bound port");
            }
        }
        else {
            for (final Channel channel : serverManager.getListeners()) {
                final SocketAddress localAddress = channel.localAddress();
                if (localAddress instanceof final InetSocketAddress inetSocketAddress) {
                    if (!inetSocketAddress.getAddress().isAnyLocalAddress()) {
                        continue;
                    }
                    serverManager.unbind(channel);
                }
            }
        }
        final IEventDispatcher<SingleplayerRequestAccessEvent, SingleplayerRequestAccessEvent> dispatchFor = HytaleServer.get().getEventBus().dispatchFor((Class<? super SingleplayerRequestAccessEvent>)SingleplayerRequestAccessEvent.class);
        if (dispatchFor.hasListener()) {
            dispatchFor.dispatch(new SingleplayerRequestAccessEvent(access));
        }
        Universe.get().getPlayer(getUuid()).getPacketHandler().writeNoCache(new RequestServerAccess(access, externalPort));
        this.requestedAccess = access;
    }
    
    public void setPublicAddresses(final List<InetSocketAddress> publicAddresses) {
        this.publicAddresses = publicAddresses;
    }
    
    public void updateAccess(@Nonnull final Access access) {
        if (this.requestedAccess != access) {
            Universe.get().sendMessage(Message.translation("server.modules.sp.requestAccessDifferent").param("requestedAccess", this.requestedAccess.toString()).param("access", access.toString()));
        }
        Universe.get().sendMessage(Message.translation("server.modules.sp.serverAccessUpdated").param("access", access.toString()));
        this.access = access;
    }
    
    public static void checkClientPid() {
        if (!ProcessUtil.isProcessRunning(Options.getOptionSet().valueOf(Options.CLIENT_PID))) {
            HytaleServer.get().shutdownServer(ShutdownReason.CLIENT_GONE);
        }
    }
    
    public static UUID getUuid() {
        return Options.getOptionSet().valueOf(Options.OWNER_UUID);
    }
    
    public static String getUsername() {
        return Options.getOptionSet().valueOf(Options.OWNER_NAME);
    }
    
    public static boolean isOwner(@Nonnull final PlayerRef player) {
        return isOwner(player.getPacketHandler().getAuth(), player.getUuid());
    }
    
    public static boolean isOwner(final PlayerAuthentication playerAuth, @Nonnull final UUID playerUuid) {
        return playerUuid.equals(getUuid());
    }
    
    static {
        MANIFEST = PluginManifest.corePlugin(SingleplayerModule.class).depends(AccessControlModule.class).optDepends(InteractionModule.class).build();
    }
}
