package net.minecraft;

import java.net.URISyntaxException;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.io.IOException;
import java.security.PrivilegedActionException;
import org.apache.commons.io.IOUtils;
import java.security.AccessController;
import java.net.URL;
import org.apache.logging.log4j.LogManager;
import java.util.UUID;
import com.mojang.datafixers.Dynamic;
import com.mojang.datafixers.DataFixUtils;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.Hash;
import java.util.function.Consumer;
import java.util.function.Supplier;
import com.google.common.collect.Iterators;
import java.util.Iterator;
import java.util.List;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ManagementFactory;
import java.util.stream.Stream;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Executor;
import net.minecraft.server.Bootstrap;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import com.google.common.util.concurrent.MoreExecutors;
import net.minecraft.util.Mth;
import java.time.Instant;
import javax.annotation.Nullable;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.state.properties.Property;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Map;
import java.util.stream.Collector;
import org.apache.logging.log4j.Logger;
import java.util.function.LongSupplier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

public class Util {
    private static final AtomicInteger WORKER_COUNT;
    private static final ExecutorService BACKGROUND_EXECUTOR;
    public static LongSupplier timeSource;
    private static final Logger LOGGER;
    
    public static <K, V> Collector<Map.Entry<? extends K, ? extends V>, ?, Map<K, V>> toMap() {
        return Collectors.<Map.Entry<? extends K, ? extends V>, K, V>toMap(Map.Entry::getKey, Map.Entry::getValue);
    }
    
    public static <T extends Comparable<T>> String getPropertyName(final Property<T> bzm, final Object object) {
        return bzm.getName((T)object);
    }
    
    public static String makeDescriptionId(final String string, @Nullable final ResourceLocation sm) {
        if (sm == null) {
            return string + ".unregistered_sadface";
        }
        return string + '.' + sm.getNamespace() + '.' + sm.getPath().replace('/', '.');
    }
    
    public static long getMillis() {
        return getNanos() / 1000000L;
    }
    
    public static long getNanos() {
        return Util.timeSource.getAsLong();
    }
    
    public static long getEpochMillis() {
        return Instant.now().toEpochMilli();
    }
    
    private static ExecutorService makeBackgroundExecutor() {
        final int integer1 = Mth.clamp(Runtime.getRuntime().availableProcessors() - 1, 1, 7);
        ExecutorService executorService2;
        if (integer1 <= 0) {
            executorService2 = MoreExecutors.newDirectExecutorService();
        }
        else {
            final ForkJoinWorkerThread forkJoinWorkerThread2;
            executorService2 = new ForkJoinPool(integer1, forkJoinPool -> {
                forkJoinWorkerThread2 = new ForkJoinWorkerThread(forkJoinPool) {
                    @Override
                    protected void onTermination(final Throwable throwable) {
                        if (throwable != null) {
                            Util.LOGGER.warn("{} died", this.getName(), throwable);
                        }
                        else {
                            Util.LOGGER.debug("{} shutdown", this.getName());
                        }
                        super.onTermination(throwable);
                    }
                };
                forkJoinWorkerThread2.setName("Server-Worker-" + Util.WORKER_COUNT.getAndIncrement());
                return forkJoinWorkerThread2;
            }, (thread, throwable) -> {
                Util.<ReportedException>pauseInIde(throwable);
                if (throwable instanceof CompletionException) {
                    throwable = (ReportedException)throwable.getCause();
                }
                if (throwable instanceof ReportedException) {
                    Bootstrap.realStdoutPrintln(throwable.getReport().getFriendlyReport());
                    System.exit(-1);
                }
                Util.LOGGER.error(String.format("Caught exception in thread %s", thread), (Throwable)throwable);
                return;
            }, true);
        }
        return executorService2;
    }
    
    public static Executor backgroundExecutor() {
        return Util.BACKGROUND_EXECUTOR;
    }
    
    public static void shutdownBackgroundExecutor() {
        Util.BACKGROUND_EXECUTOR.shutdown();
        boolean boolean1;
        try {
            boolean1 = Util.BACKGROUND_EXECUTOR.awaitTermination(3L, TimeUnit.SECONDS);
        }
        catch (InterruptedException interruptedException2) {
            boolean1 = false;
        }
        if (!boolean1) {
            Util.BACKGROUND_EXECUTOR.shutdownNow();
        }
    }
    
