package net.minecraft.client.server;

import org.apache.logging.log4j.LogManager;
import java.util.List;
import com.mojang.authlib.GameProfile;
import java.util.ArrayList;
import com.google.common.collect.Lists;
import java.util.Iterator;
import java.io.IOException;
import net.minecraft.server.level.ServerPlayer;
import java.net.InetAddress;
import net.minecraft.world.level.GameType;
import net.minecraft.world.Snooper;
import net.minecraft.client.ClientBrandRetriever;
import java.util.Optional;
import net.minecraft.CrashReport;
import java.io.File;
import net.minecraft.util.profiling.ProfilerFiller;
import java.util.function.BooleanSupplier;
import net.minecraft.util.Crypt;
import net.minecraft.SharedConstants;
import net.minecraft.server.players.PlayerList;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.players.GameProfileCache;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import net.minecraft.world.level.storage.WorldData;
import net.minecraft.server.ServerResources;
import net.minecraft.server.packs.repository.Pack;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.core.RegistryAccess;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import org.apache.logging.log4j.Logger;
import net.minecraft.server.MinecraftServer;

public class IntegratedServer extends MinecraftServer {
    private static final Logger LOGGER;
    private final Minecraft minecraft;
    private boolean paused;
    private int publishedPort;
    private LanServerPinger lanPinger;
    private UUID uuid;
    
    public IntegratedServer(final Thread thread, final Minecraft dlx, final RegistryAccess.RegistryHolder a, final LevelStorageSource.LevelStorageAccess a, final PackRepository<Pack> aar, final ServerResources uw, final WorldData dal, final MinecraftSessionService minecraftSessionService, final GameProfileRepository gameProfileRepository, final GameProfileCache abl, final ChunkProgressListenerFactory zn) {
        super(thread, a, a, dal, aar, dlx.getProxy(), dlx.getFixerUpper(), uw, minecraftSessionService, gameProfileRepository, abl, zn);
        this.publishedPort = -1;
        this.setSingleplayerName(dlx.getUser().getName());
        this.setDemo(dlx.isDemo());
        this.setMaxBuildHeight(256);
        this.setPlayerList(new IntegratedPlayerList(this, this.registryHolder, this.playerDataStorage));
        this.minecraft = dlx;
    }
    
    public boolean initServer() {
        IntegratedServer.LOGGER.info("Starting integrated minecraft server version " + SharedConstants.getCurrentVersion().getName());
        this.setUsesAuthentication(true);
        this.setPvpAllowed(true);
        this.setFlightAllowed(true);
        IntegratedServer.LOGGER.info("Generating keypair");
        this.setKeyPair(Crypt.generateKeyPair());
        this.loadLevel();
        this.setMotd(this.getSingleplayerName() + " - " + this.getWorldData().getLevelName());
        return true;
    }
    
    public void tickServer(final BooleanSupplier booleanSupplier) {
        final boolean boolean3 = this.paused;
        this.paused = (Minecraft.getInstance().getConnection() != null && Minecraft.getInstance().isPaused());
        final ProfilerFiller ami4 = this.getProfiler();
        if (!boolean3 && this.paused) {
            ami4.push("autoSave");
            IntegratedServer.LOGGER.info("Saving and pausing game...");
            this.getPlayerList().saveAll();
            this.saveAllChunks(false, false, false);
            ami4.pop();
        }
        if (this.paused) {
            return;
        }
        super.tickServer(booleanSupplier);
        final int integer5 = Math.max(2, this.minecraft.options.renderDistance - 1);
        if (integer5 != this.getPlayerList().getViewDistance()) {
            IntegratedServer.LOGGER.info("Changing view distance to {}, from {}", integer5, this.getPlayerList().getViewDistance());
            this.getPlayerList().setViewDistance(integer5);
        }
    }
    
    @Override
    public boolean shouldRconBroadcast() {
        return true;
    }
    
    @Override
    public boolean shouldInformAdmins() {
        return true;
    }
    
    @Override
    public File getServerDirectory() {
        return this.minecraft.gameDirectory;
    }
    
    @Override
    public boolean isDedicatedServer() {
        return false;
    }
    
    @Override
    public boolean isEpollEnabled() {
        return false;
    }
    
