package com.mojang.realmsclient.client;

import org.apache.commons.io.output.CountingOutputStream;
import org.apache.commons.io.FileUtils;
import com.google.common.io.Files;
import com.google.common.hash.Hashing;
import java.awt.event.ActionEvent;
import org.apache.logging.log4j.LogManager;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import java.nio.file.Path;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import java.util.regex.Matcher;
import java.util.Iterator;
import net.minecraft.world.level.storage.LevelResource;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import net.minecraft.client.Minecraft;
import java.util.Locale;
import net.minecraft.world.level.storage.LevelSummary;
import org.apache.commons.lang3.StringUtils;
import net.minecraft.SharedConstants;
import java.util.regex.Pattern;
import org.apache.http.HttpResponse;
import com.mojang.realmsclient.exception.RealmsDefaultUncaughtExceptionHandler;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import java.awt.event.ActionListener;
import java.io.FileOutputStream;
import net.minecraft.world.level.storage.LevelStorageSource;
import com.mojang.realmsclient.gui.screens.RealmsDownloadLatestWorldScreen;
import com.mojang.realmsclient.dto.WorldDownload;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import java.io.IOException;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import java.io.File;
import org.apache.logging.log4j.Logger;

public class FileDownload {
    private static final Logger LOGGER;
    private volatile boolean cancelled;
    private volatile boolean finished;
    private volatile boolean error;
    private volatile boolean extracting;
    private volatile File tempFile;
    private volatile File resourcePackPath;
    private volatile HttpGet request;
    private Thread currentThread;
    private final RequestConfig requestConfig;
    private static final String[] INVALID_FILE_NAMES;
    
    public FileDownload() {
        this.requestConfig = RequestConfig.custom().setSocketTimeout(120000).setConnectTimeout(120000).build();
    }
    
    public long contentLength(final String string) {
        CloseableHttpClient closeableHttpClient3 = null;
        HttpGet httpGet4 = null;
        try {
            httpGet4 = new HttpGet(string);
            closeableHttpClient3 = HttpClientBuilder.create().setDefaultRequestConfig(this.requestConfig).build();
            final CloseableHttpResponse closeableHttpResponse5 = closeableHttpClient3.execute((HttpUriRequest)httpGet4);
            return Long.parseLong(closeableHttpResponse5.getFirstHeader("Content-Length").getValue());
        }
        catch (Throwable throwable5) {
            FileDownload.LOGGER.error("Unable to get content length for download");
            return 0L;
        }
        finally {
            if (httpGet4 != null) {
                httpGet4.releaseConnection();
            }
            if (closeableHttpClient3 != null) {
                try {
                    closeableHttpClient3.close();
                }
                catch (IOException iOException10) {
                    FileDownload.LOGGER.error("Could not close http client", (Throwable)iOException10);
                }
            }
        }
    }
    
