Files
FileTime/src/Core/FileTime.Core/Command/CopyCommand.cs
2022-02-13 16:49:59 +01:00

227 lines
9.2 KiB
C#

using AsyncEvent;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Command
{
public class CopyCommand : ITransportationCommand
{
private Func<AbsolutePath, AbsolutePath, OperationProgress?, CopyCommandContext, Task>? _copyOperation;
private Dictionary<AbsolutePath, List<OperationProgress>> _operationStatuses = new();
private Func<IContainer, string, Task<IContainer>>? _createContainer;
private Func<AbsolutePath, Task>? _containerCopyDone;
private OperationProgress? _currentOperationProgress;
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
public IContainer? Target { get; set; }
public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
public int Progress { get; private set; }
public int CurrentProgress { get; private set; }
public AsyncEventHandler ProgressChanged { get; } = new();
public string DisplayLabel { get; } = "Copy";
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
private async Task UpdateProgress()
{
var total = 0L;
var current = 0L;
foreach (var folder in _operationStatuses.Values)
{
foreach (var item in folder)
{
current += item.Progress;
total += item.TotalCount;
}
}
Progress = (int)(current * 100 / total);
if (_currentOperationProgress == null)
{
CurrentProgress = 0;
}
else
{
CurrentProgress = (int)(_currentOperationProgress.Progress * 100 / _currentOperationProgress.TotalCount);
}
await ProgressChanged.InvokeAsync(this, AsyncEventArgs.Empty);
}
public async Task<PointInTime> SimulateCommand(PointInTime startPoint)
{
if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
var newDiffs = new List<Difference>();
_copyOperation = (_, to, _, _) =>
{
var target = to.GetParentAsAbsolutePath().Resolve();
newDiffs.Add(new Difference(
target is IElement
? DifferenceItemType.Element
: DifferenceItemType.Container,
DifferenceActionType.Create,
to
));
return Task.CompletedTask;
};
_createContainer = async (IContainer target, string name) =>
{
var newContainerDiff = new Difference(
DifferenceItemType.Container,
DifferenceActionType.Create,
AbsolutePath.FromParentAndChildName(target, name)
);
newDiffs.Add(newContainerDiff);
return (IContainer)(await newContainerDiff.AbsolutePath.Resolve())!;
};
await TraverseTree(Sources, Target, TransportMode.Value);
return startPoint.WithDifferences(newDiffs);
}
public async Task Execute(Func<AbsolutePath, AbsolutePath, OperationProgress?, CopyCommandContext, Task> copy, TimeRunner timeRunner)
{
if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
await CalculateProgress();
_copyOperation = async (from, to, operation, context) =>
{
await copy(from, to, operation, context);
if (operation != null)
{
operation.Progress = operation.TotalCount;
}
await UpdateProgress();
};
_createContainer = async (IContainer target, string name) => await target.CreateContainer(name);
_containerCopyDone = async (path) =>
{
foreach (var item in _operationStatuses[path])
{
item.Progress = item.TotalCount;
}
if (timeRunner != null)
{
await timeRunner.RefreshContainer.InvokeAsync(this, path);
}
};
await TraverseTree(Sources, Target, TransportMode.Value);
}
private async Task CalculateProgress()
{
if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
var operationStatuses = new Dictionary<AbsolutePath, List<OperationProgress>>();
_copyOperation = async (from, to, _, _) =>
{
var parentPath = to.GetParentAsAbsolutePath();
List<OperationProgress> operationsByFolder;
if (operationStatuses.ContainsKey(parentPath))
{
operationsByFolder = operationStatuses[parentPath];
}
else
{
var resolvedFrom = await from.Resolve();
operationsByFolder = new List<OperationProgress>();
operationStatuses.Add(parentPath, operationsByFolder);
operationsByFolder.Add(new OperationProgress(from.Path, resolvedFrom is IElement element ? await element.GetElementSize() : 0L));
}
};
await TraverseTree(Sources, Target, TransportMode.Value);
_operationStatuses = operationStatuses;
}
private async Task TraverseTree(
IEnumerable<AbsolutePath> sources,
IContainer target,
TransportMode transportMode)
{
if (_copyOperation == null) throw new ArgumentException("No copy operation were given.");
if (_createContainer == null) throw new ArgumentException("No container creation function were given.");
foreach (var source in sources)
{
var item = await source.Resolve();
if (item is IContainer container)
{
var targetContainer = (await target.GetContainers())?.FirstOrDefault(d => d.Name == container.Name) ?? (await _createContainer?.Invoke(target, container.Name)!);
var childDirectories = (await container.GetContainers())!.Select(d => new AbsolutePath(d));
var childFiles = (await container.GetElements())!.Select(f => new AbsolutePath(f));
await TraverseTree(childDirectories.Concat(childFiles), targetContainer, transportMode);
if (_containerCopyDone != null) await _containerCopyDone.Invoke(new AbsolutePath(container));
}
else if (item is IElement element)
{
var targetName = element.Name;
var targetNameExists = await target.IsExists(targetName);
if (transportMode == Command.TransportMode.Merge)
{
for (var i = 0; targetNameExists; i++)
{
targetName = element.Name + (i == 0 ? "_" : $"_{i}");
}
}
else if (transportMode == Command.TransportMode.Skip && targetNameExists)
{
continue;
}
OperationProgress? operation = null;
var targetFolderPath = new AbsolutePath(target);
var targetElementPath = AbsolutePath.FromParentAndChildName(target, targetName);
foreach(var asd in _operationStatuses.Keys)
{
var hash1 = asd.GetHashCode();
var hash2 = targetFolderPath.GetHashCode();
var eq = asd == targetFolderPath;
}
if (_operationStatuses.TryGetValue(targetFolderPath, out var targetPathOperations))
{
var path = new AbsolutePath(element).Path;
operation = targetPathOperations.Find(o => o.Key == path);
}
_currentOperationProgress = operation;
if (_copyOperation != null) await _copyOperation.Invoke(new AbsolutePath(element), targetElementPath, operation, new CopyCommandContext(UpdateProgress));
}
}
}
public Task<CanCommandRun> CanRun(PointInTime startPoint)
{
//TODO: implement
return Task.FromResult(CanCommandRun.True);
}
}
}