Decompiled source of SearchThing v0.5.0

Mods/SearchThing.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using BoneLib;
using FuzzySharp;
using FuzzySharp.Edits;
using FuzzySharp.Extensions;
using FuzzySharp.Extractor;
using FuzzySharp.PreProcess;
using FuzzySharp.SimilarityRatio;
using FuzzySharp.SimilarityRatio.Scorer;
using FuzzySharp.SimilarityRatio.Scorer.Composite;
using FuzzySharp.SimilarityRatio.Scorer.Generic;
using FuzzySharp.SimilarityRatio.Scorer.StrategySensitive;
using FuzzySharp.SimilarityRatio.Scorer.StrategySensitive.Generic;
using FuzzySharp.SimilarityRatio.Strategy;
using FuzzySharp.SimilarityRatio.Strategy.Generic;
using FuzzySharp.Utils;
using HarmonyLib;
using Il2CppCysharp.Threading.Tasks;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Bonelab;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.SceneStreaming;
using Il2CppSLZ.Marrow.Warehouse;
using Il2CppSLZ.UI;
using Il2CppTMPro;
using LabFusion.Data;
using LabFusion.Marrow.Serialization;
using LabFusion.Network;
using LabFusion.Player;
using LabFusion.Scene;
using LabFusion.Utilities;
using MelonLoader;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using SearchThing;
using SearchThing.Extensions;
using SearchThing.Extensions.Components;
using SearchThing.Extensions.Pages;
using SearchThing.Extensions.Panel;
using SearchThing.Extensions.Panel.Abstract;
using SearchThing.Extensions.Panel.Data;
using SearchThing.Extensions.Panel.Filter;
using SearchThing.Extensions.Panel.History;
using SearchThing.Extensions.Sort;
using SearchThing.Fusion;
using SearchThing.History;
using SearchThing.Keyboard;
using SearchThing.Patches;
using SearchThing.Patches.Compatibility;
using SearchThing.Presets;
using SearchThing.Presets.Data;
using SearchThing.Search;
using SearchThing.Util;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "SearchThing", "0.5.0", "Mash", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("SearchThing")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SearchThing")]
[assembly: AssemblyTitle("SearchThing")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace SearchThing
{
	public class Mod : MelonMod
	{
		public static bool IsFusionLoaded;

		public override void OnInitializeMelon()
		{
			IsFusionLoaded = MelonBase.FindMelon("LabFusion", "Lakatrazz") != null;
			Hooking.OnWarehouseReady += OnWarehouseReady;
		}

		private static void HookFusion()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			FusionNetworkSpawnPatch.TryInitialize();
			MultiplayerHooking.OnTargetLevelLoaded += new UpdateEvent(SpawnGunPatches.ClearSelectedCrate);
		}

		private static void OnWarehouseReady()
		{
			SearchManager.InitializeSearchThread();
			PresetManager.LoadPresets();
			if (IsFusionLoaded)
			{
				HookFusion();
			}
		}

		public override void OnDeinitializeMelon()
		{
			SearchManager.ShutdownSearchThread();
		}
	}
	public static class SpawnablesPanelManager
	{
		private static SpawnablePanelExtension? _extension;

		public static void Load(SpawnablesPanelView panelView)
		{
			if (_extension != null && _extension.Is(panelView))
			{
				if (_extension.IsSearchActive())
				{
					_extension.RequestRefresh();
				}
			}
			else
			{
				_extension = new SpawnablePanelExtension(panelView);
			}
		}

		public static void OnTabSelected(SpawnablesPanelView panelView, int index)
		{
			if (_extension != null && _extension.Is(panelView))
			{
				if (index == 5)
				{
					_extension.Show();
				}
				else
				{
					_extension.Hide();
				}
			}
		}

		public static SpawnablePanelExtension? Get()
		{
			return _extension;
		}

		public static bool TryGet(SpawnablesPanelView instance, [MaybeNullWhen(false)] out SpawnablePanelExtension extension)
		{
			if (_extension != null && _extension.Is(instance))
			{
				extension = _extension;
				return true;
			}
			extension = null;
			return false;
		}
	}
}
namespace SearchThing.Util
{
	public class BodylogAccessor
	{
		private static PullCordDevice? _cordDevice;

		private static RigManager GetFusionRig()
		{
			return RigData.Refs.RigManager;
		}

		private static RigManager GetRig()
		{
			if (Mod.IsFusionLoaded)
			{
				return GetFusionRig();
			}
			return Player.RigManager;
		}

		public static PullCordDevice? GetCordDevice()
		{
			if ((Object)(object)_cordDevice != (Object)null)
			{
				return _cordDevice;
			}
			RigManager rig = GetRig();
			if ((Object)(object)rig == (Object)null)
			{
				return null;
			}
			_cordDevice = (from item in (IEnumerable<SlotContainer>)rig.inventory.specialItems
				select ((Component)item).transform into transform
				where transform.childCount > 0
				select ((Component)transform.GetChild(0)).GetComponent<PullCordDevice>()).FirstOrDefault((Func<PullCordDevice, bool>)((PullCordDevice item) => (Object)(object)item != (Object)null));
			return _cordDevice;
		}
	}
	public static class CrateIconProvider
	{
		private static readonly Sprite AvatarIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.AvatarIcon.png");

		private static readonly Sprite CrateIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.CrateIcon.png");

		private static readonly Sprite LevelIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.LevelIcon.png");

		private static readonly Sprite GunIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.GunIcon.png");

		private static readonly Sprite MeleeIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.MeleeIcon.png");

		private static readonly Sprite ThrowableIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.ThrowableIcon.png");

		private static readonly Sprite VehicleIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.VehicleIcon.png");

		private static Sprite GetAvatarIcon()
		{
			return AvatarIcon;
		}

		private static Sprite GetPropIcon(ISearchableCrate searchableCrate)
		{
			return (Sprite)(searchableCrate.CrateSubType switch
			{
				CrateSubType.Gun => GunIcon, 
				CrateSubType.Melee => MeleeIcon, 
				CrateSubType.Throwable => ThrowableIcon, 
				CrateSubType.Vehicle => VehicleIcon, 
				_ => CrateIcon, 
			});
		}

		private static Sprite GetLevelIcon()
		{
			return LevelIcon;
		}

		public static Sprite GetIcon(ISearchableCrate searchableCrate)
		{
			return (Sprite)(searchableCrate.CrateType switch
			{
				CrateType.Avatar => GetAvatarIcon(), 
				CrateType.Prop => GetPropIcon(searchableCrate), 
				CrateType.Level => GetLevelIcon(), 
				_ => CrateIcon, 
			});
		}
	}
	public static class ImageHelper
	{
		public static Texture2D LoadEmbeddedImage(string resourceName)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			using Stream stream = typeof(ImageHelper).Assembly.GetManifestResourceStream(resourceName);
			if (stream == null)
			{
				throw new Exception("Resource not found: " + resourceName);
			}
			byte[] array = new byte[stream.Length];
			stream.Read(array, 0, array.Length);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
			{
				name = resourceName,
				hideFlags = (HideFlags)32
			};
			if (!ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array)))
			{
				throw new Exception("Failed to load image from resource: " + resourceName);
			}
			val.Apply();
			return val;
		}

		public static Sprite LoadEmbeddedSprite(string resourceName)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = LoadEmbeddedImage(resourceName);
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), Vector2.one * 0.5f);
			((Object)obj).hideFlags = (HideFlags)32;
			return obj;
		}
	}
	public static class SearchHelper
	{
		public static int GetSalt(this string str)
		{
			return str.Aggregate(23, (int current, char c) => current * 31 + c);
		}

		public static bool IsCrate<T>(this Crate crate) where T : Crate
		{
			return (Object)(object)((Il2CppObjectBase)crate).TryCast<T>() != (Object)null;
		}

		public static CrateType GetCrateType(this Crate crate)
		{
			if (crate.IsCrate<AvatarCrate>())
			{
				return CrateType.Avatar;
			}
			if (crate.IsCrate<LevelCrate>())
			{
				return CrateType.Level;
			}
			return CrateType.Prop;
		}

		private static List<string> GetTags(this Crate crate)
		{
			return (from tag in (IEnumerable<string>)crate._tags._items
				where !string.IsNullOrEmpty(tag)
				select tag.ToLower()).ToList();
		}

		private static List<string> GetMetaDataList(this Crate crate)
		{
			List<string> tags = crate.GetTags();
			if (!string.IsNullOrEmpty(((Scannable)crate).Title))
			{
				tags.AddRange(from p in ((Scannable)crate).Title.ToLower().Split(" ")
					select p.Trim());
			}
			return tags;
		}

		private static CrateSubType GetPropSubType(this Crate crate)
		{
			List<string> metaDataList = crate.GetMetaDataList();
			if (metaDataList.Contains("gun"))
			{
				return CrateSubType.Gun;
			}
			if (metaDataList.Contains("grenade"))
			{
				return CrateSubType.Throwable;
			}
			if (metaDataList.Contains("melee"))
			{
				return CrateSubType.Melee;
			}
			if (metaDataList.Contains("vehicle"))
			{
				return CrateSubType.Vehicle;
			}
			return CrateSubType.None;
		}

		public static CrateSubType GetCrateSubType(this Crate crate, CrateType crateType)
		{
			if (crateType == CrateType.Prop)
			{
				return crate.GetPropSubType();
			}
			return CrateSubType.None;
		}

		public static bool TryGetCrate(this ISearchableCrate crate, [MaybeNullWhen(false)] out Crate outCrate)
		{
			return AssetWarehouse.Instance.TryGetCrate(crate.Barcode, ref outCrate);
		}
	}
	public struct SearchTag
	{
		public string Original { get; }

		public string Preprocessed { get; }

		public SearchTag(string original)
		{
			Original = original;
			Preprocessed = Preprocess(original);
		}

		public int PartialRatio(string other)
		{
			return Fuzz.PartialRatio(Preprocessed, other, PreprocessMode.None);
		}

		public bool Contains(string preprocessedQuery)
		{
			return Preprocessed.Contains(preprocessedQuery);
		}

		public static string Preprocess(string value)
		{
			return StringHelper.RemoveUnityRichText(StringHelper.RemoveNonAlphanumeric(value.ToLowerInvariant())).Replace(" ", "").Trim();
		}
	}
	public static class StringHelper
	{
		private static readonly Regex UnityRichTextRegex = new Regex("</?[a-zA-Z]*=[^>]*>|</?[a-zA-Z]+>", RegexOptions.Compiled);

		private static readonly Regex NonAlphanumericRegex = new Regex("[^ a-zA-Z0-9]", RegexOptions.Compiled);

		public static string RemoveUnityRichText(string input)
		{
			return UnityRichTextRegex.Replace(input, string.Empty);
		}

		public static string RemoveNonAlphanumeric(string input)
		{
			return NonAlphanumericRegex.Replace(input, string.Empty);
		}
	}
	public static class ThreadUtils
	{
		public static void RunOnMainThread(this Action action)
		{
			MelonCoroutines.Start(InvokeOnMainThread(action));
		}

		public static void RunOnMainThread<T>(this Action<T> action, T arg)
		{
			Action<T> action2 = action;
			T arg2 = arg;
			MelonCoroutines.Start(InvokeOnMainThread(delegate
			{
				action2(arg2);
			}));
		}

		private static IEnumerator InvokeOnMainThread(Action action)
		{
			yield return null;
			action();
		}
	}
}
namespace SearchThing.Search
{
	public enum CrateType
	{
		Prop,
		Avatar,
		Level,
		Invalid
	}
	public enum CrateSubType
	{
		None,
		Gun,
		Melee,
		Throwable,
		Vehicle,
		Large,
		Medium,
		Small
	}
	public static class GlobalCrateManager
	{
		private static readonly SearchableCrateLookup SearchableCrateCrates = new SearchableCrateLookup();

		public static ISearchableCrateList<ISearchableCrate> GetCrates()
		{
			return SearchableCrateCrates;
		}

		public static void AddPallet(Pallet pallet)
		{
			SearchableCrateCrates.AddCrates(from c in (IEnumerable<Crate>)pallet._crates._items
				where (Object)(object)c != (Object)null
				select c into crate
				select new SearchableCrate(crate));
		}

		public static ISearchableCrate? GetCrate(Barcode barcode)
		{
			return SearchableCrateCrates.GetCrateByBarcode(barcode._id);
		}
	}
	public interface ISearchableCrate
	{
		SearchTag Name { get; }