    public void download(final WorldDownload djc, final String string, final RealmsDownloadLatestWorldScreen.DownloadStatus a, final LevelStorageSource dae) {
        if (this.currentThread != null) {
            return;
        }
        CloseableHttpClient closeableHttpClient6;
        HttpResponse httpResponse7;
        OutputStream outputStream8;
        ProgressListener b9;
        DownloadCountingOutputStream a2;
        HttpResponse httpResponse8;
        OutputStream outputStream9;
        ResourcePackProgressListener c9;
        DownloadCountingOutputStream a3;
        HttpResponse httpResponse9;
        OutputStream outputStream10;
        ResourcePackProgressListener c10;
        DownloadCountingOutputStream a4;
        (this.currentThread = new Thread(() -> {
            closeableHttpClient6 = null;
            try {
                this.tempFile = File.createTempFile("backup", ".tar.gz");
                this.request = new HttpGet(djc.downloadLink);
                closeableHttpClient6 = HttpClientBuilder.create().setDefaultRequestConfig(this.requestConfig).build();
                httpResponse7 = (HttpResponse)closeableHttpClient6.execute((HttpUriRequest)this.request);
                a.totalBytes = Long.parseLong(httpResponse7.getFirstHeader("Content-Length").getValue());
                if (httpResponse7.getStatusLine().getStatusCode() != 200) {
                    this.error = true;
                    this.request.abort();
                }
                else {
                    outputStream8 = new FileOutputStream(this.tempFile);
                    b9 = new ProgressListener(string.trim(), this.tempFile, dae, a);
                    a2 = new DownloadCountingOutputStream(outputStream8);
                    a2.setListener(b9);
                    IOUtils.copy(httpResponse7.getEntity().getContent(), (OutputStream)a2);
                }
            }
            catch (Exception exception7) {
                FileDownload.LOGGER.error("Caught exception while downloading: " + exception7.getMessage());
                this.error = true;
                this.request.releaseConnection();
                if (this.tempFile != null) {
                    this.tempFile.delete();
                }
                if (!this.error) {
                    if (!djc.resourcePackUrl.isEmpty() && !djc.resourcePackHash.isEmpty()) {
                        try {
                            this.tempFile = File.createTempFile("resources", ".tar.gz");
                            this.request = new HttpGet(djc.resourcePackUrl);
                            httpResponse8 = (HttpResponse)closeableHttpClient6.execute((HttpUriRequest)this.request);
                            a.totalBytes = Long.parseLong(httpResponse8.getFirstHeader("Content-Length").getValue());
                            if (httpResponse8.getStatusLine().getStatusCode() != 200) {
                                this.error = true;
                                this.request.abort();
                                return;
                            }
                            else {
                                outputStream9 = new FileOutputStream(this.tempFile);
                                c9 = new ResourcePackProgressListener(this.tempFile, a, djc);
                                a3 = new DownloadCountingOutputStream(outputStream9);
                                a3.setListener(c9);
                                IOUtils.copy(httpResponse8.getEntity().getContent(), (OutputStream)a3);
                            }
                        }
                        catch (Exception exception8) {
                            FileDownload.LOGGER.error("Caught exception while downloading: " + exception8.getMessage());
                            this.error = true;
                        }
                        finally {
                            this.request.releaseConnection();
                            if (this.tempFile != null) {
                                this.tempFile.delete();
                            }
                        }
                    }
                    else {
                        this.finished = true;
                    }
                }
                if (closeableHttpClient6 != null) {
                    try {
                        closeableHttpClient6.close();
                    }
                    catch (IOException iOException7) {
                        FileDownload.LOGGER.error("Failed to close Realms download client");
                    }
                }
            }
            finally {
                this.request.releaseConnection();
                if (this.tempFile != null) {
                    this.tempFile.delete();
                }
                if (!this.error) {
                    if (!djc.resourcePackUrl.isEmpty() && !djc.resourcePackHash.isEmpty()) {
                        try {
                            this.tempFile = File.createTempFile("resources", ".tar.gz");
                            this.request = new HttpGet(djc.resourcePackUrl);
                            httpResponse9 = (HttpResponse)closeableHttpClient6.execute((HttpUriRequest)this.request);
                            a.totalBytes = Long.parseLong(httpResponse9.getFirstHeader("Content-Length").getValue());
                            if (httpResponse9.getStatusLine().getStatusCode() != 200) {
                                this.error = true;
                                this.request.abort();
                                return;
                            }
                            else {
                                outputStream10 = new FileOutputStream(this.tempFile);
                                c10 = new ResourcePackProgressListener(this.tempFile, a, djc);
                                a4 = new DownloadCountingOutputStream(outputStream10);
                                a4.setListener(c10);
                                IOUtils.copy(httpResponse9.getEntity().getContent(), (OutputStream)a4);
                            }
                        }
                        catch (Exception exception9) {
                            FileDownload.LOGGER.error("Caught exception while downloading: " + exception9.getMessage());
                            this.error = true;
                            this.request.releaseConnection();
                            if (this.tempFile != null) {
                                this.tempFile.delete();
                            }
                        }
                        finally {
                            this.request.releaseConnection();
                            if (this.tempFile != null) {
                                this.tempFile.delete();
                            }
                        }
                    }
                    else {
                        this.finished = true;
                    }
                }
                if (closeableHttpClient6 != null) {
                    try {
                        closeableHttpClient6.close();
                    }
                    catch (IOException iOException8) {
                        FileDownload.LOGGER.error("Failed to close Realms download client");
                    }
                }
            }
            return;
        })).setUncaughtExceptionHandler(new RealmsDefaultUncaughtExceptionHandler(FileDownload.LOGGER));
        this.currentThread.start();
    }
    
