import java.util.List; public class ProgressMonitor implements Runnable { private final String fileName; private final int totalSizeMB; private final List chunks; private final long monitorDelayMs; private double previousTotalDownloadedMB; private long previousTimeMs; public ProgressMonitor(DownloadConfig config, List chunks) { this.fileName = config.getFileName(); this.totalSizeMB = config.getTotalSizeMB(); this.chunks = chunks; this.monitorDelayMs = 500; this.previousTotalDownloadedMB = 0.0; this.previousTimeMs = System.currentTimeMillis(); } @Override public void run() { // Repeatedly check chunk progress until all chunks are completed. // In each loop: // 1. Read the downloaded size from every chunk // 2. Add all downloaded amounts to totalDownloadedMB // 3. Count completed chunks // 4. Print a progress message // 5. If completedChunks == chunks.size(), print a final monitor message and stop // 6. Otherwise, sleep for monitorDelayMs and continue while (true) { double totalDownloadedMB = 0.0; int completedChunks = 0; for (ChunkStatus chunk : chunks) { totalDownloadedMB += chunk.getDownloadedMB(); if (chunk.isCompleted()) { completedChunks++; } } double percent = 0.0; if (totalSizeMB > 0) { percent = (totalDownloadedMB * 100.0) / totalSizeMB; } long currentTimeMs = System.currentTimeMillis(); long timeDiffMs = currentTimeMs - previousTimeMs; double downloadedDiff = totalDownloadedMB - previousTotalDownloadedMB; double speedMBps = 0.0; if (timeDiffMs > 0){ speedMBps = (downloadedDiff * 1000.0) / timeDiffMs; } double etaSeconds = 0.0; if (speedMBps > 0 && totalDownloadedMB < totalSizeMB){ double remainingMB =totalSizeMB - totalDownloadedMB; etaSeconds = remainingMB / speedMBps; } previousTotalDownloadedMB = totalDownloadedMB; previousTimeMs = currentTimeMs; int barWidth = 50; int filledWidth = (int)(barWidth * percent / 100.0); System.out.print("["); for (int i = 0; i < barWidth; i++) { if (i < filledWidth){ System.out.print("="); }else if (i == filledWidth && percent< 100){ System.out.print(">"); }else { System.out.print(" "); } } System.out.print("]"); System.out.printf( "Progress for %s: %.1f/%.1f MB (%.2f%%), completed chunks: %d/%d%n , ETA: %s", fileName, totalDownloadedMB, (double) totalSizeMB, percent, completedChunks, chunks.size(), speedMBps, formatETA(etaSeconds) ); if (completedChunks == chunks.size()){ System.out.println("All chunks have been downloaded successfully!"); break; } try { Thread.sleep(monitorDelayMs); } catch (InterruptedException e) { System.out.println("Progress monitor interrupted."); return; } } } private String formatETA(double etaSeconds){ if (etaSeconds <= 0){ return "Calculating ..."; } int hours = (int)(etaSeconds/3600); int minutes = (int)((etaSeconds%3600)/60); int secondes = (int)(etaSeconds %60); if (hours > 0){ return String.format("%dh %dm %ds" , hours, minutes, secondes); }else if(minutes > 0){ return String.format("%dm %ds" , minutes, secondes); }else{ return String.format("%ds", secondes); } } }