		SearchTag PalletName { get; }

		SearchTag Author { get; }

		SearchTag[] Tags { get; }

		string Description { get; }

		bool Redacted { get; }

		CrateType CrateType { get; }

		CrateSubType CrateSubType { get; }

		int Salt { get; }

		int Score { get; }

		DateTime DateAdded { get; }

		Barcode Barcode { get; }
	}
	public interface ISearchOrder
	{
		int Order(ISearchableCrate searchableCrate);
	}
	public class SearchableCrate : ISearchableCrate, IEquatable<SearchableCrate>
	{
		public SearchTag Name { get; }

		public SearchTag PalletName { get; }

		public SearchTag Author { get; }

		public SearchTag[] Tags { get; }

		public string Description { get; }

		public bool Redacted { get; }

		public int Salt { get; }

		public CrateType CrateType { get; }

		public CrateSubType CrateSubType { get; }

		public virtual int Score => 0;

		public DateTime DateAdded { get; }

		public Barcode Barcode { get; }

		public SearchableCrate(Crate spawnableCrate)
		{
			if ((Object)(object)spawnableCrate == (Object)null)
			{
				throw new ArgumentNullException("spawnableCrate");
			}
			Name = new SearchTag(((Object)spawnableCrate).name);
			Description = ((Scannable)spawnableCrate)._description;
			PalletName = new SearchTag(((Object)spawnableCrate._pallet).name);
			Author = new SearchTag(spawnableCrate._pallet._author);
			Tags = ((IEnumerable<string>)spawnableCrate._tags.ToArray()).Select((string t) => new SearchTag(t)).ToArray();
			Redacted = ((Scannable)spawnableCrate)._redacted;
			Salt = ((Object)spawnableCrate).name.GetSalt();
			PalletManifest val = default(PalletManifest);
			if (!spawnableCrate._pallet.IsInMarrowGame() && AssetWarehouse.Instance.TryGetPalletManifest(((Scannable)spawnableCrate._pallet)._barcode, ref val))
			{
				DateAdded = (long.TryParse(val.UpdatedDate, out var result) ? DateTime.UnixEpoch.AddMilliseconds(result) : DateTime.MinValue);
			}
			else
			{
				DateAdded = DateTime.MinValue;
			}
			CrateType = spawnableCrate.GetCrateType();
			CrateSubType = spawnableCrate.GetCrateSubType(CrateType);
			Barcode = ((Scannable)spawnableCrate).Barcode;
		}

		public SearchableCrate(Barcode barcode)
		{
			Crate spawnableCrate = default(Crate);
			if (!AssetWarehouse.Instance.TryGetCrate(barcode, ref spawnableCrate))
			{
				throw new ArgumentException($"Crate with barcode {barcode} not found in warehouse", "barcode");
			}
			this..ctor(spawnableCrate);
		}

		public bool Equals(SearchableCrate? other)
		{
			if (other == null)
			{
				return false;
			}
			if (this == other)
			{
				return true;
			}
			return Barcode._id.Equals(other.Barcode._id);
		}

		public override bool Equals(object? obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (this == obj)
			{
				return true;
			}
			if (obj.GetType() != GetType())
			{
				return false;
			}
			return Equals((SearchableCrate)obj);
		}

		public override int GetHashCode()
		{
			return Barcode._id.GetHashCode();
		}
	}
	public interface ISearchableCrateList<out TCrate> where TCrate : class, ISearchableCrate
	{
		IEnumerable<TCrate> GetCrates();
	}
	public class SearchableCrateList<TCrate> : ISearchableCrateList<TCrate> where TCrate : class, ISearchableCrate
	{
		private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

		private readonly List<TCrate> _crates = new List<TCrate>();

		public void AddCrate(TCrate crate)
		{
			_lock.EnterWriteLock();
			try
			{
				_crates.Add(crate);
			}
			finally
			{
				_lock.ExitWriteLock();
			}
		}

		public void AddCrates(IEnumerable<TCrate> crates)
		{
			_lock.EnterWriteLock();
			try
			{
				_crates.AddRange(crates);
			}
			finally
			{
				_lock.ExitWriteLock();
			}
		}

		public IEnumerable<TCrate> GetCrates()
		{
			_lock.EnterReadLock();
			try
			{
				return _crates.ToArray();
			}
			finally
			{
				_lock.ExitReadLock();
			}
		}
	}
	public record SearchableCrateListWrapper<TCrate>(IEnumerable<TCrate> Crates) : ISearchableCrateList<TCrate> where TCrate : class, ISearchableCrate
	{
		public IEnumerable<TCrate> GetCrates()
		{
			return Crates;
		}
	}
	public static class SearchableCrateListWrapperHelper
	{
		public static ISearchableCrateList<TCrate> ToSearchable<TCrate>(this IEnumerable<TCrate> crates) where TCrate : class, ISearchableCrate
		{
			return new SearchableCrateListWrapper<TCrate>(crates);
		}
	}
	public class SearchableCrateLookup : ISearchableCrateList<ISearchableCrate>
	{
		private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

		private readonly Dictionary<string, ISearchableCrate> _lookup = new Dictionary<string, ISearchableCrate>();

		private readonly List<ISearchableCrate> _crates = new List<ISearchableCrate>();

		public void AddCrate(ISearchableCrate crate)
		{
			_lock.EnterWriteLock();
			try
			{
				_lookup[crate.Barcode._id] = crate;
				_crates.Add(crate);
			}
			finally
			{
				_lock.ExitWriteLock();
			}
		}

		public void AddCrates(IEnumerable<ISearchableCrate> crates)
		{
			_lock.EnterWriteLock();
			try
			{
				foreach (ISearchableCrate crate in crates)
				{
					_lookup[crate.Barcode._id] = crate;
					_crates.Add(crate);
				}
			}
			finally
			{
				_lock.ExitWriteLock();
			}
		}

		public ISearchableCrate? GetCrateByBarcode(string barcode)
		{
			_lock.EnterReadLock();
			try
			{
				return _lookup.GetValueOrDefault(barcode);
			}
			finally
			{
				_lock.ExitReadLock();
			}
		}

		public IEnumerable<ISearchableCrate> GetCrates()
		{
			_lock.EnterReadLock();
			try
			{
				return _crates.ToArray();
			}
			finally
			{
				_lock.ExitReadLock();
			}
		}
	}
	public record ScoredCrate(ISearchableCrate Crate, int Score) : ISearchableCrate
	{
		public SearchTag Name => Crate.Name;

		public SearchTag PalletName => Crate.PalletName;

		public SearchTag Author => Crate.Author;

		public SearchTag[] Tags => Crate.Tags;

		public string Description => Crate.Description;

		public bool Redacted => Crate.Redacted;

		public CrateType CrateType => Crate.CrateType;

		public CrateSubType CrateSubType => Crate.CrateSubType;

		public int Salt => Crate.Salt;

		public Barcode Barcode => Crate.Barcode;

		public DateTime DateAdded => Crate.DateAdded;

		public static ScoredCrate ScoreCrate(ISearchableCrate crate, string preprocessedQuery)
		{
			return new ScoredCrate(crate, SearchManager.ScoreCrate(preprocessedQuery, crate));
		}
	}
	internal interface ISearchTask
	{
		string Query { get; }

		ISearchableCrateList<ISearchableCrate> PureSourceList { get; }

		Func<ISearchableCrate, bool> Filter { get; }

		ISearchOrder SearchOrder { get; }

		CancellationToken CancellationToken { get; }

		void RunAndStore(IEnumerable<ISearchableCrate> results);
	}
	public record SearchTask<TCrate>(string Query, ISearchableCrateList<TCrate> SourceList, Func<ISearchableCrate, bool> Filter, ISearchOrder SearchOrder, Action<SearchResults<TCrate>> OnComplete, CancellationToken CancellationToken) : ISearchTask where TCrate : class, ISearchableCrate
	{
		public ISearchableCrateList<ISearchableCrate> PureSourceList => SourceList;

		public void RunAndStore(IEnumerable<ISearchableCrate> results)
		{
			List<TCrate> entries = results.OfType<TCrate>().ToList();
			SearchResults<TCrate> searchResults = new SearchResults<TCrate>(entries);
			ThreadUtils.RunOnMainThread(delegate
			{
				OnComplete(searchResults);
			});
		}
	}
	public static class SearchManager
	{
		private const int RequiredMatchRate = 75;

		private static readonly object SearchLock = new object();

		private static CancellationTokenSource? _lastSearchCts = new CancellationTokenSource();

		private static Thread? _searchThread;

		private static readonly BlockingCollection<ISearchTask> SearchQueue = new BlockingCollection<ISearchTask>();

		private static readonly CancellationTokenSource SearchThreadCts = new CancellationTokenSource();

		public static void InitializeSearchThread()
		{
			_searchThread = new Thread(SearchThreadWorker)
			{
				IsBackground = true,
				Name = "SearchWorker"
			};
			_searchThread.Start();
		}

		public static void ShutdownSearchThread()
		{
			SearchThreadCts.Cancel();
			SearchQueue.CompleteAdding();
			_searchThread?.Join(TimeSpan.FromMilliseconds(500.0));
		}

		private static void SearchThreadWorker()
		{
			try
			{
				foreach (ISearchTask item in SearchQueue.GetConsumingEnumerable(SearchThreadCts.Token))
				{
					ExecuteSearch(item);
				}
			}
			catch (OperationCanceledException)
			{
			}
		}

		private static void ExecuteSearch(ISearchTask task)
		{
			IEnumerable<ISearchableCrate> crates = task.PureSourceList.GetCrates();
			try
			{
				if (string.IsNullOrWhiteSpace(task.Query) || !AssetWarehouse.ready)
				{
					OrderedParallelQuery<ISearchableCrate> results = crates.AsParallel().WithDegreeOfParallelism(Math.Max(1, Environment.ProcessorCount / 2)).WithCancellation(task.CancellationToken)
						.Where(task.Filter)
						.OrderByDescending(task.SearchOrder.Order)
						.ThenByDescending((ISearchableCrate c) => c.Salt);
					task.RunAndStore(results);
					return;
				}
				string preprocessedQuery = SearchTag.Preprocess(task.Query);
				ParallelQuery<ISearchableCrate> results2 = from c in (from crate in crates.AsParallel().WithDegreeOfParallelism(Math.Max(1, Environment.ProcessorCount / 2)).WithCancellation(task.CancellationToken)
							.Where(task.Filter)
						select new ScoredCrate(crate, ScoreCrate(preprocessedQuery, crate)) into c
						where c.Score >= 75
						select c).OrderByDescending(task.SearchOrder.Order).ThenByDescending((ScoredCrate c) => c.Crate.Salt)
					select c.Crate;
				task.RunAndStore(results2);
			}
			catch (OperationCanceledException)
			{
			}
			catch (Exception ex2)
			{
				MelonLogger.Error("Search failed: {0}", ex2);
			}
		}

		public static void SearchAsync<TCrate>(string query, ISearchableCrateList<TCrate> crateList, Func<ISearchableCrate, bool> filter, ISearchOrder searchOrder, Action<SearchResults<TCrate>> onComplete) where TCrate : class, ISearchableCrate
		{
			lock (SearchLock)
			{
				_lastSearchCts?.Cancel();
				_lastSearchCts?.Dispose();
				_lastSearchCts = new CancellationTokenSource();
			}
			SearchTask<TCrate> item = new SearchTask<TCrate>(query, crateList, filter, searchOrder, onComplete, _lastSearchCts.Token);
			SearchQueue.Add(item);
		}

		public static int ScoreCrate(string preprocessedQuery, ISearchableCrate crate)
		{
			string preprocessedQuery2 = preprocessedQuery;
			int num = crate.Name.PartialRatio(preprocessedQuery2);
			int num2 = crate.PalletName.PartialRatio(preprocessedQuery2);
			int num3 = crate.Author.PartialRatio(preprocessedQuery2);
			int num4 = (crate.Tags.Any((SearchTag t) => t.PartialRatio(preprocessedQuery2) > 80) ? 90 : 0);
			return new int[4] { num, num2, num3, num4 }.Max();
		}
	}
	public class SearchResults<TCrate> where TCrate : class, ISearchableCrate
	{
		public static SearchResults<ISearchableCrate> Empty { get; } = new SearchResults<ISearchableCrate>(new List<ISearchableCrate>());


