using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using SkiaSharp;
namespace Artemis.Core
{
///
/// Represents an element of a
///
public abstract class ProfileElement : BreakableModel, IDisposable
{
private Guid _entityId;
private string? _name;
private int _order;
private ProfileElement? _parent;
private Profile _profile;
private bool _suspended;
internal readonly List ChildrenList;
internal ProfileElement(Profile profile)
{
_profile = profile;
ChildrenList = new List();
Children = new(ChildrenList);
}
///
/// Gets the unique ID of this profile element
///
public Guid EntityId
{
get => _entityId;
internal set => SetAndNotify(ref _entityId, value);
}
///
/// Gets the profile this element belongs to
///
public Profile Profile
{
get => _profile;
internal set => SetAndNotify(ref _profile, value);
}
///
/// Gets the parent of this element
///
public ProfileElement? Parent
{
get => _parent;
internal set => SetAndNotify(ref _parent, value);
}
///
/// The element's children
///
public ReadOnlyCollection Children { get; }
///
/// The order in which this element appears in the update loop and editor
///
public int Order
{
get => _order;
internal set => SetAndNotify(ref _order, value);
}
///
/// The name which appears in the editor
///
public string? Name
{
get => _name;
set => SetAndNotify(ref _name, value);
}
///
/// Gets or sets the suspended state, if suspended the element is skipped in render and update
///
public bool Suspended
{
get => _suspended;
set => SetAndNotify(ref _suspended, value);
}
///
/// Gets a boolean indicating whether the profile element is disposed
///
public bool Disposed { get; protected set; }
///
/// Updates the element
///
///
public abstract void Update(double deltaTime);
///
/// Renders the element
///
public abstract void Render(SKCanvas canvas, SKPointI basePosition);
///
/// Resets the internal state of the element
///
public abstract void Reset();
///
public override string ToString()
{
return $"{nameof(EntityId)}: {EntityId}, {nameof(Order)}: {Order}, {nameof(Name)}: {Name}";
}
#region Overrides of BreakableModel
///
public override string BrokenDisplayName => Name ?? GetType().Name;
#endregion
#region Hierarchy
///
/// Adds a profile element to the collection, optionally at the given position (0-based)
///
/// The profile element to add
/// The order where to place the child (0-based), defaults to the end of the collection
public virtual void AddChild(ProfileElement child, int? order = null)
{
if (Disposed)
throw new ObjectDisposedException(GetType().Name);
lock (ChildrenList)
{
if (ChildrenList.Contains(child))
return;
// Add to the end of the list
if (order == null)
ChildrenList.Add(child);
// Insert at the given index
else
{
if (order < 0)
order = 0;
if (order > ChildrenList.Count)
order = ChildrenList.Count;
ChildrenList.Insert(order.Value, child);
}
child.Parent = this;
StreamlineOrder();
}
OnChildAdded();
}
///
/// Removes a profile element from the collection
///
/// The profile element to remove
public virtual void RemoveChild(ProfileElement child)
{
if (Disposed)
throw new ObjectDisposedException(GetType().Name);
lock (ChildrenList)
{
ChildrenList.Remove(child);
StreamlineOrder();
child.Parent = null;
}
OnChildRemoved();
}
private void StreamlineOrder()
{
for (int index = 0; index < ChildrenList.Count; index++)
ChildrenList[index].Order = index;
}
///
/// Returns a flattened list of all child folders
///
///
public List GetAllFolders()
{
if (Disposed)
throw new ObjectDisposedException(GetType().Name);
List folders = new();
foreach (Folder childFolder in Children.Where(c => c is Folder).Cast())
{
// Add all folders in this element
folders.Add(childFolder);
// Add all folders in folders inside this element
folders.AddRange(childFolder.GetAllFolders());
}
return folders;
}
///
/// Returns a flattened list of all child layers
///
///
public List GetAllLayers()
{
if (Disposed)
throw new ObjectDisposedException(GetType().Name);
List layers = new();
// Add all layers in this element
layers.AddRange(Children.Where(c => c is Layer).Cast());
// Add all layers in folders inside this element
foreach (Folder childFolder in Children.Where(c => c is Folder).Cast())
layers.AddRange(childFolder.GetAllLayers());
return layers;
}
#endregion
#region Storage
internal abstract void Load();
internal abstract void Save();
#endregion
#region IDisposable
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Disposes the profile element
///
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
}
#endregion
#region Events
///
/// Occurs when a child was added to the list
///
public event EventHandler? ChildAdded;
///
/// Occurs when a child was removed from the list
///
public event EventHandler? ChildRemoved;
///
/// Invokes the event
///
protected virtual void OnChildAdded()
{
ChildAdded?.Invoke(this, EventArgs.Empty);
}
///
/// Invokes the event
///
protected virtual void OnChildRemoved()
{
ChildRemoved?.Invoke(this, EventArgs.Empty);
}
#endregion
}
}