    public static <T> CompletableFuture<T> failedFuture(final Throwable throwable) {
        final CompletableFuture<T> completableFuture2 = new CompletableFuture<T>();
        completableFuture2.completeExceptionally(throwable);
        return completableFuture2;
    }
    
    public static void throwAsRuntime(final Throwable throwable) {
        throw (throwable instanceof RuntimeException) ? throwable : new RuntimeException(throwable);
    }
    
    public static OS getPlatform() {
        final String string1 = System.getProperty("os.name").toLowerCase(Locale.ROOT);
        if (string1.contains("win")) {
            return OS.WINDOWS;
        }
        if (string1.contains("mac")) {
            return OS.OSX;
        }
        if (string1.contains("solaris")) {
            return OS.SOLARIS;
        }
        if (string1.contains("sunos")) {
            return OS.SOLARIS;
        }
        if (string1.contains("linux")) {
            return OS.LINUX;
        }
        if (string1.contains("unix")) {
            return OS.LINUX;
        }
        return OS.UNKNOWN;
    }
    
    public static Stream<String> getVmArguments() {
        final RuntimeMXBean runtimeMXBean1 = ManagementFactory.getRuntimeMXBean();
        return runtimeMXBean1.getInputArguments().stream().filter(string -> string.startsWith("-X"));
    }
    
    public static <T> T lastOf(final List<T> list) {
        return list.get(list.size() - 1);
    }
    
    public static <T> T findNextInIterable(final Iterable<T> iterable, @Nullable final T object) {
        final Iterator<T> iterator3 = iterable.iterator();
        final T object2 = iterator3.next();
        if (object != null) {
            for (T object3 = object2; object3 != object; object3 = iterator3.next()) {
                if (iterator3.hasNext()) {}
            }
            if (iterator3.hasNext()) {
                return iterator3.next();
            }
        }
        return object2;
    }
    
    public static <T> T findPreviousInIterable(final Iterable<T> iterable, @Nullable final T object) {
        final Iterator<T> iterator3 = iterable.iterator();
        T object2 = null;
        while (iterator3.hasNext()) {
            final T object3 = iterator3.next();
            if (object3 == object) {
                if (object2 == null) {
                    object2 = (iterator3.hasNext() ? Iterators.<T>getLast(iterator3) : object);
                    break;
                }
                break;
            }
            else {
                object2 = object3;
            }
        }
        return object2;
    }
    
    public static <T> T make(final Supplier<T> supplier) {
        return supplier.get();
    }
    
    public static <T> T make(final T object, final Consumer<T> consumer) {
        consumer.accept(object);
        return object;
    }
    
    public static <K> Hash.Strategy<K> identityStrategy() {
        return (Hash.Strategy<K>)IdentityStrategy.INSTANCE;
    }
    
    public static <V> CompletableFuture<List<V>> sequence(final List<? extends CompletableFuture<? extends V>> list) {
        final List<V> list2 = Lists.newArrayListWithCapacity(list.size());
        final CompletableFuture<?>[] arr3 = new CompletableFuture[list.size()];
        final CompletableFuture<Void> completableFuture5 = new CompletableFuture<Void>();
        final List<Object> list3;
        final int integer5;
        final Object o;
        final CompletableFuture completableFuture6;
        final List<Object> list4;
        final int n;
        list.forEach(completableFuture4 -> {
            integer5 = list3.size();
            list3.add(null);
            o[integer5] = completableFuture4.whenComplete((object, throwable) -> {
                if (throwable != null) {
                    completableFuture6.completeExceptionally(throwable);
                }
                else {
                    list4.set(n, object);
                }
            });
            return;
        });
        return CompletableFuture.allOf(arr3).<List<V>>applyToEither(completableFuture5, void2 -> list2);
    }
    
    public static <T> Stream<T> toStream(final Optional<? extends T> optional) {
        return (Stream<T>)DataFixUtils.orElseGet((Optional)optional.map(Stream::of), (Supplier)Stream::empty);
    }
    
    public static <T> Optional<T> ifElse(final Optional<T> optional, final Consumer<T> consumer, final Runnable runnable) {
        if (optional.isPresent()) {
            consumer.accept(optional.get());
        }
        else {
            runnable.run();
        }
        return optional;
    }
    