		private IReadOnlyList<TCrate> Entries { get; }

		public SearchResults(List<TCrate> entries)
		{
			Entries = entries;
		}

		private IEnumerable<TCrate> GetPageIterator(int start, int end)
		{
			for (int i = start; i < end; i++)
			{
				yield return Entries[i];
			}
		}

		public IEnumerable<TCrate> GetPage(int page, int pageSize)
		{
			int num = page * pageSize;
			int end = Math.Min(num + pageSize, Entries.Count);
			if (num >= Entries.Count)
			{
				return Array.Empty<TCrate>();
			}
			return GetPageIterator(num, end);
		}

		public TCrate? GetEntryAt(int index)
		{
			if (index < 0 || index >= Entries.Count)
			{
				return null;
			}
			return Entries[index];
		}

		public TCrate? GetEntryAt(int page, int pageSize, int index)
		{
			int index2 = page * pageSize + index;
			return GetEntryAt(index2);
		}

		public int GetPageCount(int pageSize)
		{
			return (Entries.Count + pageSize - 1) / pageSize;
		}
	}
}
namespace SearchThing.Presets
{
	public class Preset : BasicSearchPanel<ISearchableCrate>
	{
		private const string DefaultTag = "EMPTY";

		private static readonly Sprite PresetRemoveIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.RemoveIcon.png");

		private static readonly Sprite EditIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.EditIcon.png");

		private string _tag = "EMPTY";

		public override string Tag
		{
			get
			{
				if (!IsInitialized)
				{
					if (!PresetManager.IsAssignmentMode)
					{
						return "Empty Preset";
					}
					return "Add Presset";
				}
				return _tag;
			}
		}

		public override bool TagEditable => true;

		public bool IsInitialized { get; private set; }

		public override bool ResearchOnPageChange => true;

		public HashSet<ISearchableCrate> AssignedCrates { get; } = new HashSet<ISearchableCrate>();


		public override bool HasPanelFunction => true;

		public override Sprite PanelFunctionIcon => EditIcon;

		public override bool HasItemFunction => true;

		public override Sprite ItemFunctionIcon => PresetRemoveIcon;

		public Preset()
		{
			IsInitialized = false;
		}

		public Preset(string tag)
		{
			_tag = tag;
			IsInitialized = true;
		}

		public override Color? GetItemFunctionHighlight(SpawnablePanelExtension extension, ISearchableCrate? crate)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return Color.red;
		}

		public override void OnItemFunction(SpawnablePanelExtension extension, ISearchableCrate crate)
		{
			PresetManager.IsAssignmentMode = false;
			RemoveCrate(crate);
			extension.RequestRefresh();
		}

		public override Color? GetPanelFunctionHighlight(SpawnablePanelExtension extension)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			return extension.IsEditing ? Color.green : Color.white;
		}

		public override void OnPanelFunction(SpawnablePanelExtension extension)
		{
			extension.SetIsEditing(!extension.IsEditing);
			extension.RenderFavoriteButton();
		}

		public override Color? IsForceHighlighted(SpawnablePanelExtension extension, ISearchableCrate? selectedCrate)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (extension.IsPanelSelected(this))
			{
				return Color.white;
			}
			if (!IsInitialized || selectedCrate == null || !AssignedCrates.Contains(selectedCrate))
			{
				return null;
			}
			return Color.yellow;
		}

		private bool IsTagValid(string tag)
		{
			return !string.IsNullOrEmpty(tag);
		}

		public override void OnTagEdited(SpawnablePanelExtension extension, string newTag)
		{
			if (IsTagValid(newTag))
			{
				_tag = newTag;
				IsInitialized = true;
			}
		}

		public override bool OnSelected(SpawnablePanelExtension extension)
		{
			if (!PresetManager.IsAssignmentMode)
			{
				return true;
			}
			bool flag = extension.IsPanelSelected(this);
			if (!IsInitialized)
			{
				_tag = $"Preset {Random.Shared.Next(1000, 9999)}";
				IsInitialized = true;
			}
			ISearchableCrate selectedSpawnable = extension.GetSelectedSpawnable();
			if (selectedSpawnable == null)
			{
				return false;
			}
			if (!AssignedCrates.Add(selectedSpawnable))
			{
				AssignedCrates.Remove(selectedSpawnable);
			}
			PresetManager.IsAssignmentMode = false;
			if (flag)
			{
				extension.RequestRefresh();
			}
			else
			{
				extension.RenderTags();
				extension.RenderFavoriteButton();
			}
			return false;
		}

		public void RemoveCrate(ISearchableCrate crate)
		{
			AssignedCrates.Remove(crate);
		}

		protected override void Search(string query, ISearchOrder order, Action<SearchResults<ISearchableCrate>> callback)
		{
			SearchManager.SearchAsync(query, AssignedCrates.ToSearchable(), (ISearchableCrate _) => true, order, callback);
		}

		public PresetData ToData()
		{
			return new PresetData
			{
				Name = _tag,
				Barcodes = AssignedCrates.Select((ISearchableCrate b) => b.Barcode._id).ToList()
			};
		}

		public void FromData(PresetData data)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			_tag = data.Name;
			IsInitialized = _tag != "EMPTY";
			if (!IsInitialized)
			{
				return;
			}
			AssignedCrates.Clear();
			foreach (string barcode in data.Barcodes)
			{
				AssignedCrates.Add(new SearchableCrate(new Barcode(barcode)));
			}
		}
	}
	public static class PresetManager
	{
		private static readonly string FilePath;

		private const int MaxPresets = 6;

		private static bool _assignmentMode;

		private static readonly List<PresetPage> PresetPages;

		public static bool IsAssignmentMode
		{
			get
			{
				return _assignmentMode;
			}
			set
			{
				if (!value)
				{
					SavePresets();
				}
				_assignmentMode = value;
			}
		}

		static PresetManager()
		{
			FilePath = MelonEnvironment.UserDataDirectory + "/SearchThingPresets.json";
			PresetPages = new List<PresetPage>();
			for (int i = 0; i < 6; i++)
			{
				PresetPages.Add(new PresetPage());
			}
		}

		public static IReadOnlyList<ISearchPage> GetPages()
		{
			return PresetPages;
		}

		public static void LoadPresets()
		{
			if (!File.Exists(FilePath))
			{
				return;
			}
			try
			{
				List<PresetPageData> list = JsonSerializer.Deserialize<List<PresetPageData>>(File.ReadAllText(FilePath));
				if (list == null)
				{
					return;
				}
				for (int i = 0; i < list.Count; i++)
				{
					PresetPageData data = list[i];
					if (i < PresetPages.Count)
					{
						PresetPages[i].FromData(data);
						continue;
					}
					break;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("Failed to load presets from file!", ex);
			}
		}

		public static void SavePresets()
		{
			try
			{
				string contents = JsonSerializer.Serialize(PresetPages.Select((PresetPage p) => p.ToData()).ToList(), new JsonSerializerOptions
				{
					WriteIndented = true
				});
				File.WriteAllText(FilePath, contents);
			}
			catch (Exception ex)
			{
				MelonLogger.Error("Failed to save presets to file!", ex);
			}
		}
	}
	public class PresetPage : ISearchPage
	{
		public const int MaxPresets = 6;

		private readonly List<Preset> _presets = new List<Preset>();

		public bool IsVisible => true;

		public IReadOnlyList<ISearchPanel> Panels => _presets;

		public PresetPage()
		{
			for (int i = 0; i < 6; i++)
			{
				_presets.Add(new Preset());
			}
		}

		public PresetPageData ToData()
		{
			return new PresetPageData
			{
				Presets = _presets.Select((Preset p) => p.ToData()).ToList()
			};
		}

		public void FromData(PresetPageData data)
		{
			for (int i = 0; i < 6; i++)
			{
				_presets[i].FromData(data.Presets[i]);
			}
		}
	}
}
namespace SearchThing.Presets.Data
{
	public class PresetData
	{
		public string Name { get; set; } = "New Preset";


		public List<string> Barcodes { get; set; } = new List<string>();

	}
	public class PresetPageData
	{
		public List<PresetData> Presets { get; set; } = new List<PresetData>();

	}
}
namespace SearchThing.Patches
{
	[HarmonyPatch(typeof(AssetWarehouse))]
	public static class AssetWarehousePatches
	{
		[HarmonyPatch("AddPallet")]
		[HarmonyPostfix]
		public static void AddPallet_Postfix(Pallet pallet)
		{
			GlobalCrateManager.AddPallet(pallet);
		}
	}
	[HarmonyPatch(typeof(PopUpMenuView))]
	public static class PopupMenuViewPatches
	{
		[HarmonyPatch("Activate")]
		[HarmonyPrefix]
		public static void Activate_Prefix(PopUpMenuView __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				__instance.AddSpawnMenu();
			}
		}

