//  
//   Rebex Sample Code License
// 
//   Copyright 2026, REBEX CR s.r.o.
//   All rights reserved.
//   https://www.rebex.net/
// 
//   Permission to use, copy, modify, and/or distribute this software for any
//   purpose with or without fee is hereby granted.
// 
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//   OTHER DEALINGS IN THE SOFTWARE.
// 

using System;
using System.Collections.Concurrent;

namespace Rebex.Samples
{
    /// <summary>
    /// Simple implementation of <see cref="IProgress{T}"/> interface.
    /// Used for tracking progress of a download operation.
    /// </summary>
    public class ConsoleDownloadProgress : IProgress<DownloadProgressInfo>
    {
        private readonly BlockingCollection<DownloadProgressInfo> _messageCollection;
        private const int CALCULATE_SPEED_INTERVAL_IN_SECONDS = 1;

        private string _progressLine;
        private DownloadProgressInfo _previousProgress;
        private DateTime _lastSpeedCalculationTime = DateTime.MinValue;
        private double _lastSpeed = DownloadProgressInfo.UNKNOWN_VALUE;
        private string _deleteLine;

        public ConsoleDownloadProgress(BlockingCollection<DownloadProgressInfo> messageCollection)
        {
            _messageCollection = messageCollection ?? throw new ArgumentNullException(nameof(messageCollection));
            _progressLine = _deleteLine = string.Empty;
            _previousProgress = new DownloadProgressInfo(null, 0, string.Empty, TimeSpan.Zero);
        }

        public void Report(DownloadProgressInfo value)
        {
            _messageCollection.Add(value);
        }

        public void WriteCollection()
        {
            foreach (var downloadProgressData in _messageCollection.GetConsumingEnumerable())
            {
                WriteDataToConsole(downloadProgressData);
            }
        }

        private void WriteDataToConsole(DownloadProgressInfo progressInfo)
        {
            if (Console.IsOutputRedirected)
            {
                return;
            }

            Console.Write(_deleteLine);

            _progressLine = FormattableString.Invariant($"{progressInfo.FileName}: {progressInfo.TotalLengthInMB:00.00;--} MB  Downloaded: {progressInfo.DownloadedBytesInMB:0.00} MB {progressInfo.PercentsDownloaded:00%;--} Speed: {CalculateSpeed(ref _previousProgress, ref progressInfo):0.00;--} MB/s Time: {progressInfo.ElapsedTime.TotalSeconds:00.00} s");

            Console.Write(_progressLine);

            if (_deleteLine.Length != _progressLine.Length)
            {
                _deleteLine = new string('\b', _progressLine.Length);
            }
        }

        private double CalculateSpeed(ref DownloadProgressInfo previousProgress, ref DownloadProgressInfo currentProgress)
        {
            if (currentProgress.TotalLength == currentProgress.DownloadedBytes)
            {
                return (currentProgress.DownloadedBytes / currentProgress.ElapsedTime.TotalSeconds) / DownloadProgressInfo.BYTES_IN_MEGABYTE;
            }

            if ((DateTime.UtcNow - _lastSpeedCalculationTime).TotalSeconds < CALCULATE_SPEED_INTERVAL_IN_SECONDS)
            {
                return _lastSpeed;
            }

            _lastSpeedCalculationTime = DateTime.UtcNow;
            long bytesDelta = currentProgress.DownloadedBytes - previousProgress.DownloadedBytes;
            double timeDelta = currentProgress.ElapsedTime.TotalSeconds - previousProgress.ElapsedTime.TotalSeconds;
            _lastSpeed = (bytesDelta / timeDelta) / DownloadProgressInfo.BYTES_IN_MEGABYTE;
            _previousProgress = currentProgress;

            return _lastSpeed;
        }
    }
}
