using System;
using System.Management.Automation;
namespace PSMinIO.Utils
{
///
/// Multi-layer progress reporter for 3-level progress tracking:
/// Collection progress (overall files) > File progress (current file) > Transfer progress (bytes)
///
public class MultiLayerProgressReporter
{
private readonly PSCmdlet _cmdlet;
private readonly ThreadSafeProgressCollector _progressCollector;
// Activity IDs for the three layers
private const int CollectionActivityId = 1;
private const int FileActivityId = 2;
private const int TransferActivityId = 3;
// Collection-level tracking
private int _totalFiles;
private int _currentFileIndex;
private string _collectionOperation = "Processing Files";
// File-level tracking
private string _currentFileName = string.Empty;
private long _currentFileSize;
private long _currentFileBytesTransferred;
// Transfer-level tracking
private long _totalBytesTransferred;
private DateTime _startTime;
public MultiLayerProgressReporter(PSCmdlet cmdlet, int totalFiles, string operation = "Processing Files")
{
_cmdlet = cmdlet ?? throw new ArgumentNullException(nameof(cmdlet));
_progressCollector = new ThreadSafeProgressCollector(cmdlet);
_totalFiles = totalFiles;
_collectionOperation = operation;
_startTime = DateTime.UtcNow;
// Initialize collection progress
UpdateCollectionProgress();
}
///
/// Starts processing a new file
///
/// Name of the file being processed
/// Size of the file in bytes
public void StartNewFile(string fileName, long fileSize)
{
_currentFileIndex++;
_currentFileName = fileName;
_currentFileSize = fileSize;
_currentFileBytesTransferred = 0;
// Update collection progress
UpdateCollectionProgress();
// Initialize file progress
UpdateFileProgress();
// Initialize transfer progress
UpdateTransferProgress();
// Process any queued updates
_progressCollector.ProcessQueuedUpdates();
}
///
/// Updates transfer progress for the current file
///
/// Bytes transferred for current file
public void UpdateTransferProgress(long bytesTransferred)
{
_currentFileBytesTransferred = bytesTransferred;
UpdateTransferProgress();
}
///
/// Completes the current file and updates overall progress
///
public void CompleteCurrentFile()
{
_totalBytesTransferred += _currentFileSize;
// Complete transfer progress
_progressCollector.QueueProgressCompletion(TransferActivityId, "Transfer Progress", FileActivityId);
// Complete file progress
_progressCollector.QueueProgressCompletion(FileActivityId, "File Progress", CollectionActivityId);
// Update collection progress
UpdateCollectionProgress();
// Process updates
_progressCollector.ProcessQueuedUpdates();
}
///
/// Completes all progress reporting
///
public void Complete()
{
// Complete collection progress
_progressCollector.QueueProgressCompletion(CollectionActivityId, _collectionOperation);
// Process final updates and complete
_progressCollector.Complete();
}
///
/// Processes any queued progress updates
///
public void ProcessQueuedUpdates()
{
_progressCollector.ProcessQueuedUpdates();
}
///
/// Updates collection-level progress
///
private void UpdateCollectionProgress()
{
var percentage = _totalFiles > 0 ? (int)((double)_currentFileIndex / _totalFiles * 100) : 0;
var status = $"Processing file {_currentFileIndex} of {_totalFiles}";
if (_currentFileIndex > 0)
{
var elapsed = DateTime.UtcNow - _startTime;
var avgTimePerFile = elapsed.TotalSeconds / _currentFileIndex;
var remainingFiles = _totalFiles - _currentFileIndex;
var estimatedTimeRemaining = TimeSpan.FromSeconds(avgTimePerFile * remainingFiles);
status += $" (ETA: {SizeFormatter.FormatDuration(estimatedTimeRemaining)})";
}
_progressCollector.QueueProgressUpdate(CollectionActivityId, _collectionOperation, status, percentage);
}
///
/// Updates file-level progress
///
private void UpdateFileProgress()
{
var percentage = _currentFileSize > 0 ? (int)((double)_currentFileBytesTransferred / _currentFileSize * 100) : 0;
var status = $"Processing: {_currentFileName} ({SizeFormatter.FormatBytes(_currentFileBytesTransferred)}/{SizeFormatter.FormatBytes(_currentFileSize)})";
_progressCollector.QueueProgressUpdate(FileActivityId, "File Progress", status, percentage, CollectionActivityId);
}
///
/// Updates transfer-level progress
///
private void UpdateTransferProgress()
{
var percentage = _currentFileSize > 0 ? (int)((double)_currentFileBytesTransferred / _currentFileSize * 100) : 0;
var status = $"Transferring: {SizeFormatter.FormatBytes(_currentFileBytesTransferred)}/{SizeFormatter.FormatBytes(_currentFileSize)}";
if (_currentFileBytesTransferred > 0)
{
var elapsed = DateTime.UtcNow - _startTime;
if (elapsed.TotalSeconds > 0)
{
var speed = _currentFileBytesTransferred / elapsed.TotalSeconds;
status += $" at {SizeFormatter.FormatSpeed(speed)}";
}
}
_progressCollector.QueueProgressUpdate(TransferActivityId, "Transfer Progress", status, percentage, FileActivityId);
}
///
/// Queues a verbose message
///
/// Message to log
/// Message arguments
public void QueueVerboseMessage(string message, params object[] args)
{
_progressCollector.QueueVerboseMessage(message, args);
}
///
/// Gets the current collection progress percentage
///
public int CollectionProgressPercentage => _totalFiles > 0 ? (int)((double)_currentFileIndex / _totalFiles * 100) : 0;
///
/// Gets the current file progress percentage
///
public int FileProgressPercentage => _currentFileSize > 0 ? (int)((double)_currentFileBytesTransferred / _currentFileSize * 100) : 0;
///
/// Gets the total bytes transferred across all files
///
public long TotalBytesTransferred => _totalBytesTransferred + _currentFileBytesTransferred;
}
}