		[HarmonyPatch("RemoveSpawnMenu")]
		[HarmonyPrefix]
		public static bool RemoveSpawnMenu_Prefix(PopUpMenuView __instance)
		{
			return false;
		}
	}
	[HarmonyPatch(typeof(RigManager))]
	public static class RigManagerPatches
	{
		private static bool CheckIfLocalRig(RigManager rigManager)
		{
			return FusionPlayer.IsLocalPlayer(rigManager);
		}

		[HarmonyPatch("SwapAvatarCrate")]
		[HarmonyPostfix]
		public static void SwapAvatarCrate_Prefix(RigManager __instance, Barcode barcode)
		{
			Crate crate = default(Crate);
			if (!((Object)(object)__instance == (Object)null) && (!Mod.IsFusionLoaded || CheckIfLocalRig(__instance)) && AssetWarehouse.ready && AssetWarehouse.Instance.TryGetCrate(barcode, ref crate))
			{
				HistoryManager.AddEntry(crate);
			}
		}
	}
	[HarmonyPatch(typeof(SpawnGun))]
	public class SpawnGunPatches
	{
		private static SpawnableCrate? _selectedCrate;

		private static SpawnGun? HeldSpawnGun { get; set; }

		public static void SelectCrate(SpawnableCrate crate)
		{
			_selectedCrate = crate;
			if ((Object)(object)HeldSpawnGun != (Object)null)
			{
				HeldSpawnGun.OnSpawnableSelected(_selectedCrate);
			}
		}

		public static void ClearSelectedCrate()
		{
			_selectedCrate = null;
		}

		private static bool IsHeldByLocalPlayer(SpawnGun spawnGun)
		{
			if (!NetworkSceneManager.IsLevelNetworked)
			{
				return true;
			}
			InteractableHost host = ((Gun)spawnGun).host;
			if ((Object)(object)host == (Object)null)
			{
				return false;
			}
			Hand hand = host.GetHand(0);
			RigManager val = ((hand != null) ? hand.manager : null);
			if ((Object)(object)val != (Object)null)
			{
				return FusionPlayer.IsLocalPlayer(val);
			}
			return false;
		}

		[HarmonyPatch("OnTriggerGripAttached")]
		[HarmonyPostfix]
		public static void OnTriggerGripAttached_Prefix(SpawnGun __instance, Hand hand)
		{
			if (!((Object)(object)__instance == (Object)null) && (!Mod.IsFusionLoaded || IsHeldByLocalPlayer(__instance)))
			{
				HeldSpawnGun = __instance;
				if ((Object)(object)_selectedCrate != (Object)null)
				{
					__instance.OnSpawnableSelected(_selectedCrate);
				}
			}
		}

		[HarmonyPatch("OnTriggerGripDetached")]
		[HarmonyPostfix]
		public static void OnTriggerGripDetached_Prefix(SpawnGun __instance, Hand hand)
		{
			if (!((Object)(object)__instance == (Object)null) && (!Mod.IsFusionLoaded || IsHeldByLocalPlayer(__instance)))
			{
				HeldSpawnGun = null;
			}
		}

		[HarmonyPatch("OnFire")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		public static void OnFire_Prefix(SpawnGun __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && (!Mod.IsFusionLoaded || IsHeldByLocalPlayer(__instance)))
			{
				SpawnableCrate selectedCrate = __instance._selectedCrate;
				if (!((Object)(object)selectedCrate == (Object)null))
				{
					HistoryManager.AddEntry((Crate)(object)selectedCrate);
				}
			}
		}

		[HarmonyPatch("OnSpawnableSelected")]
		[HarmonyPrefix]
		public static bool OnSpawnableSelected_Prefix(SpawnGun __instance, SpawnableCrate crate)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (Mod.IsFusionLoaded && !IsHeldByLocalPlayer(__instance))
			{
				return true;
			}
			_selectedCrate = crate;
			if ((Object)(object)((Il2CppObjectBase)crate).TryCast<AvatarCrate>() == (Object)null)
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(SpawnablesPanelView))]
	public static class ToolUiPatches
	{
		private static SpawnGun? _DummySpawnGun;

		private static SpawnGun GetDummySpawnGun()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_DummySpawnGun != (Object)null)
			{
				return _DummySpawnGun;
			}
			GameObject val = new GameObject("DummySpawnGun");
			val.SetActive(false);
			_DummySpawnGun = val.AddComponent<SpawnGun>();
			return _DummySpawnGun;
		}

		[HarmonyPatch("Activate")]
		[HarmonyPrefix]
		public static void SpawnablesPanelView_Activate_Prefix(SpawnablesPanelView __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.spawnGun != (Object)null))
			{
				__instance.spawnGun = GetDummySpawnGun();
				__instance._spawnGun_k__BackingField = GetDummySpawnGun();
			}
		}

		[HarmonyPatch("Activate")]
		[HarmonyPostfix]
		public static void SpawnablesPanelView_Activate_Postfix(SpawnablesPanelView __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				SpawnablesPanelManager.Load(__instance);
			}
		}

		[HarmonyPatch("SelectTab")]
		[HarmonyPostfix]
		public static void SpawnablesPanelView_SelectTab_Postfix(SpawnablesPanelView __instance, int idx)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				SpawnablesPanelManager.OnTabSelected(__instance, idx);
			}
		}

		[HarmonyPatch("NextPage")]
		[HarmonyPrefix]
		public static bool NextPage_Prefix(SpawnablesPanelView __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension))
			{
				return true;
			}
			if (!extension.IsSearchActive())
			{
				return true;
			}
			extension.ChangePanelPage(1);
			return false;
		}

		[HarmonyPatch("PrevPage")]
		[HarmonyPrefix]
		public static bool PrevPage_Prefix(SpawnablesPanelView __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension))
			{
				return true;
			}
			if (!extension.IsSearchActive())
			{
				return true;
			}
			extension.ChangePanelPage(-1);
			return false;
		}

		[HarmonyPatch("SelectItem")]
		[HarmonyPrefix]
		public static bool SelectItem_Prefix(SpawnablesPanelView __instance, int idx)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension))
			{
				return true;
			}
			if (!extension.IsSearchActive())
			{
				return true;
			}
			extension.OnSelectItem(idx);
			return false;
		}

		[HarmonyPatch("SelectCategory")]
		[HarmonyPrefix]
		public static bool SelectCategory_Prefix(SpawnablesPanelView __instance, int idx)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension))
			{
				return true;
			}
			if (!extension.IsSearchActive())
			{
				return true;
			}
			extension.SelectCategory(idx);
			return false;
		}

		[HarmonyPatch("NextTagPage")]
		[HarmonyPrefix]
		private static bool NextTagPage_Prefix(SpawnablesPanelView __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension))
			{
				return true;
			}
			if (!extension.IsSearchActive())
			{
				return true;
			}
			extension.ChangeTagPage(1);
			return false;
		}

		[HarmonyPatch("PrevTagPage")]
		[HarmonyPrefix]
		private static bool PrevTagPage_Prefix(SpawnablesPanelView __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension))
			{
				return true;
			}
			if (!extension.IsSearchActive())
			{
				return true;
			}
			extension.ChangeTagPage(-1);
			return false;
		}

		[HarmonyPatch("SwapSortButton")]
		[HarmonyPrefix]
		public static bool SwapSortButton_Prefix(SpawnablesPanelView __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension))
			{
				return true;
			}
			if (!extension.IsSearchActive())
			{
				return true;
			}
			extension.SwapSortButton();
			return false;
		}

		[HarmonyPatch("FavoriteItem")]
		[HarmonyPrefix]
		public static bool FavoriteItem_Prefix(SpawnablesPanelView __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension))
			{
				return true;
			}
			if (!extension.IsSearchActive())
			{
				return true;
			}
			extension.OnFavoriteButton();
			return false;
		}

		[HarmonyPatch("SelectItem")]
		[HarmonyPostfix]
		public static void SelectItem_Postfix(SpawnablesPanelView __instance, int idx)
		{
			if (!((Object)(object)__instance == (Object)null) && SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension) && extension.IsSearchActive())
			{
				extension.RenderFavoriteButton();
			}
		}

		[HarmonyPatch("SelectItem")]
		[HarmonyPrefix]
		public static void ToggleFavorite_Prefix(SpawnablesPanelView __instance, int idx)
		{
			if (!((Object)(object)__instance == (Object)null) && SpawnablesPanelManager.TryGet(__instance, out SpawnablePanelExtension extension) && extension.IsSearchActive())
			{
				extension.RenderFavoriteButton();
			}
		}
	}
}
namespace SearchThing.Patches.Compatibility
{
	public static class FusionNetworkSpawnPatch
	{
		private static class FusionIntegrationPatches
		{
			[HarmonyPostfix]
			public static void RegisterSpawnHistory(ReceivedMessage received)
			{
				if (NetworkInfo.HasServer)
				{
					SerializedSpawnData val = ((ReceivedMessage)(ref received)).ReadData<SerializedSpawnData>();
					if (val != null)
					{
						FusionSpawnHistory.AddEntry(val.Barcode, ((ReceivedMessage)(ref received)).Sender);
					}
				}
			}
		}

		private static void TryPatch()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			try
			{
				Harmony val = new Harmony("com.mash.searchthing.fusionspawnhistoryintegration");
				MethodInfo method = typeof(SpawnRequestMessage).GetMethod("OnHandleMessage", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method == null)
				{
					MelonLogger.Warning("MIDT integration: Could not find OnSpawnDelegateFusion method");
					return;
				}
				MethodInfo method2 = typeof(FusionIntegrationPatches).GetMethod("RegisterSpawnHistory");
				if (val.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null) != null)
				{
					MelonLogger.Msg("Fusion integration: Successfully applied RegisterSpawnHistory patch");
				}
				else
				{
					MelonLogger.Warning("Fusion integration: Failed to apply patch, harmony.Patch returned null");
				}
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Failed to patch MIDT integration: {value}");
			}
		}

		public static void TryInitialize()
		{
			if (Mod.IsFusionLoaded)
			{
				TryPatch();
			}
		}
	}
}
namespace SearchThing.Keyboard
{
	public class Keyboard
	{
		private static readonly Vector2 KeySize = new Vector2(80f, 80f);

		private static readonly Vector2 KeySpacing = new Vector2(10f, 10f);

		private const int KeyboardLayer = 5;

		private static readonly string[] KeyRows = new string[4] { "1234567890", "QWERTYUIOP", "ASDFGHJKL", "ZXCVBNM" };

		private readonly GameObject _parent;

		private GameObject? _keyboardRoot;

		private string _text = "";

		private readonly TMP_FontAsset _font;

		private readonly Sprite _bg;

		private readonly Sprite _outlineBg;

		public event Action<string>? OnTextChanged;

		public Keyboard(GameObject parent, ButtonReferenceHolder resources)
		{
			_parent = parent;
			_font = ((TMP_Text)resources.tmp).font;
			_bg = resources.background.sprite;
			_outlineBg = resources.highlight.sprite;
			Create();
		}

		public void SetText(string text, bool triggerEvent = true)
		{
			_text = text;
			if (triggerEvent)
			{
				OnInternalTextChanged(_text);
			}
		}

		private void Create()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			_keyboardRoot = new GameObject("Keyboard");
			_keyboardRoot.transform.SetParent(_parent.transform, false);
			_keyboardRoot.transform.localPosition = new Vector3(-85f, -450f, 0f);
			_keyboardRoot.transform.localRotation = Quaternion.Euler(0f, 0f, 0f);
			_keyboardRoot.layer = 5;
			_keyboardRoot.AddComponent<CanvasRenderer>();
			((Graphic)_keyboardRoot.AddComponent<Image>()).color = Color.clear;
			RectTransform component = _keyboardRoot.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 0f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(0.5f, 0.5f);
			component.offsetMin = new Vector2(10f, 10f);
			component.offsetMax = new Vector2(-10f, -10f);
			CreateKeys();
		}

		private void CreateKey(string text, Vector2 position, Vector2 size, Action action, Vector2? margin = null)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_keyboardRoot == (Object)null)
			{
				throw new InvalidOperationException("Keyboard root is not initialized.");
			}
			GameObject val = new GameObject("Key_" + text);
			val.transform.SetParent(_keyboardRoot.transform, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val.AddComponent<CanvasRenderer>();
			GameObject val3 = new GameObject("bg");
			val3.transform.SetParent(val.transform, false);
			val3.AddComponent<RectTransform>().sizeDelta = size;
			Image val4 = val3.AddComponent<Image>();
			val4.sprite = _bg;
			val4.type = (Type)1;
			GameObject val5 = new GameObject("image_backline");
			val5.transform.SetParent(val.transform, false);
			val5.AddComponent<RectTransform>().sizeDelta = size;
			Image obj = val5.AddComponent<Image>();
			obj.sprite = _outlineBg;
			obj.type = (Type)1;
			((Graphic)obj).raycastTarget = false;
			obj.fillCenter = false;
			GameObject val6 = new GameObject("Collider");
			val6.transform.SetParent(val.transform, false);
			BoxCollider obj2 = val6.AddComponent<BoxCollider>();
			Vector2 val7 = size + margin.GetValueOrDefault();
			obj2.size = new Vector3(val7.x, val7.y, 1f);
			((Collider)obj2).isTrigger = true;
			Button obj3 = val.AddComponent<Button>();
			((UnityEvent)obj3.onClick).AddListener(UnityAction.op_Implicit(action));
			((Selectable)obj3).transition = (Transition)1;
			((Selectable)obj3).targetGraphic = (Graphic)(object)val4;
			ColorBlock colors = ((Selectable)obj3).colors;
			((ColorBlock)(ref colors)).normalColor = new Color(0f, 0f, 0f, 0.6118f);
			((ColorBlock)(ref colors)).highlightedColor = new Color(0.6038f, 0.6038f, 0.6038f, 0.209f);
			((ColorBlock)(ref colors)).pressedColor = new Color(0.7176f, 0.7176f, 0.7176f, 1f);
			((ColorBlock)(ref colors)).selectedColor = ((ColorBlock)(ref colors)).normalColor;
			((Selectable)obj3).colors = colors;
			val.AddComponent<ButtonHoverClick>();
			GameObject val8 = new GameObject("Text");
			val8.transform.SetParent(val.transform, false);
			TextMeshProUGUI obj4 = val8.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj4).text = text;
			((TMP_Text)obj4).font = _font;
			((TMP_Text)obj4).fontSize = 40f;
			((Graphic)obj4).color = Color.white;
			((TMP_Text)obj4).alignment = (TextAlignmentOptions)514;
			RectTransform component = val8.GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			val2.sizeDelta = size;
			val2.anchoredPosition = position;
			val.layer = 5;
			val6.layer = 5;
		}

		private void CreateKeys()
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_keyboardRoot == (Object)null)
			{
				return;
			}
			float num = 0f;
			string[] keyRows = KeyRows;
			Vector2 position = default(Vector2);
			foreach (string text in keyRows)
			{
				float num2 = (0f - ((float)text.Length * KeySize.x + (float)(text.Length - 1) * KeySpacing.x)) / 2f;
				for (int j = 0; j < text.Length; j++)
				{
					char keyChar = text[j];
					((Vector2)(ref position))..ctor(num2 + (float)j * (KeySize.x + KeySpacing.x) + KeySize.x / 2f, num);
					CreateKey(keyChar.ToString(), position, KeySize, delegate
					{
						OnKeyPress(keyChar);
					}, KeySpacing / 2f);
				}
				num -= KeySize.y + KeySpacing.y;
			}
			CreateKey("Backspace", new Vector2(-280f, num), new Vector2(240f, 80f), OnBackspace);
			CreateKey("Space", new Vector2(0f, num), new Vector2(249f, 80f), OnSpace);
			CreateKey("Clear", new Vector2(280f, num), new Vector2(240f, 80f), delegate
			{
				SetText("");
			});
		}

		private void OnInternalTextChanged(string text)
		{
			this.OnTextChanged?.Invoke(text);
		}

		private void OnKeyPress(char key)
		{
			_text += key;
			OnInternalTextChanged(_text);
		}

		private void OnBackspace()
		{
			if (_text.Length > 0)
			{
				string text = _text;
				_text = text.Substring(0, text.Length - 1);
				OnInternalTextChanged(_text);
			}
		}

		private void OnSpace()
		{
			_text += " ";
			OnInternalTextChanged(_text);
		}

		public void Show()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_keyboardRoot == (Object)null))
			{
				_keyboardRoot.SetActive(true);
				_keyboardRoot.transform.SetAsLastSibling();
				_keyboardRoot.transform.localPosition = new Vector3(-85f, -450f, 0f);
			}
		}

		public void Hide()
		{
			if (!((Object)(object)_keyboardRoot == (Object)null))
			{
				_keyboardRoot.SetActive(false);
			}
		}
	}
}
namespace SearchThing.History
{
	public interface IHistorySearchableCrate : ISearchableCrate
	{
		new DateTime DateAdded { get; set; }
	}
	public class BoundCrateList<TCrate> : ISearchableCrateList<TCrate> where TCrate : class, IHistorySearchableCrate
	{
		private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