    public static Runnable name(final Runnable runnable, final Supplier<String> supplier) {
        return runnable;
    }
    
    public static Optional<UUID> readUUID(final String string, final Dynamic<?> dynamic) {
        return dynamic.get(string + "Most").asNumber().<UUID>flatMap(number -> dynamic.get(string + "Least").asNumber().map(number2 -> new UUID(number.longValue(), number2.longValue())));
    }
    
    public static <T> Dynamic<T> writeUUID(final String string, final UUID uUID, final Dynamic<T> dynamic) {
        return (Dynamic<T>)dynamic.set(string + "Most", dynamic.createLong(uUID.getMostSignificantBits())).set(string + "Least", dynamic.createLong(uUID.getLeastSignificantBits()));
    }
    
    public static <T extends Throwable> T pauseInIde(final T throwable) {
        if (SharedConstants.IS_RUNNING_IN_IDE) {
            Util.LOGGER.error("Trying to throw a fatal exception, pausing in IDE", (Throwable)throwable);
            try {
                while (true) {
                    Thread.sleep(1000L);
                    Util.LOGGER.error("paused");
                }
            }
            catch (InterruptedException interruptedException2) {
                return throwable;
            }
        }
        return throwable;
    }
    
    public static String describeError(final Throwable throwable) {
        if (throwable.getCause() != null) {
            return describeError(throwable.getCause());
        }
        if (throwable.getMessage() != null) {
            return throwable.getMessage();
        }
        return throwable.toString();
    }
    
    static {
        WORKER_COUNT = new AtomicInteger(1);
        BACKGROUND_EXECUTOR = makeBackgroundExecutor();
        Util.timeSource = System::nanoTime;
        LOGGER = LogManager.getLogger();
    }
    
    public enum OS {
        LINUX, 
        SOLARIS, 
        WINDOWS {
            @Override
            protected String[] getOpenUrlArguments(final URL uRL) {
                return new String[] { "rundll32", "url.dll,FileProtocolHandler", uRL.toString() };
            }
        }, 
        OSX {
            @Override
            protected String[] getOpenUrlArguments(final URL uRL) {
                return new String[] { "open", uRL.toString() };
            }
        }, 
        UNKNOWN;
        
        public void openUrl(final URL uRL) {
            try {
                final Process process3 = AccessController.<Process>doPrivileged(() -> Runtime.getRuntime().exec(this.getOpenUrlArguments(uRL)));
                for (final String string5 : IOUtils.readLines(process3.getErrorStream())) {
                    Util.LOGGER.error(string5);
                }
                process3.getInputStream().close();
                process3.getErrorStream().close();
                process3.getOutputStream().close();
            }
            catch (PrivilegedActionException | IOException ex2) {
                final Exception ex;
                final Exception exception3 = ex;
                Util.LOGGER.error("Couldn't open url '{}'", uRL, exception3);
            }
        }
        
        public void openUri(final URI uRI) {
            try {
                this.openUrl(uRI.toURL());
            }
            catch (MalformedURLException malformedURLException3) {
                Util.LOGGER.error("Couldn't open uri '{}'", uRI, malformedURLException3);
            }
        }
        
        public void openFile(final File file) {
            try {
                this.openUrl(file.toURI().toURL());
            }
            catch (MalformedURLException malformedURLException3) {
                Util.LOGGER.error("Couldn't open file '{}'", file, malformedURLException3);
            }
        }
        
        protected String[] getOpenUrlArguments(final URL uRL) {
            String string3 = uRL.toString();
            if ("file".equals(uRL.getProtocol())) {
                string3 = string3.replace("file:", "file://");
            }
            return new String[] { "xdg-open", string3 };
        }
        
        public void openUri(final String string) {
            try {
                this.openUrl(new URI(string).toURL());
            }
            catch (URISyntaxException | MalformedURLException | IllegalArgumentException ex2) {
                final Exception ex;
                final Exception exception3 = ex;
                Util.LOGGER.error("Couldn't open uri '{}'", string, exception3);
            }
        }
    }
    
    enum IdentityStrategy implements Hash.Strategy<Object> {
        INSTANCE;
        
        public int hashCode(final Object object) {
            return System.identityHashCode(object);
        }
        
        public boolean equals(final Object object1, final Object object2) {
            return object1 == object2;
        }
    }
}
