using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using static Vanara.PInvoke.BITS;
namespace Vanara.IO
{
/// Information about a file in a background copy job.
public class BackgroundCopyFileInfo
{
internal BG_FILE_INFO fi;
private IBackgroundCopyFile iFile;
internal BackgroundCopyFileInfo(IBackgroundCopyFile ibgfile)
{
iFile = ibgfile;
}
internal BackgroundCopyFileInfo(BG_FILE_INFO bgfi)
{
fi = bgfi;
}
/// Retrieves the ranges that you want to download from the remote file.
/// The file copy ranges.
public IEnumerable FileRanges
{
get
{
IFile2.GetFileRanges(out var cnt, out var rng);
return rng.ToEnumerable((int)cnt);
}
}
/// Returns the set of file ranges that have been downloaded.
/// The file copy ranges that have been downloaded. Ranges will be merged together as much as possible. The ranges are ordered by offset.
public IEnumerable FilledFileRanges
{
get
{
IFile6.GetFilledFileRanges(out var cnt, out var rng);
return rng.ToEnumerable((int)cnt);
}
}
/// Gets or sets a value indicating whether the contents of the file are valid.
/// true if the file content is valid; otherwise, false.
public bool IsFileContentValid { get => IFile3.GetValidationState(); set => IFile3.SetValidationState(value); }
///
/// Size of the file in bytes. If the value is -1, the total size of the file has not been determined. BITS does not set this value if it cannot
/// determine the size of the file. For example, if the specified file or server does not exist, BITS cannot determine the size of the file. If you are
/// downloading ranges from a file, BytesTotal reflects the total number of bytes you want to download from the file.
///
public long Length => (long)CopyProgress.BytesTotal;
/// Retrieves the local name of the file.
public string LocalFilePath
{
get => iFile == null ? fi.LocalName : iFile.GetLocalName();
set
{
if (iFile != null)
throw new InvalidOperationException("You cannot change the LocalFilePath property on CurrentFileSet results.");
fi.LocalName = value;
}
}
/// Gets the amount of file data downloaded from the originating server.
public ulong OriginServerDownloadBytes { get { IFile4.GetPeerDownloadStats(out var o, out var _); return o; } }
/// Gets the amount of file data downloaded from a peer-to-peer source.
public ulong PeerDownloadBytes { get { IFile4.GetPeerDownloadStats(out var _, out var p); return p; } }
/// Gets the percentage of the transfer that has completed.
public float PercentComplete
{
get
{
var p = CopyProgress;
return p.Completed || p.BytesTotal == 0 ? 100.0F : p.BytesTransferred * 100.0F / p.BytesTotal;
}
}
/// Retrieves the remote name of the file.
public string RemoteFilePath
{
get => iFile == null ? fi.RemoteName : iFile.GetRemoteName();
set
{
fi.RemoteName = value;
(iFile as IBackgroundCopyFile2)?.SetRemoteName(value);
}
}
public string ResponseHeaders
{
get
{
var hdr = IFile5.GetProperty(BITS_FILE_PROPERTY_ID.BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS);
return hdr.String;
}
set
{
var hdr = new BITS_FILE_PROPERTY_VALUE { String = value };
IFile5.SetProperty(BITS_FILE_PROPERTY_ID.BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS, hdr);
}
}
/// Gets the full path of the temporary file that contains the content of the download.
public string TemporaryName => IFile3.GetTemporaryName();
///
/// For downloads, the value is TRUE if the file is available to the user; otherwise, the value is FALSE. Files are available to the user after calling
/// the method. If the method generates a transient error, those files
/// processed before the error occurred are available to the user; the others are not. Use the Completed property to determine if the file is available
/// to the user when Complete fails. For uploads, the value is TRUE when the file upload is complete; otherwise, the value is FALSE.
///
public bool TransferCompleted => CopyProgress.Completed;
/// Gets the number of bytes transferred.
public long BytesTransferred => (long)CopyProgress.BytesTransferred;
/// Gets a value that determines if any part of the file was downloaded from a peer.
/// Is TRUE if any part of the file was downloaded from a peer; otherwise, FALSE.
public bool IsDownloadedFromPeer => IFile3.IsDownloadedFromPeer();
/// Retrieves the progress of the file transfer.
internal BG_FILE_PROGRESS CopyProgress => iFile?.GetProgress() ?? throw new InvalidOperationException("You can only get the CopyProgress on CurrentFileSet results.");
private IBackgroundCopyFile2 IFile2 => GetDerived();
private IBackgroundCopyFile3 IFile3 => GetDerived();
private IBackgroundCopyFile4 IFile4 => GetDerived();
private IBackgroundCopyFile5 IFile5 => GetDerived();
private IBackgroundCopyFile6 IFile6 => GetDerived();
/// Adds a new set of file ranges to be prioritized for download.
///
/// An array of file ranges to be downloaded. Requested ranges are allowed to overlap previously downloaded (or pending) ranges. Ranges are automatically
/// split into non-overlapping ranges.
///
public void RequestFileRanges(BackgroundCopyFileRange[] ranges)
{
IFile6.RequestFileRanges((uint)ranges.Length, Array.ConvertAll(ranges, r => r.fr));
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString() => $"{LocalFilePath} => {RemoteFilePath}";
/// Specifies a position to prioritize downloading missing data from.
/// Specifies the new position to prioritize downloading missing data from.
public void UpdateDownloadPosition(ulong offset) => IFile6.UpdateDownloadPosition(offset);
private T GetDerived() where T : class
{
T ret = iFile as T;
return ret ?? throw new PlatformNotSupportedException();
}
}
/// Identifies a range of bytes to download from a file.
[StructLayout(LayoutKind.Sequential)]
public class BackgroundCopyFileRange
{
internal BG_FILE_RANGE fr;
/// Zero-based offset to the beginning of the range of bytes to download from a file.
public ulong InitialOffset { get => fr.InitialOffset; set => fr.InitialOffset = value; }
///
/// The length of the range, in bytes. Do not specify a zero byte length. To indicate that the range extends to the end of the file, specify BG_LENGTH_TO_EOF.
///
public ulong Length { get => fr.Length; set => fr.Length = value; }
/// Performs an implicit conversion from to .
/// The BG_FILE_RANGE instance.
/// The result of the conversion.
public static implicit operator BackgroundCopyFileRange(BG_FILE_RANGE p) => new BackgroundCopyFileRange { fr = p };
}
}