		private readonly Dictionary<string, TCrate> _crates = new Dictionary<string, TCrate>();

		private readonly LinkedList<string> _accessOrder = new LinkedList<string>();

		private readonly Dictionary<string, LinkedListNode<string>> _nodes = new Dictionary<string, LinkedListNode<string>>();

		private const int MaxEntries = 100;

		public void AddCrate(TCrate crate)
		{
			_lock.EnterWriteLock();
			try
			{
				string id = crate.Barcode._id;
				if (_crates.TryGetValue(id, out var value))
				{
					value.DateAdded = DateTime.UtcNow;
					_accessOrder.Remove(_nodes[id]);
					_nodes[id] = _accessOrder.AddLast(id);
					return;
				}
				crate.DateAdded = DateTime.UtcNow;
				_crates[id] = crate;
				_nodes[id] = _accessOrder.AddLast(id);
				if (_crates.Count > 100)
				{
					RemoveOldest();
				}
			}
			finally
			{
				_lock.ExitWriteLock();
			}
		}

		private void RemoveOldest()
		{
			if (_accessOrder.First != null)
			{
				string value = _accessOrder.First.Value;
				_accessOrder.RemoveFirst();
				_nodes.Remove(value);
				_crates.Remove(value);
			}
		}

		public IEnumerable<TCrate> GetCrates()
		{
			_lock.EnterReadLock();
			try
			{
				return _crates.Values.ToArray();
			}
			finally
			{
				_lock.ExitReadLock();
			}
		}
	}
	public class HistoryCrateEntry : IHistorySearchableCrate, ISearchableCrate
	{
		public ISearchableCrate Crate { get; }

		public DateTime DateAdded { get; set; }

		public SearchTag Name => Crate.Name;

		public SearchTag PalletName => Crate.PalletName;

		public SearchTag Author => Crate.Author;

		public SearchTag[] Tags => Crate.Tags;

		public string Description => Crate.Description;

		public bool Redacted => Crate.Redacted;

		public CrateType CrateType => Crate.CrateType;

		public CrateSubType CrateSubType => Crate.CrateSubType;

		public int Salt => Crate.Salt;

		public int Score => 0;

		public Barcode Barcode => Crate.Barcode;

		public HistoryCrateEntry(ISearchableCrate crate, DateTime dateAdded)
		{
			Crate = crate;
			DateAdded = dateAdded;
		}
	}
	public static class HistoryManager
	{
		private static readonly BoundCrateList<HistoryCrateEntry> BoundEntries = new BoundCrateList<HistoryCrateEntry>();

		public static void AddEntry(Crate crate)
		{
			BoundEntries.AddCrate(new HistoryCrateEntry(new SearchableCrate(crate), DateTime.Now));
		}

		public static void SearchAsync(string query, ISearchOrder order, Func<ISearchableCrate, bool> filter, Action<SearchResults<HistoryCrateEntry>> callback)
		{
			SearchManager.SearchAsync(query, BoundEntries, filter, order, callback);
		}
	}
}
namespace SearchThing.Fusion
{
	public static class FusionBlacklistHelper
	{
		private static List<string>? GetBlacklist()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Traverse val = new Traverse(typeof(ModBlacklist)).Field("_blacklistedModIds");
			if (val.FieldExists())
			{
				return val.GetValue<List<string>>();
			}
			MelonLogger.Error("Failed to get blacklist: _blacklistedModIds field not found");
			return new List<string>();
		}

		private static void SaveBlacklist(List<string> blacklist)
		{
			using StreamWriter streamWriter = File.CreateText(PersistentData.GetPath("mod_blacklist.txt"));
			streamWriter.WriteLine("# This file is for preventing the use of specific mods while in a server.");
			streamWriter.WriteLine("# To blacklist a mod, add a mod identifier on its own line.");
			streamWriter.WriteLine("# A mod identifier can either be its mod.io number ID, mod.io URL ID, or AssetWarehouse barcode.");
			streamWriter.WriteLine("# When the identifier is a number ID or URL ID, it will block the mod from being downloaded.");
			streamWriter.WriteLine("# On the other hand, if the identifier is a AssetWarehouse barcode, it will prevent the mod from being used.");
			streamWriter.WriteLine("# For server hosts, blacklisting a barcode prevents all clients from using that mod.");
			streamWriter.WriteLine("#");
			streamWriter.WriteLine("# Examples");
			streamWriter.WriteLine("# -------------------------------------");
			streamWriter.WriteLine("# Number ID");
			streamWriter.WriteLine("# 4057308");
			streamWriter.WriteLine("#");
			streamWriter.WriteLine("# URL ID");
			streamWriter.WriteLine("# test-chambers");
			streamWriter.WriteLine("#");
			streamWriter.WriteLine("# Barcode");
			streamWriter.WriteLine("# SLZ.TestChambers.Level.TestChamber02");
			streamWriter.WriteLine("# SLZ.TestChambers.Level.TestChamber07");
			streamWriter.WriteLine("# -------------------------------------");
			foreach (string item in blacklist)
			{
				streamWriter.WriteLine(item);
			}
		}

		public static void ToggleBlacklist(string barcode)
		{
			List<string> blacklist = GetBlacklist();
			if (blacklist == null)
			{
				MelonLogger.Error("Cannot toggle blacklist: failed to retrieve blacklist");
				return;
			}
			if (blacklist.Contains(barcode))
			{
				blacklist.Remove(barcode);
			}
			else
			{
				blacklist.Add(barcode);
			}
			SaveBlacklist(blacklist);
		}

		public static bool IsBlacklisted(string barcodeID)
		{
			return ModBlacklist.IsBlacklisted(barcodeID);
		}
	}
	public class FusionBlocklistPage : BasicSearchPanel<ISearchableCrate>
	{
		private static readonly Sprite BlockIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.BlockIcon.png");

		public override string Tag => "Fusion Blocklist";

		public override bool CanAssign => false;

		public override Sprite ItemFunctionIcon => BlockIcon;

		public override bool HasItemFunction => true;

		public override ISelectableSearchOrder[] SupportedOrders { get; } = new ISelectableSearchOrder[5]
		{
			new IsBlockedFilter(),
			new DateNewAddedSearchOrder(),
			new DateOldAddedSearchOrder(),
			new ScoreSearchOrder(),
			new AlphabeticalSearchOrder()
		};


		public override Color? GetItemFunctionHighlight(SpawnablePanelExtension extension, ISearchableCrate? crate)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			return (crate != null && FusionBlacklistHelper.IsBlacklisted(crate.Barcode._id)) ? Color.red : Color.green;
		}

		public override void OnItemFunction(SpawnablePanelExtension extension, ISearchableCrate crate)
		{
			FusionBlacklistHelper.ToggleBlacklist(crate.Barcode._id);
			MakeDirty();
			extension.RequestRefresh();
		}

		protected override void Search(string query, ISearchOrder order, Action<SearchResults<ISearchableCrate>> callback)
		{
			SearchManager.SearchAsync(query, GlobalCrateManager.GetCrates(), (ISearchableCrate c) => c.CrateType == CrateType.Prop, order, callback);
		}
	}
	public class FusionPage : ISearchPage
	{
		public bool IsVisible => Mod.IsFusionLoaded;

		public IReadOnlyList<ISearchPanel> Panels { get; } = new ISearchPanel[2]
		{
			new FusionBlocklistPage(),
			new FusionSpawnHistoryPage()
		};

	}
	public record FusionSpawnHistoryEntry(Barcode Barcode, PlayerID? SpawnerId) : IHistorySearchableCrate, ISearchableCrate
	{
		public ISearchableCrate? Crate { get; } = GlobalCrateManager.GetCrate(Barcode);


		public SearchTag Name => Crate?.Name ?? UnknownCrateTag;

		public SearchTag PalletName => Crate?.PalletName ?? UnknownCrateTag;

		public SearchTag Author => Crate?.Author ?? UnknownCrateTag;

		public SearchTag[] Tags => Crate?.Tags ?? Array.Empty<SearchTag>();

		public string Description => Crate?.Description ?? "No description available.";

		public bool Redacted => Crate?.Redacted ?? false;

		public CrateType CrateType => Crate?.CrateType ?? CrateType.Invalid;

		public CrateSubType CrateSubType => Crate?.CrateSubType ?? CrateSubType.None;

		public int Salt => Crate?.Salt ?? 0;

		public int Score => Crate?.Score ?? 0;

		public DateTime DateAdded { get; set; }

		private static readonly SearchTag UnknownCrateTag = new SearchTag("Unknown Crate");

		[CompilerGenerated]
		protected FusionSpawnHistoryEntry(FusionSpawnHistoryEntry original)
		{
			Barcode = original.Barcode;
			SpawnerId = original.SpawnerId;
			Crate = original.Crate;
			DateAdded = original.DateAdded;
		}
	}
	public static class FusionSpawnHistory
	{
		private static readonly BoundCrateList<FusionSpawnHistoryEntry> BoundEntries = new BoundCrateList<FusionSpawnHistoryEntry>();

		public static void AddEntry(string barcode, byte? smallPlayerId)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			PlayerID spawnerId = (smallPlayerId.HasValue ? PlayerIDManager.GetPlayerID(smallPlayerId.Value) : null);
			Barcode barcode2 = new Barcode(barcode);
			BoundEntries.AddCrate(new FusionSpawnHistoryEntry(barcode2, spawnerId));
		}

		public static void SearchAsync(string query, ISearchOrder order, Func<ISearchableCrate, bool> filter, Action<SearchResults<FusionSpawnHistoryEntry>> callback)
		{
			SearchManager.SearchAsync(query, BoundEntries, filter, order, callback);
		}
	}
	public class FusionSpawnHistoryPage : BasicSearchPanel<FusionSpawnHistoryEntry>
	{
		private static readonly Sprite BlockIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.BlockIcon.png");

		public override string Tag => "Fusion Spawn History";

		public override bool IsVisible => NetworkInfo.HasServer;

		public override bool CanAssign => false;

		public override bool ResearchOnPageChange => true;

		public override Sprite ItemFunctionIcon => BlockIcon;

		public override bool HasItemFunction => true;

		public override ISelectableSearchOrder[] SupportedOrders { get; } = new ISelectableSearchOrder[5]
		{
			new DateNewAddedSearchOrder(),
			new DateOldAddedSearchOrder(),
			new ScoreSearchOrder(),
			new AlphabeticalSearchOrder(),
			new RandomSearchOrder()
		};


		public override Color? GetItemFunctionHighlight(SpawnablePanelExtension extension, ISearchableCrate? crate)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			return (crate != null && FusionBlacklistHelper.IsBlacklisted(crate.Barcode._id)) ? Color.red : Color.green;
		}

		public override void OnItemFunction(SpawnablePanelExtension extension, ISearchableCrate crate)
		{
			FusionBlacklistHelper.ToggleBlacklist(crate.Barcode._id);
			extension.RequestRefresh();
		}

		public override ItemRenderData GetRenderDataForCrate(FusionSpawnHistoryEntry crate)
		{
			PlayerID? spawnerId = crate.SpawnerId;
			string input = default(string);
			string text = ((spawnerId != null && spawnerId.IsValid && MetadataHelper.TryGetDisplayName(crate.SpawnerId, ref input)) ? StringHelper.RemoveUnityRichText(input) : "Unknown");
			return new ItemRenderData
			{
				Name = crate.Name.Original + " (" + text + ")",
				Description = crate.Description,
				Author = crate.Author.Original,
				PalletName = crate.PalletName.Original,
				Tags = crate.Tags.Select((SearchTag t) => t.Original).ToArray(),
				Icon = CrateIconProvider.GetIcon(crate)
			};
		}

		protected override void Search(string query, ISearchOrder order, Action<SearchResults<FusionSpawnHistoryEntry>> callback)
		{
			FusionSpawnHistory.SearchAsync(query, order, (ISearchableCrate c) => c.CrateType == CrateType.Prop, callback);
		}
	}
	public class IsBlockedFilter : ISelectableSearchOrder, ISearchOrder
	{
		public string Name => "Blocked First";

		public int Order(ISearchableCrate searchableCrate)
		{
			if (FusionBlacklistHelper.IsBlacklisted(searchableCrate.Barcode._id))
			{
				return 10000 + searchableCrate.Score;
			}
			return searchableCrate.Score;
		}
	}
}
namespace SearchThing.Extensions
{
	public class SpawnablePageProvider
	{
		private readonly ISearchPage[] _basePages = new ISearchPage[2]
		{
			new BasicSearchPage(new PropTagSearchPanel(), new AvatarTagSearchPanel(), new PropHistorySearchPanel(), new AvatarHistorySearchPanel(), new LevelTagSearchPanel()),
			new FusionPage()
		};