    public void cancel() {
        if (this.request != null) {
            this.request.abort();
        }
        if (this.tempFile != null) {
            this.tempFile.delete();
        }
        this.cancelled = true;
    }
    
    public boolean isFinished() {
        return this.finished;
    }
    
    public boolean isError() {
        return this.error;
    }
    
    public boolean isExtracting() {
        return this.extracting;
    }
    
    public static String findAvailableFolderName(String string) {
        string = string.replaceAll("[\\./\"]", "_");
        for (final String string2 : FileDownload.INVALID_FILE_NAMES) {
            if (string.equalsIgnoreCase(string2)) {
                string = "_" + string + "_";
            }
        }
        return string;
    }
    
    private void untarGzipArchive(String string, final File file, final LevelStorageSource dae) throws IOException {
        final Pattern pattern5 = Pattern.compile(".*-([0-9]+)$");
        int integer7 = 1;
        for (final char character11 : SharedConstants.ILLEGAL_FILE_CHARACTERS) {
            string = string.replace(character11, '_');
        }
        if (StringUtils.isEmpty((CharSequence)string)) {
            string = "Realm";
        }
        string = findAvailableFolderName(string);
        try {
            for (final LevelSummary daf9 : dae.getLevelList()) {
                if (daf9.getLevelId().toLowerCase(Locale.ROOT).startsWith(string.toLowerCase(Locale.ROOT))) {
                    final Matcher matcher10 = pattern5.matcher(daf9.getLevelId());
                    if (matcher10.matches()) {
                        if (Integer.valueOf(matcher10.group(1)) <= integer7) {
                            continue;
                        }
                        integer7 = Integer.valueOf(matcher10.group(1));
                    }
                    else {
                        ++integer7;
                    }
                }
            }
        }
        catch (Exception exception8) {
            FileDownload.LOGGER.error("Error getting level list", (Throwable)exception8);
            this.error = true;
            return;
        }
        String string2;
        if (!dae.isNewLevelIdAcceptable(string) || integer7 > 1) {
            string2 = string + ((integer7 == 1) ? "" : ("-" + integer7));
            if (!dae.isNewLevelIdAcceptable(string2)) {
                for (boolean boolean8 = false; !boolean8; boolean8 = true) {
                    ++integer7;
                    string2 = string + ((integer7 == 1) ? "" : ("-" + integer7));
                    if (dae.isNewLevelIdAcceptable(string2)) {}
                }
            }
        }
        else {
            string2 = string;
        }
        TarArchiveInputStream tarArchiveInputStream8 = null;
        final File file2 = new File(Minecraft.getInstance().gameDirectory.getAbsolutePath(), "saves");
        try {
            file2.mkdir();
            tarArchiveInputStream8 = new TarArchiveInputStream((InputStream)new GzipCompressorInputStream((InputStream)new BufferedInputStream(new FileInputStream(file))));
            for (TarArchiveEntry tarArchiveEntry10 = tarArchiveInputStream8.getNextTarEntry(); tarArchiveEntry10 != null; tarArchiveEntry10 = tarArchiveInputStream8.getNextTarEntry()) {
                final File file3 = new File(file2, tarArchiveEntry10.getName().replace("world", string2));
                if (tarArchiveEntry10.isDirectory()) {
                    file3.mkdirs();
                }
                else {
                    file3.createNewFile();
                    try (final FileOutputStream fileOutputStream12 = new FileOutputStream(file3)) {
                        IOUtils.copy((InputStream)tarArchiveInputStream8, (OutputStream)fileOutputStream12);
                    }
                }
            }
        }
        catch (Exception exception9) {
            FileDownload.LOGGER.error("Error extracting world", (Throwable)exception9);
            this.error = true;
        }
        finally {
            if (tarArchiveInputStream8 != null) {
                tarArchiveInputStream8.close();
            }
            if (file != null) {
                file.delete();
            }
            try (final LevelStorageSource.LevelStorageAccess a22 = dae.createAccess(string2)) {
                a22.renameLevel(string2.trim());
                final Path path24 = a22.getLevelPath(LevelResource.LEVEL_DATA_FILE);
                deletePlayerTag(path24.toFile());
            }
            catch (IOException iOException22) {
                FileDownload.LOGGER.error("Failed to rename unpacked realms level {}", string2, iOException22);
            }
            this.resourcePackPath = new File(file2, string2 + File.separator + "resources.zip");
        }
    }
    
