using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using FSI.Lib.Tools.RoboSharp.EventArgObjects;
using FSI.Lib.Tools.RoboSharp.Interfaces;
using StatType = FSI.Lib.Tools.RoboSharp.Results.Statistic.StatType;
using System.Collections;
namespace FSI.Lib.Tools.RoboSharp.Results
{
///
/// Object used to represent results from multiple s.
/// As are added to this object, it will update the Totals and Averages accordingly.
/// Implements:
///
///
where T = RoboCopyResults
///
///
///
///
///
public sealed class RoboCopyResultsList : IRoboCopyResultsList, IList, INotifyCollectionChanged
{
#region < Constructors >
///
public RoboCopyResultsList() { InitCollection(null); Init(); }
/// Populate the new List object with this result as the first item.
///
public RoboCopyResultsList(RoboCopyResults result) { ResultsList.Add(result); InitCollection(null); Init(); }
///
public RoboCopyResultsList(IEnumerable collection) { InitCollection(collection); Init(); }
///
/// Clone a RoboCopyResultsList into a new object
///
public RoboCopyResultsList(RoboCopyResultsList resultsList)
{
InitCollection(resultsList);
Total_DirStatsField = GetLazyStat(resultsList.Total_DirStatsField, GetLazyFunc(GetDirectoriesStatistics, StatType.Directories));
Total_FileStatsField = GetLazyStat(resultsList.Total_FileStatsField, GetLazyFunc(GetFilesStatistics, StatType.Files));
Total_ByteStatsField = GetLazyStat(resultsList.Total_ByteStatsField, GetLazyFunc(GetByteStatistics, StatType.Bytes));
Average_SpeedStatsField = GetLazyStat(resultsList.Average_SpeedStatsField, GetLazyAverageSpeedFunc());
ExitStatusSummaryField = GetLazyStat(resultsList.ExitStatusSummaryField, GetLazCombinedStatusFunc());
}
#region < Constructor Helper Methods >
private void InitCollection(IEnumerable collection)
{
ResultsList.AddRange(collection);
ResultsList.CollectionChanged += OnCollectionChanged;
}
private void Init()
{
Total_DirStatsField = new Lazy(GetLazyFunc(GetDirectoriesStatistics, StatType.Directories));
Total_FileStatsField = new Lazy(GetLazyFunc(GetFilesStatistics, StatType.Files));
Total_ByteStatsField = new Lazy(GetLazyFunc(GetByteStatistics, StatType.Bytes));
Average_SpeedStatsField = new Lazy(GetLazyAverageSpeedFunc());
ExitStatusSummaryField = new Lazy(GetLazCombinedStatusFunc());
}
private Func GetLazyFunc(Func Action, StatType StatType) => new Func(() => Statistic.AddStatistics(Action.Invoke(), StatType));
private Func GetLazyAverageSpeedFunc() => new Func(() => AverageSpeedStatistic.GetAverage(GetSpeedStatistics()));
private Func GetLazCombinedStatusFunc() => new Func(() => RoboCopyCombinedExitStatus.CombineStatuses(GetStatuses()));
private Lazy GetLazyStat(Lazy lazyStat, Func action) where T : ICloneable
{
if (lazyStat.IsValueCreated)
{
var clone = lazyStat.Value.Clone();
return new Lazy(() => (T)clone);
}
else
{
return new Lazy(action);
}
}
#endregion
#endregion
#region < Fields >
//These objects are the underlying Objects that may be bound to by consumers.
//The values are updated upon request of the associated property.
//This is so that properties are not returning new objects every request (which would break bindings)
//If the statistic is never requested, then Lazy<> allows the list to skip performing the math against that statistic.
private Lazy Total_DirStatsField;
private Lazy Total_ByteStatsField;
private Lazy Total_FileStatsField;
private Lazy Average_SpeedStatsField;
private Lazy ExitStatusSummaryField;
private readonly ObservableList ResultsList = new ObservableList();
#endregion
#region < Events >
///
/// Delegate for objects to send notification that the list behind an interface has been updated
///
public delegate void ResultsListUpdated(object sender, ResultListUpdatedEventArgs e);
#endregion
#region < Public Properties >
/// Sum of all DirectoryStatistics objects
/// Underlying value is Lazy{Statistic} object - Initial value not calculated until first request.
public IStatistic DirectoriesStatistic => Total_DirStatsField?.Value;
/// Sum of all ByteStatistics objects
/// Underlying value is Lazy{Statistic} object - Initial value not calculated until first request.
public IStatistic BytesStatistic => Total_ByteStatsField?.Value;
/// Sum of all FileStatistics objects
/// Underlying value is Lazy{Statistic} object - Initial value not calculated until first request.
public IStatistic FilesStatistic => Total_FileStatsField?.Value;
/// Average of all SpeedStatistics objects
/// Underlying value is Lazy{SpeedStatistic} object - Initial value not calculated until first request.
public ISpeedStatistic SpeedStatistic => Average_SpeedStatsField?.Value;
/// Sum of all RoboCopyExitStatus objects
/// Underlying value is Lazy object - Initial value not calculated until first request.
public IRoboCopyCombinedExitStatus Status => ExitStatusSummaryField?.Value;
/// The Collection of RoboCopy Results. Add/Removal of objects must be performed through this object's methods, not on the list directly.
public IReadOnlyList Collection => ResultsList;
///
public int Count => ResultsList.Count;
///
/// Get or Set the element at the specified index.
///
/// The zero-based index of the item to Get or Set.
///
public RoboCopyResults this[int index] { get => ResultsList[index]; set => ResultsList[index] = value; }
#endregion
#region < Get Array Methods ( Public ) >
///
/// Get a snapshot of the ByteStatistics objects from this list.
///
/// New array of the ByteStatistic objects
public IStatistic[] GetByteStatistics()
{
List tmp = new List { };
foreach (RoboCopyResults r in this)
tmp.Add(r?.BytesStatistic);
return tmp.ToArray();
}
///
/// Get a snapshot of the DirectoriesStatistic objects from this list.
///
/// New array of the DirectoriesStatistic objects
public IStatistic[] GetDirectoriesStatistics()
{
List tmp = new List { };
foreach (RoboCopyResults r in this)
tmp.Add(r?.DirectoriesStatistic);
return tmp.ToArray();
}
///
/// Get a snapshot of the FilesStatistic objects from this list.
///
/// New array of the FilesStatistic objects
public IStatistic[] GetFilesStatistics()
{
List tmp = new List { };
foreach (RoboCopyResults r in this)
tmp.Add(r?.FilesStatistic);
return tmp.ToArray();
}
///
/// Get a snapshot of the FilesStatistic objects from this list.
///
/// New array of the FilesStatistic objects
public RoboCopyExitStatus[] GetStatuses()
{
List tmp = new List { };
foreach (RoboCopyResults r in this)
tmp.Add(r?.Status);
return tmp.ToArray();
}
///
/// Get a snapshot of the FilesStatistic objects from this list.
///
/// New array of the FilesStatistic objects
public ISpeedStatistic[] GetSpeedStatistics()
{
List tmp = new List { };
foreach (RoboCopyResults r in this)
tmp.Add(r?.SpeedStatistic);
return tmp.ToArray();
}
///
/// Combine the into a single array of errors
///
/// New array of the ErrorEventArgs objects
public ErrorEventArgs[] GetErrors()
{
List tmp = new List { };
foreach (RoboCopyResults r in this)
tmp.AddRange(r?.RoboCopyErrors);
return tmp.ToArray();
}
#endregion
#region < INotifyCollectionChanged >
///
///
///
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// Process the Added/Removed items, then fire the event
///
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Move) goto RaiseEvent; // Sorting causes no change in math -> Simply raise the event
//Reset means a drastic change -> Recalculate everything, then goto RaiseEvent
if (e.Action == NotifyCollectionChangedAction.Reset)
{
//Bytes
if (Total_ByteStatsField.IsValueCreated)
{
Total_ByteStatsField.Value.Reset(false);
Total_ByteStatsField.Value.AddStatistic(GetByteStatistics());
}
//Directories
if (Total_DirStatsField.IsValueCreated)
{
Total_DirStatsField.Value.Reset(false);
Total_DirStatsField.Value.AddStatistic(GetDirectoriesStatistics());
}
//Files
if (Total_FileStatsField.IsValueCreated)
{
Total_FileStatsField.Value.Reset(false);
Total_FileStatsField.Value.AddStatistic(GetFilesStatistics());
}
//Exit Status
if (ExitStatusSummaryField.IsValueCreated)
{
ExitStatusSummaryField.Value.Reset(false);
ExitStatusSummaryField.Value.CombineStatus(GetStatuses());
}
//Speed
if (Average_SpeedStatsField.IsValueCreated)
{
Average_SpeedStatsField.Value.Reset(false);
Average_SpeedStatsField.Value.Average(GetSpeedStatistics());
}
goto RaiseEvent;
}
//Process New Items
if (e.NewItems != null)
{
int i = 0;
int i2 = e.NewItems.Count;
foreach (RoboCopyResults r in e?.NewItems)
{
i++;
bool RaiseValueChangeEvent = (e.OldItems == null || e.OldItems.Count == 0) && (i == i2); //Prevent raising the event if more calculation needs to be performed either from NewItems or from OldItems
//Bytes
if (Total_ByteStatsField.IsValueCreated)
Total_ByteStatsField.Value.AddStatistic(r?.BytesStatistic, RaiseValueChangeEvent);
//Directories
if (Total_DirStatsField.IsValueCreated)
Total_DirStatsField.Value.AddStatistic(r?.DirectoriesStatistic, RaiseValueChangeEvent);
//Files
if (Total_FileStatsField.IsValueCreated)
Total_FileStatsField.Value.AddStatistic(r?.FilesStatistic, RaiseValueChangeEvent);
//Exit Status
if (ExitStatusSummaryField.IsValueCreated)
ExitStatusSummaryField.Value.CombineStatus(r?.Status, RaiseValueChangeEvent);
//Speed
if (Average_SpeedStatsField.IsValueCreated)
{
Average_SpeedStatsField.Value.Add(r?.SpeedStatistic);
if (RaiseValueChangeEvent) Average_SpeedStatsField.Value.CalculateAverage();
}
}
}
//Process Removed Items
if (e.OldItems != null)
{
int i = 0;
int i2 = e.OldItems.Count;
foreach (RoboCopyResults r in e?.OldItems)
{
i++;
bool RaiseValueChangeEvent = i == i2;
//Bytes
if (Total_ByteStatsField.IsValueCreated)
Total_ByteStatsField.Value.Subtract(r?.BytesStatistic, RaiseValueChangeEvent);
//Directories
if (Total_DirStatsField.IsValueCreated)
Total_DirStatsField.Value.Subtract(r?.DirectoriesStatistic, RaiseValueChangeEvent);
//Files
if (Total_FileStatsField.IsValueCreated)
Total_FileStatsField.Value.Subtract(r?.FilesStatistic, RaiseValueChangeEvent);
//Exit Status
if (ExitStatusSummaryField.IsValueCreated && RaiseValueChangeEvent)
{
ExitStatusSummaryField.Value.Reset(false);
ExitStatusSummaryField.Value.CombineStatus(GetStatuses());
}
//Speed
if (Average_SpeedStatsField.IsValueCreated)
{
if (this.Count == 0)
Average_SpeedStatsField.Value.Reset();
else
Average_SpeedStatsField.Value.Subtract(r.SpeedStatistic);
if (RaiseValueChangeEvent) Average_SpeedStatsField.Value.CalculateAverage();
}
}
}
RaiseEvent:
//Raise the CollectionChanged event
CollectionChanged?.Invoke(this, e);
}
#endregion
#region < ICloneable >
/// Clone this object to a new RoboCopyResultsList
public RoboCopyResultsList Clone() => new RoboCopyResultsList(this);
#endregion
#region < IList{T} Implementation >
bool ICollection.IsReadOnly => false;
///
public int IndexOf(RoboCopyResults item) => ResultsList.IndexOf(item);
///
public void Insert(int index, RoboCopyResults item) => ResultsList.Insert(index, item);
///
public void RemoveAt(int index) => ResultsList.RemoveAt(index);
///
public void Add(RoboCopyResults item) => ResultsList.Add(item);
///
public void Clear() => ResultsList.Clear();
///
public bool Contains(RoboCopyResults item) => ResultsList.Contains(item);
///
public void CopyTo(RoboCopyResults[] array, int arrayIndex) => ResultsList.CopyTo(array, arrayIndex);
///
public bool Remove(RoboCopyResults item) => ResultsList.Remove(item);
///
public IEnumerator GetEnumerator() => ResultsList.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ResultsList.GetEnumerator();
#endregion
}
}