		public int PageCount => GetAllPages().Count((ISearchPage c) => c.IsVisible);

		public IEnumerable<ISearchPage> GetAllPages()
		{
			return _basePages.Concat(PresetManager.GetPages());
		}

		public ISearchPage[] GetVisiblePages()
		{
			return (from p in GetAllPages()
				where p.IsVisible
				select p).ToArray();
		}

		public ISearchPage GetBasePage()
		{
			return _basePages[0];
		}

		public ISearchPage GetPage(int index)
		{
			ISearchPage[] visiblePages = GetVisiblePages();
			int num = Math.Clamp(index, 0, visiblePages.Length - 1);
			return visiblePages[num];
		}
	}
	public class SpawnablePanelExtension
	{
		public const int SearchTabIndex = 5;

		private const string SourceTabButtonPath = "group_tabs/grid_tabs/button_tab_05";

		private const string SearchTabName = "button_tab_search";

		private string _searchQuery = "";

		private readonly SpawnablesPanelView _panelView;

		private readonly SearchThing.Keyboard.Keyboard _keyboard;

		private readonly List<ItemButton> _itemButtons = new List<ItemButton>();

		private GameObject _itemPageNextButton;

		private GameObject _itemPagePreviousButton;

		private readonly List<TagButton> _tagButtons = new List<TagButton>();

		private GameObject _tagPageNextButton;

		private GameObject _tagPagePreviousButton;

		private static readonly Sprite TabIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.SearchIcon.png");

		private SpawnInfoFocus _currentFocus = SpawnInfoFocus.SelectedItem;

		private string _editValue = "";

		private Image _fadedButtonImage;

		private Image _favoriteButtonImage;

		private Sprite _originalFavoriteSprite;

		private Color _originalFavoriteColor = Color.white;

		private TextMeshPro _sortButtonText;

		private GameObject _sortButtonObject;

		private int _renderedTagPageIndex;

		private int _selectedItemIndex;

		private int _selectedTagIndex;

		private int _selectedPageIndex;

		private readonly SpawnablePageProvider _pages = new SpawnablePageProvider();

		public bool IsEditing { get; private set; }

