package net.minecraft.launcher;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.mojang.authlib.Agent;
import com.mojang.authlib.UserAuthentication;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.launcher.OperatingSystem;
import com.mojang.launcher.updater.DateTypeAdapter;
import com.mojang.launcher.updater.VersionSyncInfo;
import com.mojang.launcher.updater.download.assets.AssetIndex;
import com.mojang.launcher.versions.CompleteVersion;
import com.mojang.launcher.versions.Version;
import java.io.File;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URL;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import javax.swing.JFrame;
import joptsimple.NonOptionArgumentSpec;
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import net.minecraft.launcher.game.GameLaunchDispatcher;
import net.minecraft.launcher.game.MinecraftReleaseType;
import net.minecraft.launcher.game.MinecraftReleaseTypeFactory;
import net.minecraft.launcher.profile.Profile;
import net.minecraft.launcher.profile.ProfileManager;
import net.minecraft.launcher.updater.CompleteMinecraftVersion;
import net.minecraft.launcher.updater.Library;
import net.minecraft.launcher.updater.LocalVersionList;
import net.minecraft.launcher.updater.MinecraftVersionManager;
import net.minecraft.launcher.updater.RemoteVersionList;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.AgeFileFilter;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.FileFileFilter;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.PrefixFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;

public class Launcher {
   private final Gson gson;
   private boolean winTenHack;
   private UUID clientToken;
   private static final Logger LOGGER;
   private static Launcher INSTANCE;
   private final com.mojang.launcher.Launcher launcher;
   private final Integer bootstrapVersion;
   private final MinecraftUserInterface userInterface;
   private final ProfileManager profileManager;
   private final GameLaunchDispatcher launchDispatcher;
   private String requestedUser;
   private final StorageManager usernameManager;
   private final OptionsManager optionsManager;

   public static Launcher getCurrentInstance() {
      return INSTANCE;
   }

   public Launcher(JFrame frame, File workingDirectory, Proxy proxy, PasswordAuthentication proxyAuth, String[] args) {
      this(frame, workingDirectory, proxy, proxyAuth, args, 0);
   }

   public Launcher(JFrame frame, File workingDirectory, Proxy proxy, PasswordAuthentication proxyAuth, String[] args, Integer bootstrapVersion) {
      this.gson = new Gson();
      this.winTenHack = false;
      this.clientToken = UUID.randomUUID();

      try {
         LoggerContext context = (LoggerContext)LogManager.getContext(false);
         URL url = Launcher.class.getResource("/log4j2.xml");
         context.setConfigLocation(url.toURI());
      } catch (Exception var9) {
      }

      INSTANCE = this;
      this.usernameManager = new StorageManager();
      this.usernameManager.load();
      this.optionsManager = new OptionsManager();
      this.setupErrorHandling();
      this.bootstrapVersion = bootstrapVersion;
      this.userInterface = this.selectUserInterface(frame);
      if (bootstrapVersion < 4) {
         this.userInterface.showOutdatedNotice();
         System.exit(0);
         throw new Error("Outdated bootstrap");
      } else {
         LOGGER.info(this.userInterface.getTitle() + " (through bootstrap " + bootstrapVersion + ") started on " + OperatingSystem.getCurrentPlatform().getName() + "...");
         LOGGER.info("Current time is " + DateFormat.getDateTimeInstance(2, 2, Locale.US).format(new Date()));
         if (!OperatingSystem.getCurrentPlatform().isSupported()) {
            LOGGER.fatal("This operating system is unknown or unsupported, we cannot guarantee that the game will launch successfully.");
         }

         LOGGER.info("System.getProperty('os.name') == '" + System.getProperty("os.name") + "'");
         LOGGER.info("System.getProperty('os.version') == '" + System.getProperty("os.version") + "'");
         LOGGER.info("System.getProperty('os.arch') == '" + System.getProperty("os.arch") + "'");
         LOGGER.info("System.getProperty('java.version') == '" + System.getProperty("java.version") + "'");
         LOGGER.info("System.getProperty('java.vendor') == '" + System.getProperty("java.vendor") + "'");
         LOGGER.info("System.getProperty('sun.arch.data.model') == '" + System.getProperty("sun.arch.data.model") + "'");
         LOGGER.info("proxy == " + proxy);
         this.launchDispatcher = new GameLaunchDispatcher(this, this.processArgs(args));
         this.launcher = new com.mojang.launcher.Launcher(this.userInterface, workingDirectory, proxy, proxyAuth, new MinecraftVersionManager(new LocalVersionList(workingDirectory), new RemoteVersionList(LauncherConstants.PROPERTIES.getVersionManifest(), proxy)), Agent.MINECRAFT, MinecraftReleaseTypeFactory.instance(), 21);
         this.profileManager = new ProfileManager(this);
         ((SwingUserInterface)this.userInterface).initializeFrame();
         this.refreshVersionsAndProfiles();
      }
   }

