package net.minecraft;

import org.apache.logging.log4j.LogManager;
import java.util.concurrent.CompletionException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.io.FileOutputStream;
import java.util.Date;
import java.text.SimpleDateFormat;
import org.apache.commons.io.IOUtils;
import java.io.Writer;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Iterator;
import org.apache.commons.lang3.ArrayUtils;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import com.google.common.collect.Lists;
import java.io.File;
import java.util.List;
import org.apache.logging.log4j.Logger;

public class CrashReport {
    private static final Logger LOGGER;
    private final String title;
    private final Throwable exception;
    private final CrashReportCategory systemDetails;
    private final List<CrashReportCategory> details;
    private File saveFile;
    private boolean trackingStackTrace;
    private StackTraceElement[] uncategorizedStackTrace;
    
    public CrashReport(final String string, final Throwable throwable) {
        this.systemDetails = new CrashReportCategory(this, "System Details");
        this.details = Lists.newArrayList();
        this.trackingStackTrace = true;
        this.uncategorizedStackTrace = new StackTraceElement[0];
        this.title = string;
        this.exception = throwable;
        this.initDetails();
    }
    
    private void initDetails() {
        this.systemDetails.setDetail("Minecraft Version", () -> SharedConstants.getCurrentVersion().getName());
        this.systemDetails.setDetail("Minecraft Version ID", () -> SharedConstants.getCurrentVersion().getId());
        this.systemDetails.setDetail("Operating System", () -> System.getProperty("os.name") + " (" + System.getProperty("os.arch") + ") version " + System.getProperty("os.version"));
        this.systemDetails.setDetail("Java Version", () -> System.getProperty("java.version") + ", " + System.getProperty("java.vendor"));
        this.systemDetails.setDetail("Java VM Version", () -> System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"));
        final Runtime runtime1;
        final long long2;
        final long long3;
        final long long4;
        final long long5;
        final long long6;
        final long long7;
        this.systemDetails.setDetail("Memory", () -> {
            runtime1 = Runtime.getRuntime();
            long2 = runtime1.maxMemory();
            long3 = runtime1.totalMemory();
            long4 = runtime1.freeMemory();
            long5 = long2 / 1024L / 1024L;
            long6 = long3 / 1024L / 1024L;
            long7 = long4 / 1024L / 1024L;
            return long4 + " bytes (" + long7 + " MB) / " + long3 + " bytes (" + long6 + " MB) up to " + long2 + " bytes (" + long5 + " MB)";
        });
        this.systemDetails.setDetail("CPUs", Runtime.getRuntime().availableProcessors());
        final List<String> list1;
        this.systemDetails.setDetail("JVM Flags", () -> {
            list1 = Util.getVmArguments().collect(Collectors.toList());
            return String.format("%d total; %s", list1.size(), list1.stream().collect(Collectors.joining(" ")));
        });
    }
    
    public String getTitle() {
        return this.title;
    }
    
    public Throwable getException() {
        return this.exception;
    }
    
    public void getDetails(final StringBuilder stringBuilder) {
        if ((this.uncategorizedStackTrace == null || this.uncategorizedStackTrace.length <= 0) && !this.details.isEmpty()) {
            this.uncategorizedStackTrace = (StackTraceElement[])ArrayUtils.subarray((Object[])this.details.get(0).getStacktrace(), 0, 1);
        }
        if (this.uncategorizedStackTrace != null && this.uncategorizedStackTrace.length > 0) {
            stringBuilder.append("-- Head --\n");
            stringBuilder.append("Thread: ").append(Thread.currentThread().getName()).append("\n");
            stringBuilder.append("Stacktrace:\n");
            for (final StackTraceElement stackTraceElement6 : this.uncategorizedStackTrace) {
                stringBuilder.append("\t").append("at ").append(stackTraceElement6);
                stringBuilder.append("\n");
            }
            stringBuilder.append("\n");
        }
        for (final CrashReportCategory i4 : this.details) {
            i4.getDetails(stringBuilder);
            stringBuilder.append("\n\n");
        }
        this.systemDetails.getDetails(stringBuilder);
    }
    
    public String getExceptionMessage() {
        StringWriter stringWriter2 = null;
        PrintWriter printWriter3 = null;
        Throwable throwable4 = this.exception;
        if (throwable4.getMessage() == null) {
            if (throwable4 instanceof NullPointerException) {
                throwable4 = new NullPointerException(this.title);
            }
            else if (throwable4 instanceof StackOverflowError) {
                throwable4 = new StackOverflowError(this.title);
            }
            else if (throwable4 instanceof OutOfMemoryError) {
                throwable4 = new OutOfMemoryError(this.title);
            }
            throwable4.setStackTrace(this.exception.getStackTrace());
        }
        try {
            stringWriter2 = new StringWriter();
            printWriter3 = new PrintWriter(stringWriter2);
            throwable4.printStackTrace(printWriter3);
            return stringWriter2.toString();
        }
        finally {
            IOUtils.closeQuietly((Writer)stringWriter2);
            IOUtils.closeQuietly((Writer)printWriter3);
        }
    }
    
    public String getFriendlyReport() {
        final StringBuilder stringBuilder2 = new StringBuilder();
        stringBuilder2.append("---- Minecraft Crash Report ----\n");
        stringBuilder2.append("// ");
        stringBuilder2.append(getErrorComment());
        stringBuilder2.append("\n\n");
        stringBuilder2.append("Time: ");
        stringBuilder2.append(new SimpleDateFormat().format(new Date()));
        stringBuilder2.append("\n");
        stringBuilder2.append("Description: ");
        stringBuilder2.append(this.title);
        stringBuilder2.append("\n\n");
        stringBuilder2.append(this.getExceptionMessage());
        stringBuilder2.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n");
        for (int integer3 = 0; integer3 < 87; ++integer3) {
            stringBuilder2.append("-");
        }
        stringBuilder2.append("\n\n");
        this.getDetails(stringBuilder2);
        return stringBuilder2.toString();
    }
    
    public File getSaveFile() {
        return this.saveFile;
    }
    
    public boolean saveToFile(final File file) {
        if (this.saveFile != null) {
            return false;
        }
        if (file.getParentFile() != null) {
            file.getParentFile().mkdirs();
        }
        Writer writer3 = null;
        try {
            writer3 = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
            writer3.write(this.getFriendlyReport());
            this.saveFile = file;
            return true;
        }
        catch (Throwable throwable4) {
            CrashReport.LOGGER.error("Could not save crash report to {}", file, throwable4);
            return false;
        }
        finally {
            IOUtils.closeQuietly(writer3);
        }
    }
    
    public CrashReportCategory getSystemDetails() {
        return this.systemDetails;
    }
    
    public CrashReportCategory addCategory(final String string) {
        return this.addCategory(string, 1);
    }
    
    public CrashReportCategory addCategory(final String string, final int integer) {
        final CrashReportCategory i4 = new CrashReportCategory(this, string);
        if (this.trackingStackTrace) {
            final int integer2 = i4.fillInStackTrace(integer);
            final StackTraceElement[] arr6 = this.exception.getStackTrace();
            StackTraceElement stackTraceElement7 = null;
            StackTraceElement stackTraceElement8 = null;
            final int integer3 = arr6.length - integer2;
            if (integer3 < 0) {
                System.out.println("Negative index in crash report handler (" + arr6.length + "/" + integer2 + ")");
            }
            if (arr6 != null && 0 <= integer3 && integer3 < arr6.length) {
                stackTraceElement7 = arr6[integer3];
                if (arr6.length + 1 - integer2 < arr6.length) {
                    stackTraceElement8 = arr6[arr6.length + 1 - integer2];
                }
            }
            this.trackingStackTrace = i4.validateStackTrace(stackTraceElement7, stackTraceElement8);
            if (integer2 > 0 && !this.details.isEmpty()) {
                final CrashReportCategory i5 = this.details.get(this.details.size() - 1);
                i5.trimStacktrace(integer2);
            }
            else if (arr6 != null && arr6.length >= integer2 && 0 <= integer3 && integer3 < arr6.length) {
                System.arraycopy(arr6, 0, this.uncategorizedStackTrace = new StackTraceElement[integer3], 0, this.uncategorizedStackTrace.length);
            }
            else {
                this.trackingStackTrace = false;
            }
        }
        this.details.add(i4);
        return i4;
    }
    
    private static String getErrorComment() {
        final String[] arr1 = { "Who set us up the TNT?", "Everything's going to plan. No, really, that was supposed to happen.", "Uh... Did I do that?", "Oops.", "Why did you do that?", "I feel sad now :(", "My bad.", "I'm sorry, Dave.", "I let you down. Sorry :(", "On the bright side, I bought you a teddy bear!", "Daisy, daisy...", "Oh - I know what I did wrong!", "Hey, that tickles! Hehehe!", "I blame Dinnerbone.", "You should try our sister game, Minceraft!", "Don't be sad. I'll do better next time, I promise!", "Don't be sad, have a hug! <3", "I just don't know what went wrong :(", "Shall we play a game?", "Quite honestly, I wouldn't worry myself about that.", "I bet Cylons wouldn't have this problem.", "Sorry :(", "Surprise! Haha. Well, this is awkward.", "Would you like a cupcake?", "Hi. I'm Minecraft, and I'm a crashaholic.", "Ooh. Shiny.", "This doesn't make any sense!", "Why is it breaking :(", "Don't do that.", "Ouch. That hurt :(", "You're mean.", "This is a token for 1 free hug. Redeem at your nearest Mojangsta: [~~HUG~~]", "There are four lights!", "But it works on my machine." };
        try {
            return arr1[(int)(Util.getNanos() % arr1.length)];
        }
        catch (Throwable throwable2) {
            return "Witty comment unavailable :(";
        }
    }
    
    public static CrashReport forThrowable(Throwable throwable, final String string) {
        while (throwable instanceof CompletionException && throwable.getCause() != null) {
            throwable = throwable.getCause();
        }
        CrashReport h3;
        if (throwable instanceof ReportedException) {
            h3 = ((ReportedException)throwable).getReport();
        }
        else {
            h3 = new CrashReport(string, throwable);
        }
        return h3;
    }
    
    public static void preload() {
        new CrashReport("Don't panic!", new Throwable()).getFriendlyReport();
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
