using System.Collections.Generic; using UnityEngine; using TMPro; using System; using static CommunicationEvents; using System.Linq; using UnityEngine.UI; using System.Collections; using MoreLinq; using System.ComponentModel; using UnityEditor; using System.Reflection; public class DisplayFacts : MonoBehaviour, ISerializationCallbackReceiver { private static DisplayFacts _instance; public static DisplayFacts Instance { get => _instance; private set { _instance ??= value; } } //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(); 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 ? null : 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++) // if (PrefabDataConfig[i] != null) _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 AddFactEvent.AddListener(AddFact); RemoveFactEvent.AddListener(RemoveFact); //AnimateExistingFactEvent.AddListener(AnimateFact); FactFavorisation.ChangeFavoriteEvent.AddListener(OnFavoriteChange); } #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<FactWrapper>().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(transform, fact); display.transform.localPosition = Vector3.zero; displayedFacts.TryAdd(fact.Id, display); // disable if showOnlyFavorites is true and fact is no favorite display.transform.parent.gameObject.SetActive(!(showOnlyFavorites && !display.GetComponent<FactFavorisation>().IsFavorite)); display.transform.parent.transform.SetSiblingIndex(siblingIdx); } private GameObject CreateDisplay(Transform transform, Fact fact) { Type fact_type = fact.GetType(); if(fact_type.IsConstructedGenericType) fact_type = fact_type.GetGenericTypeDefinition(); var spot = Instantiate(factSpotPrefab, factscreenContent); return fact.instantiateDisplay(PrefabDictionary[fact_type], spot.transform); } public void RemoveFact(Fact fact) { // destroy factSpot (parent of displayed fact) and the fact display with it Destroy(displayedFacts[fact.Id].transform.parent.gameObject); displayedFacts.Remove(fact.Id); } public void AnimateFact(Fact fact) { var factIcon = displayedFacts[fact.Id]; factIcon.GetComponentInChildren<ImageHintAnimation>().AnimationTrigger(); } #region Sorting #region AscDesc public void AscDescChanged(Toggle t) { sortDescending = !sortDescending; 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>().Fact.Id)).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; } internal 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) { if (x.GetType() == y.GetType()) // same type: compare labels return string.Compare(x.Label, y.Label); else // different types: compare type return string.Compare(x.GetType().ToString(), y.GetType().ToString()); } } #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.GetComponent<FactFavorisation>().IsFavorite) .ForEach(nFav => nFav.transform.parent.gameObject.SetActive(false)); } } private void OnFavoriteChange(Fact changedFact, bool isFavourite) { if (!showOnlyFavorites) return; var id = changedFact.Id; if (displayedFacts.ContainsKey(id)) displayedFacts[id].transform.parent.gameObject.SetActive(isFavourite); } #endregion Favorites #endregion Implementation }