package com.testing;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;

import org.apache.hc.client5.http.async.methods.SimpleBody;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.Timeout;
import org.apache.http.client.methods.CloseableHttpResponse;

public class FileDownloadTesting {
    public static void main(String[] args) throws IOException, InterruptedException {
        String url = "https://mmatechnical.com/Download/Download-Test-File/(MMA)-100MB.zip";
        if ((args == null) || (args.length == 0) || (args.length > 1)) {
            System.out.println("Invalid Arguments provided");
            if (args != null) {
                System.out.println(args.length);
                System.out.println(Arrays.toString(args));
            }

            System.out.println("Usage: FileDownloadTesting [Sync | ASync | SyncAsync | Sync4x]");
            System.exit(-1);
        }

        System.out.println(args.length);
        System.out.println(Arrays.toString(args));

        if ("sync".equalsIgnoreCase(args[0])) {
            Files.deleteIfExists(Paths.get("/samplesync.zip"));
            downloadFileSync(url);
        }

        if ("async".equalsIgnoreCase(args[0])) {
            Files.deleteIfExists(Paths.get("/sampleasync.zip"));
            downloadFileASync(url);
        }

        if ("syncasync".equalsIgnoreCase(args[0])) {
            Files.deleteIfExists(Paths.get("/samplesyncasync.zip"));
            downloadFileSyncAsync(url);
        }

        if ("sync4x".equalsIgnoreCase(args[0])) {
            Files.deleteIfExists(Paths.get("/samplesync4x.zip"));
            downloadFileSync4x(url);
        }
    }

    private static void downloadFileSync4x(String location) throws IOException {
        long startTime = System.nanoTime();

        try (org.apache.http.impl.client.CloseableHttpClient httpClient
                = org.apache.http.impl.client.HttpClients.createDefault()) {
            org.apache.http.client.methods.HttpGet httpGet = new org.apache.http.client.methods.HttpGet(location);
            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                int code = response.getStatusLine().getStatusCode();
                if (code == 200) {
                    org.apache.http.HttpEntity entity = response.getEntity();
                    if (entity != null) {
                        try (InputStream inputStream = entity.getContent();
                                FileOutputStream fileOutputStream = new FileOutputStream("C:\\samplesync4x.zip")) {
                            byte[] dataBuffer = new byte[1024];
                            int bytesRead;
                            while ((bytesRead = inputStream.read(dataBuffer)) != -1) {
                                fileOutputStream.write(dataBuffer, 0, bytesRead);
                            }
                        }
                    }

                    org.apache.http.util.EntityUtils.consume(entity);
                }
            }
        }

        long endTime = System.nanoTime(); // Record end time
        long durationInNanos = endTime - startTime;
        long durationInMillis = durationInNanos / 1_000_000; // Convert to milliseconds
        System.out.println(
                "downloadFileSync - Code execution took " + durationInMillis + " ms for downloading " + location);
    }

    private static void downloadFileSync(String location) throws IOException {
        long startTime = System.nanoTime();

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(location);
            httpClient.execute(httpGet, classicHttpResponse -> {
                int code = classicHttpResponse.getCode();
                if (code == 200) {
                    HttpEntity entity = classicHttpResponse.getEntity();
                    if (entity != null) {
                        try (InputStream inputStream = entity.getContent();
                                FileOutputStream fileOutputStream = new FileOutputStream("C:\\samplesync.zip")) {
                            byte[] dataBuffer = new byte[1024];
                            int bytesRead;
                            while ((bytesRead = inputStream.read(dataBuffer)) != -1) {
                                fileOutputStream.write(dataBuffer, 0, bytesRead);
                            }
                        }
                    }

                    EntityUtils.consume(entity);
                }

                return classicHttpResponse;
            });
        }

        long endTime = System.nanoTime(); // Record end time
        long durationInNanos = endTime - startTime;
        long durationInMillis = durationInNanos / 1_000_000; // Convert to milliseconds
        System.out.println(
                "downloadFileSync - Code execution took " + durationInMillis + " ms for downloading " + location);
    }

    private static void downloadFileASync(String location) throws IOException, InterruptedException {
        long startTime = System.nanoTime();

        try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
            httpclient.start();

            SimpleHttpRequest request = SimpleRequestBuilder.get(URI.create(location)).build();
            CountDownLatch latch = new CountDownLatch(1);

            httpclient.execute(request, new FutureCallback<SimpleHttpResponse>() {
                @Override
                public void completed(SimpleHttpResponse response) {
                    try {
                        SimpleBody entity = response.getBody();
                        int returncode = response.getCode();
                        System.out.println("Return code: " + returncode);
                        if (entity != null) {
                            try (FileOutputStream outputStream = new FileOutputStream("C:\\sampleasync.zip")) {
                                outputStream.write(entity.getBodyBytes());
                                System.out.println("File downloaded successfully to: C:\sampleasync.zip");
                            }
                        } else {
                            System.err.println("No content in the response.");
                        }
                    } catch (IOException e) {
                        System.err.println("Error writing file: " + e.getMessage());
                    } finally {
                        latch.countDown();
                    }
                }

                @Override
                public void failed(Exception ex) {
                    System.err.println("Download failed: " + ex.getMessage());
                    latch.countDown();
                }

                @Override
                public void cancelled() {
                    System.out.println("Download cancelled.");
                    latch.countDown();
                }
            });

            latch.await(); // Wait for the download to complete (or fail/cancel)
        }

        long endTime = System.nanoTime(); // Record end time
        long durationInNanos = endTime - startTime;
        long durationInMillis = durationInNanos / 1_000_000; // Convert to milliseconds
        System.out.println(
                "downloadFileASync - Code execution took " + durationInMillis + " ms for downloading " + location);
    }

    private static void downloadFileSyncAsync(String location) throws IOException {
        long startTime = System.nanoTime();

        try (final CloseableHttpClient httpClient
                = HttpAsyncClients.classic(HttpAsyncClients.createDefault(), Timeout.ofMinutes(1))) {
            HttpGet httpGet = new HttpGet(location);
            httpClient.execute(httpGet, classicHttpResponse -> {
                int code = classicHttpResponse.getCode();
                if (code == 200) {
                    HttpEntity entity = classicHttpResponse.getEntity();
                    if (entity != null) {
                        try (InputStream inputStream = entity.getContent();
                                FileOutputStream fileOutputStream = new FileOutputStream("C:\\samplesyncasync.zip")) {
                            byte[] dataBuffer = new byte[1024];
                            int bytesRead;
                            while ((bytesRead = inputStream.read(dataBuffer)) != -1) {
                                fileOutputStream.write(dataBuffer, 0, bytesRead);
                            }
                        }
                    }

                    EntityUtils.consume(entity);
                }

                return classicHttpResponse;
            });
        }

        long endTime = System.nanoTime(); // Record end time
        long durationInNanos = endTime - startTime;
        long durationInMillis = durationInNanos / 1_000_000; // Convert to milliseconds
        System.out.println(
                "downloadFileSyncAsync - Code execution took " + durationInMillis + " ms for downloading " + location);
    }
}
// End of file FileDownloadTesting.java
