package net.minecraft.launcher.updater;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.mojang.launcher.OperatingSystem;
import com.mojang.launcher.events.RefreshedVersionsListener;
import com.mojang.launcher.updater.ExceptionalThreadPoolExecutor;
import com.mojang.launcher.updater.VersionFilter;
import com.mojang.launcher.updater.VersionManager;
import com.mojang.launcher.updater.VersionSyncInfo;
import com.mojang.launcher.updater.download.DownloadJob;
import com.mojang.launcher.updater.download.Downloadable;
import com.mojang.launcher.updater.download.EtagDownloadable;
import com.mojang.launcher.updater.download.assets.AssetDownloadable;
import com.mojang.launcher.updater.download.assets.AssetIndex;
import com.mojang.launcher.versions.CompleteVersion;
import com.mojang.launcher.versions.ReleaseType;
import com.mojang.launcher.versions.Version;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import net.minecraft.launcher.game.MinecraftReleaseType;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class MinecraftVersionManager implements VersionManager {
   private static final Logger LOGGER = LogManager.getLogger();
   private final VersionList localVersionList;
   private final VersionList remoteVersionList;
   private final ThreadPoolExecutor executorService;
   private final List<RefreshedVersionsListener> refreshedVersionsListeners;
   private final Object refreshLock;
   private boolean isRefreshing;
   private final Gson gson;

   public MinecraftVersionManager(VersionList localVersionList, VersionList remoteVersionList) {
      this.executorService = new ExceptionalThreadPoolExecutor(4, 8, 30L, TimeUnit.SECONDS);
      this.refreshedVersionsListeners = Collections.synchronizedList(new ArrayList());
      this.refreshLock = new Object();
      this.gson = new Gson();
      this.localVersionList = localVersionList;
      this.remoteVersionList = remoteVersionList;
   }

   public void refreshVersions() throws IOException {
      synchronized(this.refreshLock) {
         this.isRefreshing = true;
      }

      try {
         LOGGER.info("Refreshing local version list...");
         this.localVersionList.refreshVersions();
         LOGGER.info("Refreshing remote version list...");
         this.remoteVersionList.refreshVersions();
      } catch (IOException var7) {
         synchronized(this.refreshLock) {
            this.isRefreshing = false;
         }

         throw var7;
      }

      LOGGER.info("Refresh complete.");
      synchronized(this.refreshLock) {
         this.isRefreshing = false;
      }

      Iterator var1 = Lists.newArrayList((Iterable)this.refreshedVersionsListeners).iterator();

      while(var1.hasNext()) {
         RefreshedVersionsListener listener = (RefreshedVersionsListener)var1.next();
         listener.onVersionsRefreshed(this);
      }

   }

   public List<VersionSyncInfo> getVersions() {
      return this.getVersions((VersionFilter)null);
   }

   public List<VersionSyncInfo> getVersions(VersionFilter<? extends ReleaseType> filter) {
      synchronized(this.refreshLock) {
         if (this.isRefreshing) {
            return new ArrayList();
         }
      }

      List<VersionSyncInfo> result = new ArrayList();
      Object lookup = new HashMap();
      Map<MinecraftReleaseType, Integer> counts = Maps.newEnumMap(MinecraftReleaseType.class);
      MinecraftReleaseType[] var5 = MinecraftReleaseType.values();
      int var6 = var5.length;

      for(int var7 = 0; var7 < var6; ++var7) {
         MinecraftReleaseType type = var5[var7];
         counts.put(type, 0);
      }

      Iterator var10 = Lists.newArrayList((Iterable)this.localVersionList.getVersions()).iterator();

      while(true) {
         Version version;
         MinecraftReleaseType type;
         VersionSyncInfo syncInfo;
         do {
            do {
               do {
                  if (!var10.hasNext()) {
                     var10 = this.remoteVersionList.getVersions().iterator();

                     while(true) {
                        do {
                           do {
                              do {
                                 do {
                                    if (!var10.hasNext()) {
                                       if (result.isEmpty()) {
                                          var10 = this.localVersionList.getVersions().iterator();

                                          while(var10.hasNext()) {
                                             version = (Version)var10.next();
                                             if (version.getType() != null && version.getUpdatedTime() != null) {
                                                VersionSyncInfo syncInfo = this.getVersionSyncInfo(version, this.remoteVersionList.getVersion(version.getId()));
                                                ((Map)lookup).put(version.getId(), syncInfo);
                                                result.add(syncInfo);
                                             }
                                          }
                                       }

                                       Collections.sort(result, new Comparator() {
                                          public int compare(Object aa, Object bb) {
                                             VersionSyncInfo a = (VersionSyncInfo)aa;
                                             VersionSyncInfo b = (VersionSyncInfo)bb;
                                             Version aVer = a.getLatestVersion();
                                             Version bVer = b.getLatestVersion();
                                             return aVer.getReleaseTime() != null && bVer.getReleaseTime() != null ? bVer.getReleaseTime().compareTo(aVer.getReleaseTime()) : bVer.getUpdatedTime().compareTo(aVer.getUpdatedTime());
                                          }
                                       });
                                       return result;
                                    }

                                    version = (Version)var10.next();
                                 } while(version.getType() == null);
                              } while(version.getUpdatedTime() == null);

                              type = (MinecraftReleaseType)version.getType();
                           } while(((Map)lookup).containsKey(version.getId()));
                        } while(filter != null && (!filter.getTypes().contains(type) || (Integer)counts.get(type) >= filter.getMaxCount()));

                        syncInfo = this.getVersionSyncInfo(this.localVersionList.getVersion(version.getId()), version);
                        ((Map)lookup).put(version.getId(), syncInfo);
                        result.add(syncInfo);
                        if (filter != null) {
                           counts.put(type, (Integer)counts.get(type) + 1);
                        }
                     }
                  }

                  version = (Version)var10.next();
               } while(version.getType() == null);
            } while(version.getUpdatedTime() == null);

            type = (MinecraftReleaseType)version.getType();
         } while(filter != null && (!filter.getTypes().contains(type) || (Integer)counts.get(type) >= filter.getMaxCount()));

         syncInfo = this.getVersionSyncInfo(version, this.remoteVersionList.getVersion(version.getId()));
         ((Map)lookup).put(version.getId(), syncInfo);
         result.add(syncInfo);
      }
   }

   public VersionSyncInfo getVersionSyncInfo(Version version) {
      return this.getVersionSyncInfo(version.getId());
   }

   public VersionSyncInfo getVersionSyncInfo(String name) {
      return this.getVersionSyncInfo(this.localVersionList.getVersion(name), this.remoteVersionList.getVersion(name));
   }

   public VersionSyncInfo getVersionSyncInfo(Version localVersion, Version remoteVersion) {
      boolean installed = localVersion != null;
      boolean upToDate = installed;
      CompleteMinecraftVersion resolved = null;
      if (installed && remoteVersion != null) {
         upToDate = !remoteVersion.getUpdatedTime().after(localVersion.getUpdatedTime());
      }

      if (localVersion instanceof CompleteVersion) {
         try {
            resolved = ((CompleteMinecraftVersion)localVersion).resolve(this);
         } catch (IOException var7) {
            LOGGER.error((String)("Couldn't resolve version " + localVersion.getId()), (Throwable)var7);
            resolved = (CompleteMinecraftVersion)localVersion;
         }

         upToDate &= this.localVersionList.hasAllFiles(resolved, OperatingSystem.getCurrentPlatform());
      }

      return new VersionSyncInfo(resolved, remoteVersion, installed, upToDate);
   }

   public List<VersionSyncInfo> getInstalledVersions() {
      List<VersionSyncInfo> result = new ArrayList();
      Collection<Version> versions = Lists.newArrayList((Iterable)this.localVersionList.getVersions());
      Iterator var3 = versions.iterator();

      while(var3.hasNext()) {
         Version version = (Version)var3.next();
         if (version.getType() != null && version.getUpdatedTime() != null) {
            VersionSyncInfo syncInfo = this.getVersionSyncInfo(version, this.remoteVersionList.getVersion(version.getId()));
            result.add(syncInfo);
         }
      }

      return result;
   }

   public VersionList getRemoteVersionList() {
      return this.remoteVersionList;
   }

   public VersionList getLocalVersionList() {
      return this.localVersionList;
   }

   public CompleteMinecraftVersion getLatestCompleteVersion(VersionSyncInfo syncInfo) throws IOException {
      if (syncInfo.getLatestSource() == VersionSyncInfo.VersionSource.REMOTE) {
         CompleteMinecraftVersion result = null;
         IOException exception = null;

         try {
            result = this.remoteVersionList.getCompleteVersion(syncInfo.getLatestVersion());
         } catch (IOException var7) {
            exception = var7;

            try {
               result = this.localVersionList.getCompleteVersion(syncInfo.getLatestVersion());
            } catch (IOException var6) {
            }
         }

         if (result != null) {
            return result;
         } else {
            throw exception;
         }
      } else {
         return this.localVersionList.getCompleteVersion(syncInfo.getLatestVersion());
      }
   }

   public DownloadJob downloadVersion(VersionSyncInfo syncInfo, DownloadJob job) throws IOException {
      if (!(this.localVersionList instanceof LocalVersionList)) {
         throw new IllegalArgumentException("Cannot download if local repo isn't a LocalVersionList");
      } else if (!(this.remoteVersionList instanceof RemoteVersionList)) {
         throw new IllegalArgumentException("Cannot download if local repo isn't a RemoteVersionList");
      } else {
         CompleteMinecraftVersion version = this.getLatestCompleteVersion(syncInfo);
         File baseDirectory = ((LocalVersionList)this.localVersionList).getBaseDirectory();
         Proxy proxy = ((RemoteVersionList)this.remoteVersionList).getProxy();
         job.addDownloadables((Collection)version.getRequiredDownloadables(OperatingSystem.getCurrentPlatform(), proxy, baseDirectory, false));
         String jarFile = "versions/" + version.getJar() + "/" + version.getJar() + ".jar";
         DownloadInfo clientInfo = version.getDownloadURL(DownloadType.CLIENT);
         if (clientInfo == null) {
            job.addDownloadables(new EtagDownloadable(proxy, new URL("https://s3.amazonaws.com/Minecraft.Download/" + jarFile), new File(baseDirectory, jarFile), false));
         } else {
            job.addDownloadables(new PreHashedDownloadable(proxy, clientInfo.getUrl(), new File(baseDirectory, jarFile), false, clientInfo.getSha1()));
         }

         return job;
      }
   }

   public DownloadJob downloadResources(DownloadJob job, CompleteVersion version) throws IOException {
      File baseDirectory = ((LocalVersionList)this.localVersionList).getBaseDirectory();
      job.addDownloadables((Collection)this.getResourceFiles(((RemoteVersionList)this.remoteVersionList).getProxy(), baseDirectory, (CompleteMinecraftVersion)version));
      return job;
   }

   private Set<Downloadable> getResourceFiles(Proxy proxy, File baseDirectory, CompleteMinecraftVersion version) {
      Set<Downloadable> result = new HashSet();
      InputStream inputStream = null;
      File assets = new File(baseDirectory, "assets");
      File objectsFolder = new File(assets, "objects");
      File indexesFolder = new File(assets, "indexes");
      long start = System.nanoTime();
      AssetIndexInfo indexInfo = version.getAssetIndex();
      File indexFile = new File(indexesFolder, indexInfo.getId() + ".json");

      try {
         URL indexUrl = indexInfo.getUrl();
         inputStream = indexUrl.openConnection(proxy).getInputStream();
         String json = IOUtils.toString(inputStream);
         FileUtils.writeStringToFile(indexFile, json);
         AssetIndex index = (AssetIndex)this.gson.fromJson(json, AssetIndex.class);
         Iterator var16 = index.getUniqueObjects().entrySet().iterator();

         while(true) {
            Entry entry;
            AssetIndex.AssetObject object;
            File file;
            do {
               if (!var16.hasNext()) {
                  long end = System.nanoTime();
                  long delta = end - start;
                  LOGGER.debug("Delta time to compare resources: " + delta / 1000000L + " ms ");
                  return result;
               }

               entry = (Entry)var16.next();
               object = (AssetIndex.AssetObject)entry.getKey();
               String filename = object.getHash().substring(0, 2) + "/" + object.getHash();
               file = new File(objectsFolder, filename);
            } while(file.isFile() && FileUtils.sizeOf(file) == object.getSize());

            Downloadable downloadable = new AssetDownloadable(proxy, (String)entry.getValue(), object, "http://resources.download.minecraft.net/", objectsFolder);
            downloadable.setExpectedSize(object.getSize());
            result.add(downloadable);
         }
      } catch (Exception var25) {
         LOGGER.error((String)"Couldn't download resources", (Throwable)var25);
      } finally {
         IOUtils.closeQuietly(inputStream);
      }

      return result;
   }

   public ThreadPoolExecutor getExecutorService() {
      return this.executorService;
   }

   public void addRefreshedVersionsListener(RefreshedVersionsListener listener) {
      this.refreshedVersionsListeners.add(listener);
   }

   public void removeRefreshedVersionsListener(RefreshedVersionsListener listener) {
      this.refreshedVersionsListeners.remove(listener);
   }

   public VersionSyncInfo syncVersion(VersionSyncInfo syncInfo) throws IOException {
      CompleteVersion remoteVersion = this.getRemoteVersionList().getCompleteVersion(syncInfo.getRemoteVersion());
      this.getLocalVersionList().removeVersion(syncInfo.getLocalVersion());
      this.getLocalVersionList().addVersion(remoteVersion);
      ((LocalVersionList)this.getLocalVersionList()).saveVersion(((CompleteMinecraftVersion)remoteVersion).getSavableVersion());
      return this.getVersionSyncInfo((Version)remoteVersion);
   }

   public void installVersion(CompleteVersion version) throws IOException {
      if (version instanceof CompleteMinecraftVersion) {
         version = ((CompleteMinecraftVersion)version).getSavableVersion();
      }

      VersionList localVersionList = this.getLocalVersionList();
      if (localVersionList.getVersion(((CompleteVersion)version).getId()) != null) {
         localVersionList.removeVersion(((CompleteVersion)version).getId());
      }

      localVersionList.addVersion((CompleteVersion)version);
      if (localVersionList instanceof LocalVersionList) {
         ((LocalVersionList)localVersionList).saveVersion((CompleteVersion)version);
      }

      LOGGER.info("Installed " + version);
   }

   public void uninstallVersion(CompleteVersion version) throws IOException {
      VersionList localVersionList = this.getLocalVersionList();
      if (localVersionList instanceof LocalVersionList) {
         localVersionList.uninstallVersion(version);
         LOGGER.info("Uninstalled " + version);
      }

   }
}
