package com.github.eirslett.maven.plugins.frontend.lib;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.eirslett.maven.plugins.frontend.lib.ProxyConfig.Proxy;

final class DownloadException extends Exception {
    public DownloadException(String message){
        super(message);
    }
    DownloadException(String message, Throwable cause) {
        super(message, cause);
    }
}

interface FileDownloader {
    void download(String downloadUrl, String destination, String userName, String password) throws DownloadException;
}

final class DefaultFileDownloader implements FileDownloader {
    private static final Logger LOGGER = LoggerFactory.getLogger(FileDownloader.class);

    private final ProxyConfig proxyConfig;

    public DefaultFileDownloader(ProxyConfig proxyConfig){
        this.proxyConfig = proxyConfig;
    }

    public void download(String downloadUrl, String destination, String userName, String password) throws DownloadException {
        String fixedDownloadUrl = downloadUrl;
        try {
            fixedDownloadUrl = FilenameUtils.separatorsToUnix(fixedDownloadUrl);
            URI downloadURI = new URI(fixedDownloadUrl);
            if ("file".equalsIgnoreCase(downloadURI.getScheme())) {
                FileUtils.copyFile(new File(downloadURI), new File(destination));
            }
            else {
                CloseableHttpResponse response = execute(fixedDownloadUrl, userName, password);
                int statusCode = response.getStatusLine().getStatusCode();
                if(statusCode != 200){
                    throw new DownloadException("Got error code "+ statusCode +" from the server.");
                }
                new File(FilenameUtils.getFullPathNoEndSeparator(destination)).mkdirs();
                ReadableByteChannel rbc = Channels.newChannel(response.getEntity().getContent());
                FileOutputStream fos = new FileOutputStream(destination);
                fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
                fos.close();
            }
        } catch (IOException e) {
            throw new DownloadException("Could not download "+fixedDownloadUrl, e);
        }
        catch (URISyntaxException e) {
            throw new DownloadException("Could not download "+fixedDownloadUrl, e);
        }
    }

    private CloseableHttpResponse execute(String requestUrl, String userName, String password) throws IOException {
        CloseableHttpResponse response;
        Proxy proxy = proxyConfig.getProxyForUrl(requestUrl);
        if (proxy != null) {
            LOGGER.info("Downloading via proxy " + proxy.toString());
            return executeViaProxy(proxy, requestUrl);
        } else {
            LOGGER.info("No proxy was configured, downloading directly");
            if (userName != null && !"".equals(userName) && password != null && !"".equals(password)) {
                CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                credentialsProvider.setCredentials(
                        new AuthScope(proxy.host, proxy.port),
                        new UsernamePasswordCredentials(proxy.username, proxy.password)
                );
                response = buildHttpClient(credentialsProvider).execute(new HttpGet(requestUrl));
            } else {
                response = buildHttpClient(null).execute(new HttpGet(requestUrl));
            }
        }
        return response;
    }

    private CloseableHttpResponse executeViaProxy(Proxy proxy, String requestUrl) throws IOException {
        final CloseableHttpClient proxyClient;
        if (proxy.useAuthentication()){
            CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(
                    new AuthScope(proxy.host, proxy.port),
                    new UsernamePasswordCredentials(proxy.username, proxy.password)
            );
            proxyClient = buildHttpClient(credentialsProvider);
        } else {
            proxyClient = buildHttpClient(null);
        }

        final HttpHost proxyHttpHost = new HttpHost(proxy.host, proxy.port);

        final RequestConfig requestConfig = RequestConfig.custom().setProxy(proxyHttpHost).build();

        final HttpGet request = new HttpGet(requestUrl);
        request.setConfig(requestConfig);

        return proxyClient.execute(request);
    }

    private CloseableHttpClient buildHttpClient(CredentialsProvider credentialsProvider) {
    	return HttpClients.custom()
    			.disableContentCompression()
    			.useSystemProperties()
    			.setDefaultCredentialsProvider(credentialsProvider)
    			.build();
    }

}

@@ -181,32 +181,53 @@
 yntaxException;%0A
+import java.net.URL;%0A
 import java.nio.
@@ -241,24 +241,24 @@
 s.Channels;%0A
-
 import java.
@@ -501,24 +501,65 @@
 redentials;%0A
+import org.apache.http.client.AuthCache;%0A
 import org.a
@@ -710,16 +710,16 @@
 sponse;%0A
-
 import o
@@ -757,16 +757,171 @@
 ttpGet;%0A
+import org.apache.http.client.protocol.HttpClientContext;%0Aimport org.apache.http.impl.auth.BasicScheme;%0Aimport org.apache.http.impl.client.BasicAuthCache;%0A
 import o
@@ -3924,24 +3924,782 @@
 assword)) %7B%0A
+                LOGGER.info(%22Using credentials (%22 + userName + %22) from settings.xml%22);%0A                // Auth target host%0A                URL aURL = new URL(requestUrl);%0A                HttpHost target = new HttpHost (aURL.getHost(), aURL.getPort(), aURL.getProtocol());%0A                // Create AuthCache instance%0A                AuthCache authCache = new BasicAuthCache();%0A                // Generate BASIC scheme object and add it to the local auth cache%0A                BasicScheme basicAuth = new BasicScheme();%0A                authCache.put(target, basicAuth);%0A                // Add AuthCache to the execution context%0A                HttpClientContext localContext = HttpClientContext.create();%0A                localContext.setAuthCache(authCache);%0A
             
@@ -4866,38 +4866,46 @@
 thScope(
-proxy.host, proxy.p
+aURL.getHost(), aURL.getP
 ort
+()
 ),%0A     
@@ -4955,38 +4955,26 @@
 entials(
-proxy.
 user
-n
+N
 ame, 
-proxy.
 password
@@ -5081,32 +5081,45 @@
 pGet(requestUrl)
+,localContext
 );%0A            %7D

Use preemptive authentication.