   public File findNativeLauncher() {
      String programData = System.getenv("ProgramData");
      if (programData == null) {
         programData = System.getenv("ALLUSERSPROFILE");
      }

      if (programData != null) {
         File shortcut = new File(programData, "Microsoft\\Windows\\Start Menu\\Programs\\Minecraft\\Minecraft.lnk");
         if (shortcut.isFile()) {
            return shortcut;
         }
      }

      return null;
   }

   public void runNativeLauncher(File executable, String[] args) {
      ProcessBuilder pb = new ProcessBuilder(new String[]{"cmd", "/c", executable.getAbsolutePath()});

      try {
         pb.start();
      } catch (IOException var5) {
         var5.printStackTrace();
      }

      System.exit(0);
   }

   private void setupErrorHandling() {
      Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
         public void uncaughtException(Thread t, Throwable e) {
            Launcher.LOGGER.fatal("Unhandled exception in thread " + t, e);
         }
      });
   }

   private String[] processArgs(String[] args) {
      OptionParser optionParser = new OptionParser();
      optionParser.allowsUnrecognizedOptions();
      OptionSpec<String> userOption = optionParser.accepts("user").withRequiredArg().ofType(String.class);
      NonOptionArgumentSpec nonOptions = optionParser.nonOptions();

      OptionSet optionSet;
      try {
         optionSet = optionParser.parse(args);
      } catch (OptionException var7) {
         return args;
      }

      if (optionSet.has((OptionSpec)userOption)) {
         this.requestedUser = (String)optionSet.valueOf((OptionSpec)userOption);
      }

      List<String> remainingOptions = optionSet.valuesOf((OptionSpec)nonOptions);
      return (String[])((String[])remainingOptions.toArray(new String[remainingOptions.size()]));
   }

   public void refreshVersionsAndProfiles() {
      this.getLauncher().getVersionManager().getExecutorService().submit(new Runnable() {
         public void run() {
            try {
               Launcher.this.getLauncher().getVersionManager().refreshVersions();
            } catch (Throwable var3) {
               Launcher.LOGGER.error("Unexpected exception refreshing version list", var3);
            }

            try {
               Launcher.this.profileManager.loadProfiles();
               Launcher.LOGGER.info("Loaded " + Launcher.this.profileManager.getProfiles().size() + " profile(s); selected '" + Launcher.this.profileManager.getSelectedProfile().getName() + "'");
            } catch (Throwable var2) {
               Launcher.LOGGER.error("Unexpected exception refreshing profile list", var2);
            }

            Launcher.LOGGER.info("Starting ensureloggedin");
            Launcher.this.ensureLoggedIn();
         }
      });
   }

   private MinecraftUserInterface selectUserInterface(JFrame frame) {
      return new SwingUserInterface(this, frame);
   }

   public com.mojang.launcher.Launcher getLauncher() {
      return this.launcher;
   }

   public MinecraftUserInterface getUserInterface() {
      return this.userInterface;
   }

   public Integer getBootstrapVersion() {
      return this.bootstrapVersion;
   }

   public void ensureLoggedIn() {
      UserAuthentication auth = this.profileManager.getAuthDatabase().getByUUID(this.profileManager.getSelectedUser());
      System.out.println("AUTO LOGIN " + this.usernameManager.isAutoLogin());
      if (this.usernameManager.isAutoLogin()) {
         LOGGER.info("UA2");
         auth.setUsername(this.usernameManager.getAutoLoginName());
         if (auth.canLogIn()) {
            try {
               auth.logIn();

               try {
                  this.profileManager.saveProfiles();
               } catch (IOException var3) {
                  LOGGER.error((String)"Couldn't save profiles after refreshing auth!", (Throwable)var3);
               }

               this.profileManager.fireRefreshEvent();
            } catch (AuthenticationException var4) {
               LOGGER.error((String)"Exception whilst logging into profile", (Throwable)var4);
               this.getUserInterface().showLoginPrompt();
            }
         } else {
            this.getUserInterface().showLoginPrompt();
         }
      } else {
         this.getUserInterface().showLoginPrompt();
      }

   }

   public UUID getClientToken() {
      return this.clientToken;
   }

   public void setClientToken(UUID clientToken) {
      this.clientToken = clientToken;
   }

   public void cleanupOrphanedAssets() throws IOException {
      File assetsDir = new File(this.getLauncher().getWorkingDirectory(), "assets");
      File indexDir = new File(assetsDir, "indexes");
      File objectsDir = new File(assetsDir, "objects");
      Set<String> referencedObjects = Sets.newHashSet();
      if (objectsDir.isDirectory()) {
         Iterator var5 = this.getLauncher().getVersionManager().getInstalledVersions().iterator();

         while(true) {
            VersionSyncInfo syncInfo;
            File directory;
            do {
               if (!var5.hasNext()) {
                  File[] directories = objectsDir.listFiles(DirectoryFileFilter.DIRECTORY);
                  if (directories != null) {
                     File[] var16 = directories;
                     int var17 = directories.length;

                     for(int var18 = 0; var18 < var17; ++var18) {
                        directory = var16[var18];
                        File[] files = directory.listFiles(FileFileFilter.FILE);
                        if (files != null) {
                           File[] var20 = files;
                           int var21 = files.length;

                           for(int var13 = 0; var13 < var21; ++var13) {
                              File file = var20[var13];
                              if (!referencedObjects.contains(file.getName().toLowerCase())) {
                                 LOGGER.info("Cleaning up orphaned object {}", file.getName());
                                 FileUtils.deleteQuietly(file);
                              }
                           }
                        }
                     }
                  }

                  deleteEmptyDirectories(objectsDir);
                  return;
               }

               syncInfo = (VersionSyncInfo)var5.next();
            } while(!(syncInfo.getLocalVersion() instanceof CompleteMinecraftVersion));

            CompleteMinecraftVersion version = (CompleteMinecraftVersion)syncInfo.getLocalVersion();
            String assetVersion = version.getAssetIndex().getId();
            directory = new File(indexDir, assetVersion + ".json");
            AssetIndex index = (AssetIndex)this.gson.fromJson(FileUtils.readFileToString(directory, Charsets.UTF_8), AssetIndex.class);
            Iterator var11 = index.getUniqueObjects().keySet().iterator();

            while(var11.hasNext()) {
               AssetIndex.AssetObject object = (AssetIndex.AssetObject)var11.next();
               referencedObjects.add(object.getHash().toLowerCase());
            }
         }
      }
   }

   public void cleanupOrphanedLibraries() throws IOException {
      File librariesDir = new File(this.getLauncher().getWorkingDirectory(), "libraries");
      Set<File> referencedLibraries = Sets.newHashSet();
      if (librariesDir.isDirectory()) {
         Iterator var3 = this.getLauncher().getVersionManager().getInstalledVersions().iterator();

         while(true) {
            VersionSyncInfo syncInfo;
            do {
               if (!var3.hasNext()) {
                  Collection<File> libraries = FileUtils.listFiles(librariesDir, TrueFileFilter.TRUE, TrueFileFilter.TRUE);
                  if (libraries != null) {
                     Iterator var11 = libraries.iterator();

                     while(var11.hasNext()) {
                        File file = (File)var11.next();
                        if (!referencedLibraries.contains(file)) {
                           LOGGER.info("Cleaning up orphaned library {}", file);
                           FileUtils.deleteQuietly(file);
                        }
                     }
                  }

                  deleteEmptyDirectories(librariesDir);
                  return;
               }

               syncInfo = (VersionSyncInfo)var3.next();
            } while(!(syncInfo.getLocalVersion() instanceof CompleteMinecraftVersion));

            CompleteMinecraftVersion version = (CompleteMinecraftVersion)syncInfo.getLocalVersion();
            Iterator var6 = version.getRelevantLibraries(version.createFeatureMatcher()).iterator();

            while(var6.hasNext()) {
               Library library = (Library)var6.next();
               String file = null;
               if (library.getNatives() != null) {
                  String natives = (String)library.getNatives().get(OperatingSystem.getCurrentPlatform());
                  if (natives != null) {
                     file = library.getArtifactPath(natives);
                  }
               } else {
                  file = library.getArtifactPath();
               }

               if (file != null) {
                  referencedLibraries.add(new File(librariesDir, file));
                  referencedLibraries.add(new File(librariesDir, file + ".sha"));
               }
            }
         }
      }
   }

   public void cleanupOldSkins() {
      File assetsDir = new File(this.getLauncher().getWorkingDirectory(), "assets");
      File skinsDir = new File(assetsDir, "skins");
      if (skinsDir.isDirectory()) {
         Collection<File> files = FileUtils.listFiles(skinsDir, new AgeFileFilter(System.currentTimeMillis() - 604800000L), TrueFileFilter.TRUE);
         if (files != null) {
            Iterator var4 = files.iterator();

            while(var4.hasNext()) {
               File file = (File)var4.next();
               LOGGER.info("Cleaning up old skin {}", file.getName());
               FileUtils.deleteQuietly(file);
            }
         }

         deleteEmptyDirectories(skinsDir);
      }
   }

   public void cleanupOldVirtuals() throws IOException {
      File assetsDir = new File(this.getLauncher().getWorkingDirectory(), "assets");
      File virtualsDir = new File(assetsDir, "virtual");
      DateTypeAdapter dateAdapter = new DateTypeAdapter();
      Calendar calendar = Calendar.getInstance();
      calendar.add(5, -5);
      Date cutoff = calendar.getTime();
      if (virtualsDir.isDirectory()) {
         File[] directories = virtualsDir.listFiles(DirectoryFileFilter.DIRECTORY);
         if (directories != null) {
            File[] var7 = directories;
            int var8 = directories.length;

            for(int var9 = 0; var9 < var8; ++var9) {
               File directory = var7[var9];
               File lastUsedFile = new File(directory, ".lastused");
               if (lastUsedFile.isFile()) {
                  Date lastUsed = dateAdapter.deserializeToDate(FileUtils.readFileToString(lastUsedFile));
                  if (cutoff.after(lastUsed)) {
                     LOGGER.info("Cleaning up old virtual directory {}", directory);
                     FileUtils.deleteQuietly(directory);
                  }
               } else {
                  LOGGER.info("Cleaning up strange virtual directory {}", directory);
                  FileUtils.deleteQuietly(directory);
               }
            }
         }

         deleteEmptyDirectories(virtualsDir);
      }
   }

   public void cleanupOldNatives() {
      File root = new File(this.launcher.getWorkingDirectory(), "versions/");
      LOGGER.info("Looking for old natives & assets to clean up...");
      IOFileFilter ageFilter = new AgeFileFilter(System.currentTimeMillis() - 3600000L);
      if (root.isDirectory()) {
         File[] versions = root.listFiles(DirectoryFileFilter.DIRECTORY);
         if (versions != null) {
            File[] var4 = versions;
            int var5 = versions.length;

            for(int var6 = 0; var6 < var5; ++var6) {
               File version = var4[var6];
               File[] files = version.listFiles(FileFilterUtils.and(new PrefixFileFilter(version.getName() + "-natives-"), ageFilter));
               if (files != null) {
                  File[] var9 = files;
                  int var10 = files.length;

                  for(int var11 = 0; var11 < var10; ++var11) {
                     File folder = var9[var11];
                     LOGGER.debug("Deleting " + folder);
                     FileUtils.deleteQuietly(folder);
                  }
               }
            }
         }

      }
   }

   public void cleanupOrphanedVersions() {
      LOGGER.info("Looking for orphaned versions to clean up...");
      Set<String> referencedVersions = Sets.newHashSet();
      Iterator var2 = this.getProfileManager().getProfiles().values().iterator();

      VersionSyncInfo versionSyncInfo;
      while(var2.hasNext()) {
         Profile profile = (Profile)var2.next();
         String lastVersionId = profile.getLastVersionId();
         versionSyncInfo = null;
         if (lastVersionId != null) {
            versionSyncInfo = this.getLauncher().getVersionManager().getVersionSyncInfo(lastVersionId);
         }

         if (versionSyncInfo == null || versionSyncInfo.getLatestVersion() == null) {
            versionSyncInfo = (VersionSyncInfo)this.getLauncher().getVersionManager().getVersions(profile.getVersionFilter()).get(0);
         }

         if (versionSyncInfo != null) {
            Version version = versionSyncInfo.getLatestVersion();
            referencedVersions.add(version.getId());
            if (version instanceof CompleteMinecraftVersion) {
               CompleteMinecraftVersion completeMinecraftVersion = (CompleteMinecraftVersion)version;
               referencedVersions.add(completeMinecraftVersion.getInheritsFrom());
               referencedVersions.add(completeMinecraftVersion.getJar());
            }
         }
      }

      Calendar calendar = Calendar.getInstance();
      calendar.add(5, -7);
      Date cutoff = calendar.getTime();
      Iterator var12 = this.getLauncher().getVersionManager().getInstalledVersions().iterator();

      while(var12.hasNext()) {
         versionSyncInfo = (VersionSyncInfo)var12.next();
         if (versionSyncInfo.getLocalVersion() instanceof CompleteMinecraftVersion) {
            CompleteVersion version = (CompleteVersion)versionSyncInfo.getLocalVersion();
            if (!referencedVersions.contains(version.getId()) && version.getType() == MinecraftReleaseType.SNAPSHOT) {
               if (versionSyncInfo.isOnRemote()) {
                  LOGGER.info("Deleting orphaned version {} because it's a snapshot available on remote", version.getId());

                  try {
                     this.getLauncher().getVersionManager().uninstallVersion(version);
                  } catch (IOException var8) {
                     LOGGER.warn((String)("Couldn't uninstall version " + version.getId()), (Throwable)var8);
                  }
               } else if (version.getUpdatedTime().before(cutoff)) {
                  LOGGER.info("Deleting orphaned version {} because it's an unsupported old snapshot", version.getId());

                  try {
                     this.getLauncher().getVersionManager().uninstallVersion(version);
                  } catch (IOException var9) {
                     LOGGER.warn((String)("Couldn't uninstall version " + version.getId()), (Throwable)var9);
                  }
               }
            }
         }
      }

   }

   private static Collection<File> listEmptyDirectories(File directory) {
      List<File> result = Lists.newArrayList();
      File[] files = directory.listFiles();
      if (files != null) {
         File[] var3 = files;
         int var4 = files.length;

         for(int var5 = 0; var5 < var4; ++var5) {
            File file = var3[var5];
            if (file.isDirectory()) {
               File[] subFiles = file.listFiles();
               if (subFiles != null && subFiles.length != 0) {
                  result.addAll(listEmptyDirectories(file));
               } else {
                  result.add(file);
               }
            }
         }
      }

      return result;
   }

   private static void deleteEmptyDirectories(File directory) {
      while(true) {
         Collection<File> files = listEmptyDirectories(directory);
         if (files.isEmpty()) {
            return;
         }

         Iterator var2 = files.iterator();

         while(var2.hasNext()) {
            File file = (File)var2.next();
            if (!FileUtils.deleteQuietly(file)) {
               return;
            }

            LOGGER.info("Deleted empty directory {}", file);
         }
      }
   }

   public void performCleanups() throws IOException {
      this.cleanupOrphanedVersions();
      this.cleanupOrphanedAssets();
      this.cleanupOldSkins();
      this.cleanupOldNatives();
      this.cleanupOldVirtuals();
   }

   public ProfileManager getProfileManager() {
      return this.profileManager;
   }

   public GameLaunchDispatcher getLaunchDispatcher() {
      return this.launchDispatcher;
   }

   public boolean usesWinTenHack() {
      return this.winTenHack;
   }

   public void setWinTenHack() {
      this.winTenHack = true;
   }

   public static Launcher getInstance() {
      return INSTANCE;
   }

   public StorageManager getUsernameManager() {
      return this.usernameManager;
   }

   public OptionsManager getOptionsManager() {
      return this.optionsManager;
   }

   static {
      Thread.currentThread().setContextClassLoader(Launcher.class.getClassLoader());
      LOGGER = LogManager.getLogger();
   }
}
