using AsyncEvent; using FileTime.Core.Models; using FileTime.Core.Providers; namespace FileTime.Core.Timeline { public class TimeContainer : IContainer { private readonly IContainer? _parent; private readonly PointInTime _pointInTime; public bool IsLoaded => true; public AsyncEventHandler Refreshed { get; } = new AsyncEventHandler(); public string Name { get; } public string? FullName { get; } public bool IsHidden => false; public SupportsDelete CanDelete => SupportsDelete.True; public bool CanRename => true; public IContentProvider Provider { get; } public IContentProvider VirtualProvider { get; } public IReadOnlyList Exceptions { get; } = new List().AsReadOnly(); public bool SupportsDirectoryLevelSoftDelete => false; public bool IsDisposed { get; private set; } public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime) { _parent = parent; _pointInTime = pointInTime; Name = name; Provider = contentProvider; VirtualProvider = virtualContentProvider; FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name; } public async Task Clone() => new TimeContainer(Name, await _parent!.Clone(), Provider, VirtualProvider, _pointInTime); public Task CreateContainer(string name) => Task.FromResult((IContainer)new TimeContainer(name, this, Provider, VirtualProvider, _pointInTime)); public Task CreateElement(string name) => Task.FromResult((IElement)new TimeElement(name, this, Provider, VirtualProvider)); public Task Delete(bool hardDelete = false) => Task.CompletedTask; public async Task GetByPath(string path, bool acceptDeepestMatch = false) { var paths = path.Split(Constants.SeparatorChar); var item = (await GetItems())!.FirstOrDefault(i => i.Name == paths[0]); if (paths.Length == 1) { return item; } if (item is IContainer container) { return await container.GetByPath(string.Join(Constants.SeparatorChar, paths.Skip(1)), acceptDeepestMatch); } return null; } public Task?> GetContainers(CancellationToken token = default) => Task.FromResult( (IReadOnlyList?)_pointInTime .Differences .Where(d => d.Type == DifferenceItemType.Container && GetParentPath(d.AbsolutePath.Path) == FullName) .Select(MapContainer) .ToList() .AsReadOnly() ); public Task?> GetElements(CancellationToken token = default) => Task.FromResult( (IReadOnlyList?)_pointInTime .Differences .Where(d => d.Type == DifferenceItemType.Element && GetParentPath(d.AbsolutePath.Path) == FullName) .Select(MapElement) .ToList() .AsReadOnly() ); public async Task?> GetItems(CancellationToken token = default) { var containers = (await GetContainers(token))!; var elements = (await GetElements(token))!; return containers.Cast().Concat(elements).ToList().AsReadOnly(); } public IContainer? GetParent() => _parent; public async Task IsExists(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false; public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token); public Task Rename(string newName) => Task.CompletedTask; private static string GetParentPath(string path) => string.Join(Constants.SeparatorChar, path.Split(Constants.SeparatorChar).Take(-1)); private IContainer MapContainer(Difference containerDiff) { if (containerDiff.Type != DifferenceItemType.Container) throw new ArgumentException($"{nameof(containerDiff)}'s {nameof(Difference.Type)} property is not {DifferenceItemType.Container}."); return new TimeContainer(containerDiff.Name, this, Provider, containerDiff.AbsolutePath.VirtualContentProvider ?? containerDiff.AbsolutePath.ContentProvider, _pointInTime); } private IElement MapElement(Difference elementDiff) { if (elementDiff.Type != DifferenceItemType.Container) throw new ArgumentException($"{elementDiff}'s {nameof(Difference.Type)} property is not {DifferenceItemType.Element}."); return new TimeElement(elementDiff.Name, this, Provider, elementDiff.AbsolutePath.VirtualContentProvider ?? elementDiff.AbsolutePath.ContentProvider); } public Task CanOpen() => Task.FromResult(true); public void Dispose() => IsDisposed = true; public void Unload() { } } }