    private static void deletePlayerTag(final File file) {
        if (file.exists()) {
            try {
                final CompoundTag le2 = NbtIo.readCompressed(new FileInputStream(file));
                final CompoundTag le3 = le2.getCompound("Data");
                le3.remove("Player");
                NbtIo.writeCompressed(le2, new FileOutputStream(file));
            }
            catch (Exception exception2) {
                exception2.printStackTrace();
            }
        }
    }
    
    static {
        LOGGER = LogManager.getLogger();
        INVALID_FILE_NAMES = new String[] { "CON", "COM", "PRN", "AUX", "CLOCK$", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
    }
    
    class ProgressListener implements ActionListener {
        private final String worldName;
        private final File tempFile;
        private final LevelStorageSource levelStorageSource;
        private final RealmsDownloadLatestWorldScreen.DownloadStatus downloadStatus;
        
        private ProgressListener(final String string, final File file, final LevelStorageSource dae, final RealmsDownloadLatestWorldScreen.DownloadStatus a) {
            this.worldName = string;
            this.tempFile = file;
            this.levelStorageSource = dae;
            this.downloadStatus = a;
        }
        
        @Override
        public void actionPerformed(final ActionEvent actionEvent) {
            this.downloadStatus.bytesWritten = ((DownloadCountingOutputStream)actionEvent.getSource()).getByteCount();
            if (this.downloadStatus.bytesWritten >= this.downloadStatus.totalBytes && !FileDownload.this.cancelled && !FileDownload.this.error) {
                try {
                    FileDownload.this.extracting = true;
                    FileDownload.this.untarGzipArchive(this.worldName, this.tempFile, this.levelStorageSource);
                }
                catch (IOException iOException3) {
                    FileDownload.LOGGER.error("Error extracting archive", (Throwable)iOException3);
                    FileDownload.this.error = true;
                }
            }
        }
    }
    
    class ResourcePackProgressListener implements ActionListener {
        private final File tempFile;
        private final RealmsDownloadLatestWorldScreen.DownloadStatus downloadStatus;
        private final WorldDownload worldDownload;
        
        private ResourcePackProgressListener(final File file, final RealmsDownloadLatestWorldScreen.DownloadStatus a, final WorldDownload djc) {
            this.tempFile = file;
            this.downloadStatus = a;
            this.worldDownload = djc;
        }
        
        @Override
        public void actionPerformed(final ActionEvent actionEvent) {
            this.downloadStatus.bytesWritten = ((DownloadCountingOutputStream)actionEvent.getSource()).getByteCount();
            if (this.downloadStatus.bytesWritten >= this.downloadStatus.totalBytes && !FileDownload.this.cancelled) {
                try {
                    final String string3 = Hashing.sha1().hashBytes(Files.toByteArray(this.tempFile)).toString();
                    if (string3.equals(this.worldDownload.resourcePackHash)) {
                        FileUtils.copyFile(this.tempFile, FileDownload.this.resourcePackPath);
                        FileDownload.this.finished = true;
                    }
                    else {
                        FileDownload.LOGGER.error("Resourcepack had wrong hash (expected " + this.worldDownload.resourcePackHash + ", found " + string3 + "). Deleting it.");
                        FileUtils.deleteQuietly(this.tempFile);
                        FileDownload.this.error = true;
                    }
                }
                catch (IOException iOException3) {
                    FileDownload.LOGGER.error("Error copying resourcepack file", iOException3.getMessage());
                    FileDownload.this.error = true;
                }
            }
        }
    }
    
    class DownloadCountingOutputStream extends CountingOutputStream {
        private ActionListener listener;
        
        public DownloadCountingOutputStream(final OutputStream outputStream) {
            super(outputStream);
        }
        
        public void setListener(final ActionListener actionListener) {
            this.listener = actionListener;
        }
        
        protected void afterWrite(final int integer) throws IOException {
            super.afterWrite(integer);
            if (this.listener != null) {
                this.listener.actionPerformed(new ActionEvent(this, 0, null));
            }
        }
    }
}
