using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
public class MemberOptionTree : FuzzyOptionTree
{
public enum RootMode
{
Members,
Types,
Namespaces
}
public MemberOptionTree(IEnumerable<Type> types, MemberFilter memberFilter, TypeFilter memberTypeFilter, ActionDirection direction) : base(new GUIContent("Member"))
{
favorites = new Favorites(this);
codebase = Codebase.Subset(types, memberFilter.Configured(), memberTypeFilter?.Configured(false));
this.direction = direction;
expectingBoolean = memberTypeFilter?.ExpectsBoolean ?? false;
}
public MemberOptionTree(UnityObject target, MemberFilter memberFilter, TypeFilter memberTypeFilter, ActionDirection direction)
: this(EditorUnityObjectUtility.GetUnityTypes(target), memberFilter, memberTypeFilter, direction)
{
rootMode = RootMode.Types;
}
private readonly CodebaseSubset codebase;
private readonly ActionDirection direction;
private readonly bool expectingBoolean;
private readonly RootMode rootMode = RootMode.Namespaces;
public override void Prewarm()
{
base.Prewarm();
codebase.Cache();
}
public override IFuzzyOption Option(object item)
{
if (item is Member)
{
return new MemberOption((Member)item, direction, expectingBoolean);
}
if (item is Type)
{
return new TypeOption((Type)item, true);
}
return base.Option(item);
}
#region Hierarchy
public override IEnumerable<object> Root()
{
if (rootMode == RootMode.Types)
{
foreach (var type in codebase.members
.Select(m => m.targetType)
.Distinct()
.OrderBy(t => t.DisplayName()))
{
yield return type;
}
}
else if (rootMode == RootMode.Namespaces)
{
foreach (var @namespace in codebase.members.Select(m => m.targetType)
.Distinct()
.Where(t => !t.IsEnum)
.Select(t => t.Namespace().Root)
.Distinct()
.OrderBy(ns => ns.DisplayName(false)))
{
yield return @namespace;
}
}
else
{
throw new UnexpectedEnumValueException<RootMode>(rootMode);
}
}
public override IEnumerable<object> Children(object parent)
{
if (parent is Namespace)
{
var @namespace = (Namespace)parent;
if (!@namespace.IsGlobal)
{
foreach (var childNamespace in codebase.members
.Select(m => m.targetType)
.Distinct()
.Where(t => !t.IsEnum)
.Select(t => t.Namespace())
.Distinct()
.Where(ns => ns.Parent == @namespace)
.OrderBy(ns => ns.DisplayName(false)))
{
yield return childNamespace;
}
}
foreach (var type in codebase.members
.Select(m => m.targetType)
.Where(t => !t.IsEnum)
.Distinct()
.Where(t => t.Namespace() == @namespace)
.OrderBy(t => t.DisplayName()))
{
yield return type;
}
}
else if (parent is Type)
{
foreach (var member in codebase.members
.Where(m => m.targetType == (Type)parent)
.OrderBy(m => BoltCore.Configuration.groupInheritedMembers && m.isPseudoInherited)
.ThenBy(m => m.info.DisplayName()))
{
yield return member;
}
}
else if (parent is Member)
{
yield break;
}
else
{
throw new NotSupportedException();
}
}
#endregion
#region Search
public override bool searchable { get; } = true;
public override IEnumerable<object> OrderedSearchResults(string query, CancellationToken cancellation)
{
// Exclude duplicate inherited members, like the high amount of "Destroy()" or "enabled",
// if their declaring type is also available for search.
foreach (var member in codebase.members
.Cancellable(cancellation)
.UnorderedSearchFilter(query, m => MemberOption.Haystack(m, direction, expectingBoolean))
.Where(m => !m.isPseudoInherited || !codebase.types.Contains(m.declaringType))
.OrderBy(m => BoltCore.Configuration.groupInheritedMembers && m.isPseudoInherited)
.ThenByDescending(m => SearchUtility.Relevance(query, MemberOption.Haystack(m, direction, expectingBoolean))))
{
yield return member;
}
foreach (var type in codebase.types
.Cancellable(cancellation)
.Where(t => !t.IsEnum)
.OrderedSearchFilter(query, TypeOption.Haystack))
{
yield return type;
}
}
public override string SearchResultLabel(object item, string query)
{
if (item is Type)
{
return TypeOption.SearchResultLabel((Type)item, query);
}
else if (item is Member)
{
return MemberOption.SearchResultLabel((Member)item, query, direction, expectingBoolean);
}
throw new NotSupportedException();
}
#endregion
#region Favorites
public override ICollection<object> favorites { get; }
public override string FavoritesLabel(object item)
{
if (item is Namespace)
{
return ((Namespace)item).DisplayName();
}
else if (item is Type)
{
return ((Type)item).DisplayName();
}
else if (item is Member)
{
var member = (Member)item;
if (member.isInvocable)
{
return $"{member.info.DisplayName(direction, expectingBoolean)} ({member.methodBase.DisplayParameterString(member.targetType)})";
}
else
{
return member.info.DisplayName(direction, expectingBoolean);
}
}
throw new NotSupportedException();
}
public override bool CanFavorite(object item)
{
return item is Member;
}
public override void OnFavoritesChange()
{
BoltCore.Configuration.SetDirty();
BoltCore.Configuration.Save();
}
private class Favorites : ICollection<object>
{
public Favorites(MemberOptionTree tree)
{
this.tree = tree;
}
private readonly MemberOptionTree tree;
private IEnumerable<Member> validFavorites => BoltCore.Configuration.favoriteMembers.Where(tree.codebase.ValidateMember);
public int Count => validFavorites.Count();
public bool IsReadOnly => false;
public IEnumerator<object> GetEnumerator()
{
foreach (var favorite in validFavorites)
{
yield return favorite;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Contains(object item)
{
return validFavorites.Contains((Member)item);
}
public void Add(object item)
{
favorites.Add(item);
}
public bool Remove(object item)
{
return favorites.Remove(item);
}
public void Clear()
{
favorites.Clear();
}
public void CopyTo(object[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
if (arrayIndex < 0)
{
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
}
if (array.Length - arrayIndex < Count)
{
throw new ArgumentException();
}
var i = 0;
foreach (var item in this)
{
array[i + arrayIndex] = item;
i++;
}
}
private static readonly ICollection<object> favorites = new VariantCollection<object, Member>(BoltCore.Configuration.favoriteMembers);
}
#endregion
}
}