using LiteDB;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Tango.BL.ActionLogs;
using Tango.BL.ValueObjects;
using Tango.Core.ExtensionMethods;
namespace Tango.BL
{
///
/// Represents an auto generated observable entity transport object mirror.
///
/// The type of to.
///
///
///
///
public abstract class ObservableEntityDTO : IObservableEntityDTO, IEquatable, IActionLogComparable where T : IObservableEntity where DTO : ObservableEntityDTO
{
///
/// Gets or sets the ID of the entity.
///
public int ID { get; set; }
///
/// Gets or sets the unique identifier of the entity.
///
[BsonId]
public String Guid { get; set; }
///
/// Gets or sets the last updated date of the entity.
///
public DateTime LastUpdated { get; set; }
///
/// Creates an instance of type for the specified entity of type .
///
/// The observable.
///
public static DTO FromObservable(T observable)
{
return FromObservableInternal(observable);
}
///
/// Creates an instance of type for the specified entity of type .
///
/// The observable.
///
public static DTOResult FromObservable(T observable) where DTOResult : DTO
{
return FromObservableInternal(observable);
}
internal static DTOResult FromObservableInternal(T observable) where DTOResult : DTO
{
if (observable == null) return null;
var dto = Activator.CreateInstance();
foreach (var prop in typeof(DTOResult).GetProperties())
{
var observableProp = typeof(T).GetProperty(prop.Name);
if (observableProp != null)
{
if (prop.PropertyType.IsValueTypeOrString() || prop.PropertyType == typeof(byte[]))
{
prop.SetValue(dto, observableProp.GetValue(observable));
}
else if (!prop.PropertyType.IsGenericType)
{
prop.SetValue(dto, prop.PropertyType.GetMethod(nameof(FromObservableInternal), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy).MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { observableProp.GetValue(observable) }));
}
else
{
IList collection = Activator.CreateInstance(prop.PropertyType) as IList;
IList source = observableProp.GetValue(observable) as IList;
foreach (var item in source)
{
collection.Add(prop.PropertyType.GenericTypeArguments[0].GetMethod(nameof(FromObservableInternal), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy).MakeGenericMethod(prop.PropertyType.GenericTypeArguments[0]).Invoke(null, new object[] { item }));
}
prop.SetValue(dto, collection);
}
}
else if (prop.GetCustomAttribute() != null)
{
var att = prop.GetCustomAttribute();
try
{
prop.SetValue(dto, observable.GetPropertyValueByPath(att.MapsTo));
}
catch
{
Debug.WriteLine($"Error mapping '{typeof(DTOResult).Name}.{prop.PropertyType}' to '{typeof(T)}.{att.MapsTo}'.");
}
}
}
dto.OnFromObservableCompleted(observable);
return dto;
}
///
/// Creates an entity of type from this DTO.
///
///
public T ToObservable()
{
T observable = Activator.CreateInstance();
MapToObservable(observable);
return observable;
}
///
/// Maps this instance to an instance of .
///
/// The observable.
public virtual void MapToObservable(T observable)
{
foreach (var prop in GetType().GetProperties())
{
var observableProp = typeof(T).GetProperty(prop.Name);
if (observableProp != null)
{
if (prop.PropertyType.IsValueTypeOrString() || prop.PropertyType == typeof(byte[]))
{
observableProp.SetValue(observable, prop.GetValue(this));
}
else if (!prop.PropertyType.IsGenericType)
{
var propInstance = prop.GetValue(this);
if (propInstance != null)
{
var observableInstance = observableProp.GetValue(observable);
if (observableInstance == null)
{
observableInstance = Activator.CreateInstance(observableProp.PropertyType);
observableProp.SetValue(observable, observableInstance);
}
var method = prop.PropertyType.GetRuntimeMethod(nameof(MapToObservable), new Type[] { observableProp.PropertyType });
method.Invoke(propInstance, new object[] { observableInstance });
}
}
else
{
IList collection = prop.GetValue(this) as IList;
IList observableCollection = observableProp.GetValue(observable) as IList;
if (collection != null)
{
foreach (var item in collection)
{
bool found = false;
foreach (var o in observableCollection)
{
var dtoItem = item as IObservableEntityDTO;
var observableItem = o as IObservableEntity;
if (dtoItem.Guid.Equals(observableItem.Guid))
{
var method = dtoItem.GetType().GetRuntimeMethod(nameof(MapToObservable), new Type[] { observableItem.GetType() });
method.Invoke(dtoItem, new object[] { observableItem });
found = true;
break;
}
}
if (!found)
{
var observableItem = Activator.CreateInstance(observableProp.PropertyType.GenericTypeArguments[0]);
var method = item.GetType().GetRuntimeMethod(nameof(MapToObservable), new Type[] { observableItem.GetType() });
method.Invoke(item, new object[] { observableItem });
observableCollection.Add(observableItem);
}
}
}
}
}
}
}
///
/// Returns true if this instance is equal in terms of values (not references) to the specified entity.
///
/// The observable.
///
public bool EqualsToObservable(T observable)
{
if (observable == null) return false;
foreach (var prop in typeof(DTO).GetProperties())
{
var observableProp = typeof(T).GetProperty(prop.Name);
if (observableProp != null)
{
if (prop.PropertyType.IsValueTypeOrString() || prop.PropertyType == typeof(byte[]))
{
var observableValue = observableProp.GetValue(observable);
var dtoValue = prop.GetValue(this);
if (dtoValue == null && observableValue != null) return false;
if (dtoValue != null && observableValue == null) return false;
if (dtoValue != null && observableValue != null)
{
if (!dtoValue.Equals(observableValue))
{
return false;
}
}
}
else if (!prop.PropertyType.IsGenericType)
{
var propInstance = prop.GetValue(this);
var observableInstance = observableProp.GetValue(observable);
if (propInstance == null && observableInstance != null) return false;
if (propInstance != null)
{
var method = prop.PropertyType.GetRuntimeMethod(nameof(EqualsToObservable), new Type[] { observableInstance.GetType() });
if (!((bool)method.Invoke(propInstance, new object[] { observableInstance })))
{
return false;
}
}
}
else
{
IList source = observableProp.GetValue(observable) as IList;
IList collection = prop.GetValue(this) as IList;
if (collection == null && source != null) return false;
if (source == null && collection != null) return false;
if (source != null && collection != null)
{
if (source.Count != collection.Count) return false;
for (int i = 0; i < source.Count; i++)
{
var item = collection[i];
var itemSource = source[i];
var method = item.GetType().GetRuntimeMethod(nameof(EqualsToObservable), new Type[] { itemSource.GetType() });
if (item == null && itemSource != null) return false;
if (!((bool)method.Invoke(item, new object[] { itemSource })))
{
return false;
}
}
}
}
}
}
return true;
}
///
/// Returns true if this instance is equal in terms of values (not references) to the specified entity.
///
/// The observable.
///
public bool Equals(T observable)
{
return EqualsToObservable(observable);
}
///
/// Returns true if the specified property should be ignored during ActionLog comparison.
///
/// Name of the property.
///
bool IActionLogComparable.ShouldActionLogIgnore(string propName)
{
return propName == nameof(LastUpdated) || OnShouldActionLogIgnore(propName);
}
///
/// override to specified properties to be ignored when doing ActionLog comparison.
///
/// Name of the property.
///
protected virtual bool OnShouldActionLogIgnore(string propName)
{
return false;
}
///
/// Returns an optional custom name for this instance in the comparison tree.
///
///
string IActionLogComparable.GetActionLogName()
{
return OnGetActionLogName();
}
///
/// override to specified a custom name for this instance in the ActionLog comparison tree.
///
///
protected virtual String OnGetActionLogName()
{
return this.GetType().Name;
}
///
/// Called when the static method completes.
///
/// The observable.
protected virtual void OnFromObservableCompleted(T observable)
{
//Just for override
}
}
}