/*
 * ForgeGradle
 * Copyright (C) 2018 Forge Development LLC
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 * USA
 */

package net.minecraftforge.gradle.common.task;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.TaskAction;

import net.minecraftforge.gradle.common.util.Utils;
import net.minecraftforge.gradle.common.util.VersionJson;

public class DownloadAssets extends DefaultTask {
    private static final String RESOURCE_REPO = "http://resources.download.minecraft.net/";
    private File meta;

    @TaskAction
    public void run() throws IOException {
        AssetIndex index = Utils.loadJson(getIndex(), AssetIndex.class);
        List<String> keys = new ArrayList<>(index.objects.keySet());
        Collections.sort(keys);
        for (String key : keys) {
            Asset asset = index.objects.get(key);
            File target = Utils.getCache(getProject(), "assets", "objects", asset.getPath());
            if (!target.exists()) {
                URL url = new URL(RESOURCE_REPO + asset.getPath());
                getProject().getLogger().lifecycle("Downloading: " + url + " Asset: " + key);
                FileUtils.copyURLToFile(url, target);
            }
        }
    }

    private File getIndex() throws IOException {
        VersionJson json = Utils.loadJson(getMeta(), VersionJson.class);
        File target = Utils.getCache(getProject(), "assets" , "indexes", json.assetIndex.id + ".json");
        return Utils.updateDownload(getProject(), target, json.assetIndex);
    }

    @InputFile
    public File getMeta() {
        return this.meta;
    }
    public void setMeta(File value) {
        this.meta = value;
    }

    public File getOutput() {
        return Utils.getCache(getProject(), "assets");
    }

    private static class AssetIndex {
        Map<String, Asset> objects;
    }
    private static class Asset {
        String hash;

        public String getPath() {
            return hash.substring(0, 2) + '/' + hash;
        }
    }
}

@@ -865,438 +865,609 @@
 ort 
-java.io.File;%0Aimport java.io.IOException;%0Aimport java.net.URL;%0Aimport java.util.ArrayList;%0Aimport java.util.Collections;%0Aimport java.util.List;%0Aimport java.util.Map;%0A%0Aimport org.apache.commons.io.FileUtils;%0Aimport org.gradle.api.DefaultTask;%0Aimport org.gradle.api.tasks.InputFile;%0Aimport org.gradle.api.tasks.TaskAction;%0A%0Aimport net.minecraftforge.gradle.common.util.Utils;%0Aimport net.minecraftforge.gradle.common.util.VersionJson
+net.minecraftforge.gradle.common.util.Utils;%0Aimport net.minecraftforge.gradle.common.util.VersionJson;%0Aimport org.apache.commons.io.FileUtils;%0Aimport org.gradle.api.DefaultTask;%0Aimport org.gradle.api.tasks.InputFile;%0Aimport org.gradle.api.tasks.TaskAction;%0A%0Aimport java.io.File;%0Aimport java.io.IOException;%0Aimport java.net.URL;%0Aimport java.util.ArrayList;%0Aimport java.util.Collections;%0Aimport java.util.List;%0Aimport java.util.Map;%0Aimport java.util.concurrent.CopyOnWriteArrayList;%0Aimport java.util.concurrent.ExecutorService;%0Aimport java.util.concurrent.Executors;%0Aimport java.util.concurrent.TimeUnit
 ;%0A%0Ap
@@ -1679,32 +1679,54 @@
 rows IOException
+, InterruptedException
  %7B%0A        Asset
@@ -1882,24 +1882,190 @@
 sort(keys);%0A
+        ExecutorService executorService = Executors.newFixedThreadPool(16);%0A        CopyOnWriteArrayList%3CString%3E downloadingFailedURL = new CopyOnWriteArrayList%3C%3E();%0A
         for 
@@ -2326,32 +2326,115 @@
 set.getPath());%0A
+                Runnable copyURLtoFile = () -%3E %7B%0A                    try %7B%0A        
                 
@@ -2523,24 +2523,32 @@
             
+        
 FileUtils.co
@@ -2574,24 +2574,939 @@
 rget
-);%0A            %7D
+, 10_000, 5_000);%0A%0A                    %7D catch (IOException e) %7B%0A                        downloadingFailedURL.add(key);%0A                        getProject().getLogger().error(%22%7B%7D downloading fails.%22, key);%0A                        e.printStackTrace();%0A                    %7D%0A                %7D;%0A                executorService.execute(copyURLtoFile);%0A            %7D%0A        %7D%0A        executorService.shutdown();%0A        executorService.awaitTermination(8, TimeUnit.HOURS);%0A        if (!downloadingFailedURL.isEmpty()) %7B%0A            String errorMessage = %22%22;%0A            for (String key : downloadingFailedURL) %7B%0A                errorMessage = errorMessage + %22Downloading failed Asset: %22 + key + %22%5Cn%22;%0A            %7D%0A            errorMessage = errorMessage + %22Don't panic. There are just some assets downloading fails, Maybe you should try to run again the task which you just ran.%22;%0A            throw new RuntimeException(errorMessage);
 %0A   
@@ -3700,17 +3700,16 @@
 %22assets%22
- 
 , %22index
@@ -3898,24 +3898,25 @@
 meta;%0A    %7D%0A
+%0A
     public v
@@ -4143,24 +4143,25 @@
 ects;%0A    %7D%0A
+%0A
     private 

Add multithread download for downloadAssets. (#689)