		private void AddTab()
		{
			Transform val = ((Component)_panelView).transform.Find("group_tabs/grid_tabs/button_tab_05");
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Error("Failed to find source button at path: group_tabs/grid_tabs/button_tab_05");
			}
			else
			{
				if ((Object)(object)((Component)val).transform.FindChild("button_tab_search") != (Object)null)
				{
					return;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, val.parent);
				((Object)val2).name = "button_tab_search";
				Button component = val2.GetComponent<Button>();
				ButtonReferenceHolder component2 = val2.GetComponent<ButtonReferenceHolder>();
				if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
				{
					MelonLogger.Error("Failed to find Button component in search button.");
					return;
				}
				((UnityEventBase)component.onClick).RemoveAllListeners();
				((UnityEvent)component.onClick).AddListener(UnityAction.op_Implicit((Action)OnSearchTabClicked));
				Transform obj = val2.transform.FindChild("image_icon");
				Image val3 = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null);
				if ((Object)(object)val3 != (Object)null)
				{
					val3.sprite = TabIcon;
				}
				List<ButtonReferenceHolder> list = ((IEnumerable<ButtonReferenceHolder>)_panelView.tabButtons).ToList();
				list.Add(component2);
				_panelView.tabButtons = new Il2CppReferenceArray<ButtonReferenceHolder>(list.ToArray());
				Transform obj2 = val2.transform.Find("text_spawnable_val");
				TextMeshPro val4 = ((obj2 != null) ? ((Component)obj2).GetComponent<TextMeshPro>() : null);
				if ((Object)(object)val4 != (Object)null)
				{
					((TMP_Text)val4).text = "Search";
				}
				else
				{
					MelonLogger.Error("Failed to find text component in search button.");
				}
			}
		}

		private void OnSearchTabClicked()
		{
			_panelView.SelectTab(5);
		}

		private void FetchSortButton()
		{
			_sortButtonText = ((Component)((Component)_panelView).transform.Find("group_treePath/text_treePath")).GetComponent<TextMeshPro>();
			((TMP_Text)_sortButtonText).alignment = (TextAlignmentOptions)513;
			_sortButtonObject = ((Component)((Component)_panelView).transform.Find("group_treePath/button_SwapSort")).gameObject;
		}

		private void FetchFavoriteButton()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			Transform val = ((Component)_panelView).transform.Find("group_selectedInfo/button_Favorite");
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Error("Failed to find favorite button.");
				return;
			}
			ButtonReferenceHolder component = ((Component)val).GetComponent<ButtonReferenceHolder>();
			_fadedButtonImage = component.highlight;
			_favoriteButtonImage = component.special;
			if ((Object)(object)_favoriteButtonImage == (Object)null || (Object)(object)_fadedButtonImage == (Object)null)
			{
				MelonLogger.Error("Failed to find Image component in favorite button.");
				return;
			}
			_originalFavoriteSprite = _favoriteButtonImage.sprite;
			_originalFavoriteColor = ((Graphic)_favoriteButtonImage).color;
		}

		private void FetchTagButtons()
		{
			foreach (ButtonReferenceHolder item in (Il2CppArrayBase<ButtonReferenceHolder>)(object)_panelView.treeButtons)
			{
				if (!((Object)(object)item == (Object)null))
				{
					_tagButtons.Add(new TagButton(item));
				}
			}
			_tagPageNextButton = ((Component)_panelView.treeScrollDownButton).gameObject;
			_tagPagePreviousButton = ((Component)_panelView.treeScrollUpButton).gameObject;
		}

		private void FetchItemButtons()
		{
			foreach (ButtonReferenceHolder item in (Il2CppArrayBase<ButtonReferenceHolder>)(object)_panelView.itemButtons)
			{
				if (!((Object)(object)item == (Object)null))
				{
					_itemButtons.Add(new ItemButton(item));
				}
			}
			_itemPageNextButton = ((Component)_panelView.itemScrollDownButton).gameObject;
			_itemPagePreviousButton = ((Component)_panelView.itemScrollUpButton).gameObject;
		}

		public SpawnablePanelExtension(SpawnablesPanelView panelView)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			_panelView = panelView;
			AddTab();
			FetchSortButton();
			FetchFavoriteButton();
			FetchItemButtons();
			FetchTagButtons();
			ButtonReferenceHolder component = ((Component)((Component)_panelView).transform.Find("group_spawnSelect/section_SpawnablesList/grid_buttons/button_item_01")).GetComponent<ButtonReferenceHolder>();
			_keyboard = new SearchThing.Keyboard.Keyboard(((Component)_panelView).gameObject, component);
			_keyboard.OnTextChanged += OnSearchQueryChanged;
			_keyboard.Hide();
			RequestRefresh();
		}

		public bool IsPanelSelected(ISearchPanel page)
		{
			return GetSelectedPanel().Id == page.Id;
		}

		private ISearchPage GetRenderPage()
		{
			int renderedTagPageIndex = _renderedTagPageIndex;
			if (renderedTagPageIndex < 0 || renderedTagPageIndex >= _pages.PageCount)
			{
				return _pages.GetBasePage();
			}
			return _pages.GetPage(renderedTagPageIndex);
		}

		private ISearchPanel? GetRenderPanel(int idx)
		{
			ISearchPage renderPage = GetRenderPage();
			if (idx < 0 || idx >= renderPage.Panels.Count)
			{
				return null;
			}
			return renderPage.Panels[idx];
		}

		private ISearchPage GetSelectedPage()
		{
			int selectedPageIndex = _selectedPageIndex;
			if (selectedPageIndex < 0 || selectedPageIndex >= _pages.PageCount)
			{
				return _pages.GetBasePage();
			}
			return _pages.GetPage(selectedPageIndex);
		}

		private ISearchPanel GetSelectedPanel(int idx = -1)
		{
			ISearchPage selectedPage = GetSelectedPage();
			int selectedTagIndex = _selectedTagIndex;
			if (selectedTagIndex < 0 || selectedTagIndex >= selectedPage.Panels.Count)
			{
				return selectedPage.Panels[0];
			}
			return selectedPage.Panels[(idx >= 0) ? idx : selectedTagIndex];
		}

		private void OnSearchQueryChanged(string query)
		{
			if (IsEditing)
			{
				_editValue = query;
				RenderTags();
				RenderFocus();
			}
			else
			{
				_searchQuery = query;
				RequestRefresh();
			}
		}

		public void SetIsEditing(bool isEditing)
		{
			if (isEditing == IsEditing)
			{
				return;
			}
			IsEditing = isEditing;
			ISearchPanel selectedPanel = GetSelectedPanel();
			if (!selectedPanel.TagEditable)
			{
				IsEditing = false;
				return;
			}
			if (IsEditing)
			{
				PresetManager.IsAssignmentMode = false;
				_editValue = selectedPanel.Tag;
				_keyboard.SetText(_editValue, triggerEvent: false);
			}
			else
			{
				_keyboard.SetText(_searchQuery, triggerEvent: false);
				selectedPanel.OnTagEdited(this, _editValue);
				RenderTags();
			}
			RequestRefresh();
		}

		public void RequestRefresh()
		{
			ISearchPanel selectedPanel = GetSelectedPanel();
			selectedPanel.Query = _searchQuery;
			selectedPanel.RequestSearch(this);
		}

		public ISearchableCrate? GetSelectedSpawnable()
		{
			ISearchPanel selectedPanel = GetSelectedPanel();
			int selectedItemIndex = _selectedItemIndex;
			if (selectedItemIndex < 0 || selectedItemIndex >= 12)
			{
				return null;
			}
			return selectedPanel.GetCrateAt(_selectedItemIndex);
		}

		public void RenderTags()
		{
			ISearchableCrate selectedSpawnable = GetSelectedSpawnable();
			for (int i = 0; i < _tagButtons.Count; i++)
			{
				TagButton tagButton = _tagButtons[i];
				ISearchPanel renderPanel = GetRenderPanel(i);
				if (renderPanel == null || !renderPanel.IsVisible)
				{
					tagButton.Hide();
					continue;
				}
				bool flag = i == _selectedTagIndex && _selectedPageIndex == _renderedTagPageIndex;
				string tag = ((IsEditing && _currentFocus == SpawnInfoFocus.SelectedPage && flag) ? _editValue : renderPanel.Tag);
				Color? color = renderPanel.IsForceHighlighted(this, selectedSpawnable);
				tagButton.SetTag(tag, flag || color.HasValue, color);
			}
			int pageCount = _pages.PageCount;
			((TMP_Text)_panelView.treePageText).text = $"{_renderedTagPageIndex + 1}/{pageCount}";
			_tagPageNextButton.SetActive(_renderedTagPageIndex < pageCount - 1);
			_tagPagePreviousButton.SetActive(_renderedTagPageIndex > 0);
		}

		public void RenderSpecialButtons(ISearchPanel? selectedPanel = null)
		{
			if (selectedPanel == null)
			{
				selectedPanel = GetSelectedPanel();
			}
			if (selectedPanel.SupportedOrders.Length > 1)
			{
				_sortButtonObject.SetActive(true);
				((Component)_sortButtonText).gameObject.SetActive(true);
				ISelectableSearchOrder selectableSearchOrder = selectedPanel.SupportedOrders[selectedPanel.SelectedOrderIndex];
				((TMP_Text)_sortButtonText).text = selectableSearchOrder.Name;
				((TMP_Text)_sortButtonText).alignment = (TextAlignmentOptions)513;
			}
			else
			{
				_sortButtonObject.SetActive(false);
				((Component)_sortButtonText).gameObject.SetActive(false);
			}
			RenderFavoriteButton();
		}

		public (Sprite?, Color?) GetFavoriteSprite()
		{
			ISearchPanel selectedPanel = GetSelectedPanel();
			if (_currentFocus == SpawnInfoFocus.SelectedItem && selectedPanel.HasItemFunction)
			{
				ISearchableCrate selectedSpawnable = GetSelectedSpawnable();
				Color? itemFunctionHighlight = selectedPanel.GetItemFunctionHighlight(this, selectedSpawnable);
				return (selectedPanel.ItemFunctionIcon ?? _originalFavoriteSprite, itemFunctionHighlight);
			}
			if (selectedPanel.HasPanelFunction)
			{
				Color? panelFunctionHighlight = selectedPanel.GetPanelFunctionHighlight(this);
				return (selectedPanel.PanelFunctionIcon ?? _originalFavoriteSprite, panelFunctionHighlight);
			}
			return (null, null);
		}

		public void RenderFavoriteButton()
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			bool flag = (Object)(object)_panelView.selectedObject != (Object)null && _panelView.favoriteCrates.ContainsKey(((Scannable)_panelView.selectedObject)._barcode._id);
			if (!IsSearchActive())
			{
				((Behaviour)_fadedButtonImage).enabled = !flag;
				((Behaviour)_favoriteButtonImage).enabled = flag;
				_favoriteButtonImage.sprite = _originalFavoriteSprite;
				_fadedButtonImage.sprite = _originalFavoriteSprite;
				return;
			}
			(Sprite?, Color?) favoriteSprite = GetFavoriteSprite();
			Sprite item = favoriteSprite.Item1;
			bool flag2 = (Object)(object)item != (Object)null;
			Color? item2 = favoriteSprite.Item2;
			bool hasValue = item2.HasValue;
			((Behaviour)_fadedButtonImage).enabled = !hasValue && flag2;
			((Behaviour)_favoriteButtonImage).enabled = hasValue && flag2;
			_fadedButtonImage.sprite = item;
			_favoriteButtonImage.sprite = item;
			if (hasValue)
			{
				((Graphic)_favoriteButtonImage).color = item2.Value;
			}
		}

		public void RenderFocus()
		{
			if (_currentFocus == SpawnInfoFocus.SelectedItem)
			{
				ISearchableCrate selectedSpawnable = GetSelectedSpawnable();
				if (selectedSpawnable == null)
				{
					((TMP_Text)_panelView.selectedTitle).text = "";
					((TMP_Text)_panelView.selectedDescription).text = "";
					((TMP_Text)_panelView.selectedAuthor).text = "";
					((TMP_Text)_panelView.selectedPallet).text = "";
					((TMP_Text)_panelView.selectedTags).text = "";
				}
				else
				{
					((TMP_Text)_panelView.selectedTitle).text = selectedSpawnable.Name.Original;
					((TMP_Text)_panelView.selectedDescription).text = selectedSpawnable.Description;
					((TMP_Text)_panelView.selectedPallet).text = selectedSpawnable.PalletName.Original;
					((TMP_Text)_panelView.selectedAuthor).text = selectedSpawnable.Author.Original ?? "";
					((TMP_Text)_panelView.selectedTags).text = string.Join(", ", selectedSpawnable.Tags.Select((SearchTag t) => t.Original));
				}
			}
			else
			{
				ISearchPanel selectedPanel = GetSelectedPanel();
				((TMP_Text)_panelView.selectedTitle).text = (IsEditing ? _editValue : selectedPanel.Tag);
				((TMP_Text)_panelView.selectedDescription).text = "N/A";
				((TMP_Text)_panelView.selectedAuthor).text = "N/A";
				((TMP_Text)_panelView.selectedPallet).text = "N/A";
				((TMP_Text)_panelView.selectedTags).text = "N/A";
			}
			RenderFavoriteButton();
		}

		public void RenderItemButtons()
		{
			ISearchPanel selectedPanel = GetSelectedPanel();
			IReadOnlyList<ItemRenderData> page = selectedPanel.GetPage(selectedPanel.Page);
			if (_selectedItemIndex >= page.Count)
			{
				_selectedItemIndex = page.Count - 1;
				RenderFocus();
			}
			for (int i = 0; i < _itemButtons.Count; i++)
			{
				ItemButton itemButton = _itemButtons[i];
				if (i < page.Count)
				{
					ItemRenderData crate = page[i];
					itemButton.SetCrate(crate, _selectedItemIndex == i);
				}
				else
				{
					itemButton.Hide();
				}
			}
			int pageCount = selectedPanel.PageCount;
			_panelView._numberOfPages = pageCount;
			((TMP_Text)_panelView.labelText).text = _searchQuery;
			((TMP_Text)_panelView.itemPageText).text = $"{selectedPanel.Page + 1}/{pageCount}";
			_itemPageNextButton.SetActive(selectedPanel.Page < selectedPanel.PageCount - 1);
			_itemPagePreviousButton.SetActive(selectedPanel.Page > 0);
		}

		public void RenderAll()
		{
			if (IsSearchActive())
			{
				RenderTags();
				RenderItemButtons();
				RenderFocus();
				RenderSpecialButtons();
			}
		}

		public void ChangePanelPage(int offset)
		{
			GetSelectedPanel().ChangePage(this, offset);
		}

		private void AssignSpawnableCrate(SpawnableCrate spawnableCrate)
		{
			SpawnGunPatches.SelectCrate(spawnableCrate);
		}

		private void AssignAvatarCrate(Scannable avatarCrate)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			AvatarCrateReference val = new AvatarCrateReference(avatarCrate._barcode);
			PullCordDevice cordDevice = BodylogAccessor.GetCordDevice();
			if ((Object)(object)cordDevice != (Object)null)
			{
				UniTaskVoid val2 = cordDevice.SwapAvatar(val);
				((UniTaskVoid)(ref val2)).Forget();
			}
		}

		private void LoadLevelCrate(Scannable levelCrate)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			UniTaskExtensions.Forget(SceneStreamer.LoadAsync(new LevelCrateReference(levelCrate._barcode), (LevelCrateReference)null));
		}

		private void AssignCrate()
		{
			ISearchableCrate selectedSpawnable = GetSelectedSpawnable();
			if (selectedSpawnable == null || !selectedSpawnable.TryGetCrate(out Crate outCrate))
			{
				return;
			}
			AvatarCrate val = ((Il2CppObjectBase)outCrate).TryCast<AvatarCrate>();
			if ((Object)(object)val != (Object)null)
			{
				AssignAvatarCrate((Scannable)(object)val);
				return;
			}
			SpawnableCrate val2 = ((Il2CppObjectBase)outCrate).TryCast<SpawnableCrate>();
			if ((Object)(object)val2 != (Object)null)
			{
				AssignSpawnableCrate(val2);
			}
			LevelCrate val3 = ((Il2CppObjectBase)outCrate).TryCast<LevelCrate>();
			if ((Object)(object)val3 != (Object)null)
			{
				LoadLevelCrate((Scannable)(object)val3);
			}
		}

		public void OnSelectItem(int idx)
		{
			_currentFocus = SpawnInfoFocus.SelectedItem;
			_selectedItemIndex = idx;
			SetIsEditing(isEditing: false);
			RenderItemButtons();
			RenderTags();
			RenderFocus();
			if (GetSelectedPanel().CanAssign)
			{
				AssignCrate();
			}
		}

		public void SelectCategory(int idx)
		{
			ISearchPanel? renderPanel = GetRenderPanel(idx);
			if (renderPanel != null && renderPanel.OnSelected(this))
			{
				_currentFocus = SpawnInfoFocus.SelectedPage;
				SetIsEditing(isEditing: false);
				_selectedPageIndex = _renderedTagPageIndex;
				_selectedTagIndex = idx;
				_searchQuery = "";
				_keyboard.SetText(_searchQuery, triggerEvent: false);
				RequestRefresh();
			}
		}

		public void ChangeTagPage(int offset)
		{
			int num = _renderedTagPageIndex + offset;
			if (num >= 0 && num < _pages.PageCount)
			{
				_renderedTagPageIndex = num;
				RenderTags();
			}
		}

		public void SwapSortButton()
		{
			ISearchPanel selectedPanel = GetSelectedPanel();
			selectedPanel.SelectedOrderIndex = (selectedPanel.SelectedOrderIndex + 1) % selectedPanel.SupportedOrders.Length;
			RequestRefresh();
		}

		public void OnFavoriteButton()
		{
			ISearchPanel selectedPanel = GetSelectedPanel();
			if (_currentFocus == SpawnInfoFocus.SelectedItem)
			{
				if (selectedPanel.HasItemFunction)
				{
					ISearchableCrate selectedSpawnable = GetSelectedSpawnable();
					if (selectedSpawnable != null)
					{
						selectedPanel.OnItemFunction(this, selectedSpawnable);
					}
				}
			}
			else if (selectedPanel.HasPanelFunction)
			{
				selectedPanel.OnPanelFunction(this);
			}
		}

		public void Show()
		{
			ShowKeyboard();
			RenderTags();
			RenderSpecialButtons();
			RequestRefresh();
		}

		public void Hide()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			CloseKeyboard();
			_fadedButtonImage.sprite = _originalFavoriteSprite;
			_favoriteButtonImage.sprite = _originalFavoriteSprite;
			((Graphic)_favoriteButtonImage).color = _originalFavoriteColor;
			foreach (ItemButton itemButton in _itemButtons)
			{
				itemButton.Reset();
			}
			foreach (TagButton tagButton in _tagButtons)
			{
				tagButton.Reset();
			}
		}

		public void ShowKeyboard()
		{
			_keyboard.Show();
		}

		public void CloseKeyboard()
		{
			_keyboard.Hide();
		}

		public bool IsSearchActive()
		{
			return _panelView._selectedTabIndex == 5;
		}

		public bool Is(SpawnablesPanelView panelView)
		{
			if ((Object)(object)_panelView == (Object)null || (Object)(object)panelView == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)_panelView).gameObject == (Object)null)
			{
				return false;
			}
			return ((Object)((Component)_panelView).gameObject).GetInstanceID() == ((Object)((Component)panelView).gameObject).GetInstanceID();
		}
	}
	public enum SpawnInfoFocus
	{
		SelectedPage,
		SelectedItem
	}
}
namespace SearchThing.Extensions.Sort
{
	public class AlphabeticalSearchOrder : ISelectableSearchOrder, ISearchOrder
	{
		public string Name => "ABC";

		public int Order(ISearchableCrate searchableCrate)
		{
			return -string.Compare(searchableCrate.Name.Original, string.Empty, StringComparison.Ordinal);
		}
	}
	public class DateNewAddedSearchOrder : ISelectableSearchOrder, ISearchOrder
	{
		public string Name => "DateNew";

		public int Order(ISearchableCrate searchableCrate)
		{
			return (int)(searchableCrate.DateAdded.Ticks >> 32);
		}
	}
	public class DateOldAddedSearchOrder : ISelectableSearchOrder, ISearchOrder
	{
		public string Name => "DateOld";

		public int Order(ISearchableCrate searchableCrate)
		{
			return -(int)(searchableCrate.DateAdded.Ticks >> 32);
		}
	}
	public interface ISelectableSearchOrder : ISearchOrder
	{
		string Name { get; }
	}
	public class RandomSearchOrder : ISelectableSearchOrder, ISearchOrder
	{
		public string Name => "Random";

