using MoreLinq; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.UI; using static CommunicationEvents; public class DisplayFacts : MonoBehaviour, ISerializationCallbackReceiver { private static DisplayFacts _instance; public static DisplayFacts Instance { get => _instance; private set { _instance ??= value; } } public static Transform FactscreenContent => Instance.factscreenContent; public static GameObject FactSpotPrefab => Instance.factSpotPrefab; //cannot populate static dict while serialization => "singelton" public static Dictionary<Type, GameObject> PrefabDictionary { get => Instance._PrefabDictionary; } private Dictionary<Type, GameObject> _PrefabDictionary = new(); public static Dictionary<string, GameObject> displayedFacts = new(); [SerializeField] private Transform factscreenContent; [SerializeField] private GameObject factSpotPrefab; private bool sortDescending = false; private bool showGrouped = false; private bool showOnlyFavorites = false; #region ISerializationCallbackReceiver [ReadOnly(true), SerializeField] private List<string> PrefabtTypeReadonly; [SerializeField] private List<GameObject> PrefabDataConfig; public void OnBeforeSerialize() { PrefabtTypeReadonly = TypeExtensions<Fact>.UAssemblyInheritenceTypes.Select(type => type.ToString()).ToList(); PrefabtTypeReadonly.Sort(); //for convinience // if order has changed => do not forget var keys = _PrefabDictionary.Keys.Select(type => type.ToString()).ToList(); var keys_permutation = PrefabtTypeReadonly.Select(type => keys.IndexOf(type)).ToArray(); _PrefabDictionary.TryGetValue(typeof(TestFact), out GameObject POdefault); var vals = _PrefabDictionary.Values.ToArray(); PrefabDataConfig = new(keys_permutation.Length); for (int i = 0; i < keys_permutation.Length; i++) { GameObject data = keys_permutation[i] < 0 ? POdefault : vals[keys_permutation[i]]; if (PrefabDataConfig.Count <= i) PrefabDataConfig.Add(data); else PrefabDataConfig[i] = data; } } public void OnAfterDeserialize() { _PrefabDictionary = new(); for (int i = 0; i != Math.Min(PrefabtTypeReadonly.Count, PrefabDataConfig.Count); i++) { //// No Idea how to reproduce //if (TypeExtensions<Fact>.UAssemblyInheritenceTypes.FirstOrDefault(type => type.ToString() == PrefabtTypeReadonly[i]) == null) // continue; _PrefabDictionary.TryAdd( TypeExtensions<Fact>.UAssemblyInheritenceTypes.FirstOrDefault(type => type.ToString() == PrefabtTypeReadonly[i]), PrefabDataConfig[i] ); } } #endregion ISerializationCallbackReceiver #region UnityMethods void Awake() { Instance = this; //first come, first serve displayedFacts = new(); // reset static properties } private void OnEnable() { AddFactEvent.AddListener(AddFact); RemoveFactEvent.AddListener(RemoveFact); FactFavorisation.ChangeFavoriteEvent.AddListener(OnFavoriteChange); } private void OnDisable() { AddFactEvent.RemoveListener(AddFact); RemoveFactEvent.RemoveListener(RemoveFact); FactFavorisation.ChangeFavoriteEvent.RemoveListener(OnFavoriteChange); } private void RemoveFact(Fact fact) => displayedFacts.Remove(fact.Id); #endregion UnityMethods #region Implementation public void AddFact(Fact fact) { // index where the new display will be inserted int siblingIdx = sortDescending ? 0 : factscreenContent.childCount; if (showGrouped) { var facts = factscreenContent.GetComponentsInChildren<FactObject>().Select(f => f.Fact).ToList(); if (!sortDescending) siblingIdx = GetIndexInSortedList(fact, facts); else { facts.Reverse(); var _siblingIdx = GetIndexInSortedList(fact, facts); siblingIdx = factscreenContent.childCount - _siblingIdx; } } // create display var display = CreateDisplay(fact, factscreenContent); display.transform.localPosition = Vector3.zero; displayedFacts.TryAdd(fact.Id, display); display.transform.parent.gameObject.SetActive( !showOnlyFavorites || display.GetComponent<FactFavorisation>().IsFavorite ); display.transform.parent.transform.SetSiblingIndex(siblingIdx); } public static GameObject CreateDisplay(Fact fact, Transform transform) { var spot = Instantiate(FactSpotPrefab, transform); spot.GetComponent<FactWrapper>().Fact = fact; return InstantiateDisplay(fact, spot.transform); } public static GameObject InstantiateDisplay(Fact fact, Transform transform) { Type fact_type = fact.GetType(); if (fact_type.IsConstructedGenericType) fact_type = fact_type.GetGenericTypeDefinition(); var ret = Instantiate(PrefabDictionary[fact_type], transform); ret.GetComponent<FactObject>().Fact = fact; return ret; } #region Sorting #region AscDesc public void AscDescChanged(Toggle t) { sortDescending = !sortDescending; // t.isOn factscreenContent.gameObject.ForAllChildren(child => child.transform.SetAsFirstSibling()); } #endregion AscDesc #region Grouping public void GroupingChanged(Toggle t) { showGrouped = t.isOn; var vals = factscreenContent.gameObject.GetDirectChildren(); var ordered = showGrouped ? vals.OrderBy(tr => tr.GetComponent<FactWrapper>().Fact, new FactTypeComparer()).ToList() : vals.OrderBy(tr => displayedFacts.Keys.ToList() .IndexOf( tr.GetComponent<FactWrapper>().URI )).ToList(); if (sortDescending) ordered.Reverse(); for (int i = 0; i < ordered.Count; i++) ordered[i].transform.SetSiblingIndex(i); } private int GetIndexInSortedList(Fact f, List<Fact> toCheck) { var index = toCheck.BinarySearch(f, new FactTypeComparer()); if (index < 0) index = ~index; return index; } private class FactTypeComparer : IComparer<Fact> { /// <summary> /// Compare two facts by type and label /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public int Compare(Fact x, Fact y) { int ret, index_x; #pragma warning disable CS0642 // Möglicherweise falsche leere Anweisung if (0 != (ret = (index_x = TypeRank.IndexOf(x.GetType())) - TypeRank.IndexOf(y.GetType()))) ; else if (index_x < 0) // Types not in List and same if (0 != (ret = x.DependentFactIds.Length - y.DependentFactIds.Length)) ; else if (0 != (ret = string.Compare(x.GetType().ToString(), y.GetType().ToString()))) ; else; else if (true) // keep auto format // Types in List and same if (0 != (ret = x.DependentFactIds.Zip(y.DependentFactIds, (x, y) => string.Compare(x, y)).Aggregate(0, (r1, r2) => r1 + r2))) ; else if (0 != (ret = string.Compare(x._LastLabel, y._LastLabel))) ; #pragma warning restore CS0642 // Möglicherweise falsche leere Anweisung return ret; } private List<Type> TypeRank = new() { typeof(PointFact), typeof(OnLineFact), typeof(LineFact), typeof(RayFact), typeof(ParallelLineFact), typeof(RightAngleFact), typeof(AngleFact), typeof(CircleFact), typeof(RadiusFact), typeof(AreaCircleFact), typeof(EqualCirclesFact), typeof(UnEqualCirclesFact), typeof(OnCircleFact), typeof(AngleCircleLineFact), typeof(OrthogonalCircleLineFact), typeof(CylinderVolumeFact), typeof(ConeVolumeFact), typeof(TruncatedConeVolumeFact), typeof(FunctionFact), typeof(FunctionCallFact), typeof(TestFact), }; } #endregion Grouping #endregion Sorting #region Favorites public void FavoritesFilterChanged(Toggle t) { showOnlyFavorites = t.isOn; if (!showOnlyFavorites) // show all displayedFacts.Values.ForEach(nFav => nFav.transform.parent.gameObject.SetActive(true)); else { // hide not favorites displayedFacts.Values .Where(gO => gO != null && !gO.GetComponent<FactFavorisation>().IsFavorite) .ForEach(nFav => nFav.transform.parent.gameObject.SetActive(false)); } } private void OnFavoriteChange(Fact changedFact, bool isFavourite) { if (!showOnlyFavorites) return; if (displayedFacts.TryGetValue(changedFact.Id, out GameObject display)) display.transform.parent.gameObject.SetActive(isFavourite); } #endregion Favorites #endregion Implementation }