    public void onServerCrash(final CrashReport j) {
        this.minecraft.delayCrash(j);
    }
    
    @Override
    public CrashReport fillReport(CrashReport j) {
        j = super.fillReport(j);
        j.getSystemDetails().setDetail("Type", "Integrated Server (map_client.txt)");
        j.getSystemDetails().setDetail("Is Modded", () -> this.getModdedStatus().orElse("Probably not. Jar signature remains and both client + server brands are untouched."));
        return j;
    }
    
    @Override
    public Optional<String> getModdedStatus() {
        String string2 = ClientBrandRetriever.getClientModName();
        if (!string2.equals("vanilla")) {
            return Optional.<String>of("Definitely; Client brand changed to '" + string2 + "'");
        }
        string2 = this.getServerModName();
        if (!"vanilla".equals(string2)) {
            return Optional.<String>of("Definitely; Server brand changed to '" + string2 + "'");
        }
        if (Minecraft.class.getSigners() == null) {
            return Optional.<String>of("Very likely; Jar signature invalidated");
        }
        return Optional.<String>empty();
    }
    
    @Override
    public void populateSnooper(final Snooper ano) {
        super.populateSnooper(ano);
        ano.setDynamicData("snooper_partner", this.minecraft.getSnooper().getToken());
    }
    
    @Override
    public boolean publishServer(final GameType bpy, final boolean boolean2, final int integer) {
        try {
            this.getConnection().startTcpServerListener(null, integer);
            IntegratedServer.LOGGER.info("Started serving on {}", integer);
            this.publishedPort = integer;
            (this.lanPinger = new LanServerPinger(this.getMotd(), integer + "")).start();
            this.getPlayerList().setOverrideGameMode(bpy);
            this.getPlayerList().setAllowCheatsForAllPlayers(boolean2);
            final int integer2 = this.getProfilePermissions(this.minecraft.player.getGameProfile());
            this.minecraft.player.setPermissionLevel(integer2);
            for (final ServerPlayer ze7 : this.getPlayerList().getPlayers()) {
                this.getCommands().sendCommands(ze7);
            }
            return true;
        }
        catch (IOException ex) {
            return false;
        }
    }
    
    public void stopServer() {
        super.stopServer();
        if (this.lanPinger != null) {
            this.lanPinger.interrupt();
            this.lanPinger = null;
        }
    }
    
    @Override
    public void halt(final boolean boolean1) {
        final ArrayList<Object> list2;
        final Iterator<ServerPlayer> iterator;
        ServerPlayer ze4;
        this.executeBlocking(() -> {
            list2 = Lists.newArrayList(this.getPlayerList().getPlayers());
            list2.iterator();
            while (iterator.hasNext()) {
                ze4 = iterator.next();
                if (!ze4.getUUID().equals(this.uuid)) {
                    this.getPlayerList().remove(ze4);
                }
            }
            return;
        });
        super.halt(boolean1);
        if (this.lanPinger != null) {
            this.lanPinger.interrupt();
            this.lanPinger = null;
        }
    }
    
    @Override
    public boolean isPublished() {
        return this.publishedPort > -1;
    }
    
    @Override
    public int getPort() {
        return this.publishedPort;
    }
    
    @Override
    public void setDefaultGameType(final GameType bpy) {
        super.setDefaultGameType(bpy);
        this.getPlayerList().setOverrideGameMode(bpy);
    }
    
    @Override
    public boolean isCommandBlockEnabled() {
        return true;
    }
    
    @Override
    public int getOperatorUserPermissionLevel() {
        return 2;
    }
    
    @Override
    public int getFunctionCompilationLevel() {
        return 2;
    }
    
    public void setUUID(final UUID uUID) {
        this.uuid = uUID;
    }
    
    @Override
    public boolean isSingleplayerOwner(final GameProfile gameProfile) {
        return gameProfile.getName().equalsIgnoreCase(this.getSingleplayerName());
    }
    
    @Override
    public int getScaledTrackingDistance(final int integer) {
        return (int)(this.minecraft.options.entityDistanceScaling * integer);
    }
    
    @Override
    public boolean forceSynchronousWrites() {
        return this.minecraft.options.syncWrites;
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