		public int Order(ISearchableCrate searchableCrate)
		{
			return Random.Shared.Next();
		}
	}
	public class ScoreSearchOrder : ISelectableSearchOrder, ISearchOrder
	{
		public string Name => "Relevance";

		public int Order(ISearchableCrate searchableCrate)
		{
			return searchableCrate.Score;
		}
	}
}
namespace SearchThing.Extensions.Panel
{
	public interface ISearchPanel
	{
		const int PanelSize = 12;

		Guid Id { get; }

		string Tag { get; }

		bool IsVisible { get; }

		bool CanAssign { get; }

		bool TagEditable { get; }

		string Query { get; set; }

		int SelectedOrderIndex { get; set; }

		ISelectableSearchOrder[] SupportedOrders { get; }

		int Page { get; set; }

		int PageCount { get; }

		bool HasPanelFunction { get; }

		Sprite? PanelFunctionIcon { get; }

		bool HasItemFunction { get; }

		Sprite? ItemFunctionIcon { get; }

		Color? GetPanelFunctionHighlight(SpawnablePanelExtension extension);

		Color? GetItemFunctionHighlight(SpawnablePanelExtension extension, ISearchableCrate? crate);

		void OnPanelFunction(SpawnablePanelExtension extension);

		void OnItemFunction(SpawnablePanelExtension extension, ISearchableCrate crate);

		void OnTagEdited(SpawnablePanelExtension extension, string newTag);

		Color? IsForceHighlighted(SpawnablePanelExtension extension, ISearchableCrate? selectedCrate);

		void RequestSearch(SpawnablePanelExtension extension);

		void ChangePage(SpawnablePanelExtension extension, int offset);

		bool OnSelected(SpawnablePanelExtension extension);

		ISearchableCrate? GetCrateAt(int index);

		IReadOnlyList<ItemRenderData> GetPage(int page);
	}
}
namespace SearchThing.Extensions.Panel.History
{
	public class AvatarHistorySearchPanel : HistorySearchPanel
	{
		public override string Tag => "Avatar History";

		protected override bool Filter(ISearchableCrate entry)
		{
			return entry.CrateType == CrateType.Avatar;
		}
	}
	public abstract class HistorySearchPanel : BasicSearchPanel<HistoryCrateEntry>
	{
		public override bool ResearchOnPageChange => true;

		public abstract override string Tag { get; }

		public override ISelectableSearchOrder[] SupportedOrders { get; } = new ISelectableSearchOrder[5]
		{
			new DateNewAddedSearchOrder(),
			new DateOldAddedSearchOrder(),
			new ScoreSearchOrder(),
			new AlphabeticalSearchOrder(),
			new RandomSearchOrder()
		};


		protected abstract bool Filter(ISearchableCrate entry);

		protected override void Search(string query, ISearchOrder searchOrder, Action<SearchResults<HistoryCrateEntry>> callback)
		{
			HistoryManager.SearchAsync(query, searchOrder, Filter, callback);
		}
	}
	public class PropHistorySearchPanel : HistorySearchPanel
	{
		public override string Tag => "Prop History";

		protected override bool Filter(ISearchableCrate entry)
		{
			return entry.CrateType == CrateType.Prop;
		}
	}
}
namespace SearchThing.Extensions.Panel.Filter
{
	public class AvatarTagSearchPanel : FilterSearchSearchPanel
	{
		public override string Tag => "Avatar";

		protected override bool Filter(ISearchableCrate searchableCrate)
		{
			if (searchableCrate != null && searchableCrate.CrateType == CrateType.Avatar)
			{
				return !searchableCrate.Redacted;
			}
			return false;
		}
	}
	public abstract class FilterSearchSearchPanel : BasicSearchPanel<ISearchableCrate>
	{
		private static readonly Sprite PresetAddIcon = ImageHelper.LoadEmbeddedSprite("SearchThing.resources.AddIcon.png");

		public abstract override string Tag { get; }

		public override bool HasItemFunction => true;

		public override Sprite ItemFunctionIcon => PresetAddIcon;

		protected abstract bool Filter(ISearchableCrate searchableCrate);

		public override Color? GetItemFunctionHighlight(SpawnablePanelExtension extension, ISearchableCrate? crate)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (!PresetManager.IsAssignmentMode)
			{
				return null;
			}
			return Color.green;
		}

		public override void OnItemFunction(SpawnablePanelExtension extension, ISearchableCrate crate)
		{
			PresetManager.IsAssignmentMode = !PresetManager.IsAssignmentMode;
			extension.RenderAll();
		}

		protected override void Search(string query, ISearchOrder order, Action<SearchResults<ISearchableCrate>> callback)
		{
			SearchManager.SearchAsync(query, GlobalCrateManager.GetCrates(), Filter, order, callback);
		}
	}
	public class LevelTagSearchPanel : FilterSearchSearchPanel
	{
		public override string Tag => "Levels";

		protected override bool Filter(ISearchableCrate searchableCrate)
		{
			if (searchableCrate != null && searchableCrate.CrateType == CrateType.Level)
			{
				return !searchableCrate.Redacted;
			}
			return false;
		}
	}
	public class PropTagSearchPanel : FilterSearchSearchPanel
	{
		public override string Tag => "Props";

		protected override bool Filter(ISearchableCrate searchableCrate)
		{
			if (searchableCrate != null && searchableCrate.CrateType == CrateType.Prop)
			{
				return !searchableCrate.Redacted;
			}
			return false;
		}
	}
	public class RedactedSearchPanel : FilterSearchSearchPanel
	{
		public override string Tag => "Redacted";

		protected override bool Filter(ISearchableCrate searchableCrate)
		{
			return searchableCrate?.Redacted ?? false;
		}
	}
}
namespace SearchThing.Extensions.Panel.Data
{
	public class ItemRenderData
	{
		public string Name { get; set; }

		public string Author { get; set; }

		public string Description { get; set; }

		public string PalletName { get; set; }

		public Sprite? Icon { get; set; }

		public string[] Tags { get; set; }
	}
}
namespace SearchThing.Extensions.Panel.Abstract
{
	public abstract class BasicSearchPanel<TCrate> : ISearchPanel where TCrate : class, ISearchableCrate
	{
		private bool _isDirty = true;

		private SearchResults<TCrate>? _results;

		private string _query = "";

		private int _selectedOrderIndex;

		public virtual bool ResearchOnPageChange => false;

		public abstract string Tag { get; }

		public virtual bool IsVisible => true;

		public virtual bool CanAssign => true;

		public virtual bool TagEditable => false;

		public Guid Id { get; } = Guid.NewGuid();


		public string Query
		{
			get
			{
				return _query;
			}
			set
			{
				if (!(value == _query))
				{
					_isDirty = true;
					_query = value;
				}
			}
		}

		public int SelectedOrderIndex
		{
			get
			{
				return _selectedOrderIndex;
			}
			set
			{
				if (value != _selectedOrderIndex && value >= 0 && value < SupportedOrders.Length)
				{
					_isDirty = true;
					_selectedOrderIndex = value;
				}
			}
		}

		public virtual ISelectableSearchOrder[] SupportedOrders { get; } = new ISelectableSearchOrder[5]
		{
			new ScoreSearchOrder(),
			new AlphabeticalSearchOrder(),
			new DateNewAddedSearchOrder(),
			new DateOldAddedSearchOrder(),
			new RandomSearchOrder()
		};


		public int Page { get; set; }

		public int PageCount { get; private set; } = 1;


		public virtual bool HasPanelFunction => false;

		public virtual Sprite? PanelFunctionIcon => null;

		public virtual bool HasItemFunction => false;

		public virtual Sprite? ItemFunctionIcon => null;

		protected abstract void Search(string query, ISearchOrder order, Action<SearchResults<TCrate>> callback);

		public void RequestSearch(SpawnablePanelExtension extension)
		{
			SpawnablePanelExtension extension2 = extension;
			if (!ResearchOnPageChange && !_isDirty)
			{
				extension2.RenderAll();
				return;
			}
			_isDirty = false;
			ISelectableSearchOrder order = SupportedOrders[SelectedOrderIndex];
			Search(_query, order, delegate(SearchResults<TCrate> results)
			{
				_results = results;
				Page = 0;
				PageCount = results.GetPageCount(12);
				extension2.RenderAll();
			});
		}

		public void ChangePage(SpawnablePanelExtension extension, int offset)
		{
			int num = Page + offset;
			if (num >= 0 && num < PageCount)
			{
				Page = num;
				extension.RenderAll();
			}
		}

		public void MakeDirty()
		{
			_isDirty = true;
		}

		public virtual Color? GetPanelFunctionHighlight(SpawnablePanelExtension extension)
		{
			return null;
		}

		public virtual Color? GetItemFunctionHighlight(SpawnablePanelExtension extension, ISearchableCrate? crate)
		{
			return null;
		}

		public virtual void OnPanelFunction(SpawnablePanelExtension extension)
		{
		}

		public virtual void OnItemFunction(SpawnablePanelExtension extension, ISearchableCrate crate)
		{
		}

		public virtual void OnTagEdited(SpawnablePanelExtension extension, string newTag)
		{
		}

		public virtual Color? IsForceHighlighted(SpawnablePanelExtension extension, ISearchableCrate? selectedCrate)
		{
			return null;
		}

		public virtual bool OnSelected(SpawnablePanelExtension extension)
		{
			return true;
		}

		public virtual ItemRenderData GetRenderDataForCrate(TCrate crate)
		{
			return new ItemRenderData
			{
				Name = crate.Name.Original,
				PalletName = crate.PalletName.Original,
				Author = crate.Author.Original,
				Tags = crate.Tags.Select((SearchTag t) => t.Original).ToArray(),
				Description = crate.Description,
				Icon = CrateIconProvider.GetIcon(crate)
			};
		}

		public ISearchableCrate? GetCrateAt(int index)
		{
			SearchResults<TCrate>? results = _results;
			return (results != null) ? results.GetEntryAt(Page, 12, index) : null;
		}

		public IReadOnlyList<ItemRenderData> GetPage(int page)
		{
			if (_results == null)
			{
				return Array.Empty<ItemRenderData>();
			}
			return _results.GetPage(page, 12).Select(GetRenderDataForCrate).ToList();
		}
	}
}
namespace SearchThing.Extensions.Pages
{
	public class BasicSearchPage : ISearchPage
	{
		private readonly Func<bool> _isVisibleFunc = () => true;

		public bool IsVisible => _isVisibleFunc();

		public IReadOnlyList<ISearchPanel> Panels { get; }

		public BasicSearchPage(Func<bool> isVisibleFunc, params ISearchPanel[] panels)
		{
			_isVisibleFunc = isVisibleFunc;
			Panels = panels;
		}

		public BasicSearchPage(params ISearchPanel[] panels)
		{
			Panels = panels;
		}
	}
	public interface ISearchPage
	{
		bool IsVisible { get; }

		IReadOnlyList<ISearchPanel> Panels { get; }
	}
}
namespace SearchThing.Extensions.Components
{
	public class ItemButton
	{
		private static Sprite? _defaultIcon;

		private static Color? _defaultIconColor;

		private static Color? _defaultHighlightColor;

		private GameObject _button;

		private TextMeshPro _text;

		private Image _highlight;

		private Image _icon;

		public ItemButton(ButtonReferenceHolder button)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_defaultIcon == (Object)null)
			{
				_defaultIcon = button.special.sprite;
			}
			Color valueOrDefault = _defaultIconColor.GetValueOrDefault();
			if (!_defaultIconColor.HasValue)
			{
				valueOrDefault = ((Graphic)button.special).color;
				_defaultIconColor = valueOrDefault;
			}
			valueOrDefault = _defaultHighlightColor.GetValueOrDefault();
			if (!_defaultHighlightColor.HasValue)
			{
				valueOrDefault = ((Graphic)button.highlight).color;
				_defaultHighlightColor = valueOrDefault;
			}
			_button = ((Component)button).gameObject;
			_text = button.tmp;
			_highlight = button.highlight;
			_icon = button.special;
		}

		public void SetCrate(ItemRenderData crate, bool isSelected = false)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			_button.SetActive(true);
			((TMP_Text)_text).text = crate.Name;
			((Behaviour)_icon).enabled = (Object)(o