Decompiled source of Bookmark Organizer v1.1.0

plugins/PotionCraftBookmarkOrganizer.dll

Decompiled 11 months ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using PotionCraft.Assemblies.GamepadNavigation;
using PotionCraft.Core.Extensions;
using PotionCraft.Core.ValueContainers;
using PotionCraft.InputSystem;
using PotionCraft.ManagersSystem;
using PotionCraft.ManagersSystem.Cursor;
using PotionCraft.ManagersSystem.SaveLoad;
using PotionCraft.ObjectBased.InteractiveItem;
using PotionCraft.ObjectBased.UIElements;
using PotionCraft.ObjectBased.UIElements.Bookmarks;
using PotionCraft.ObjectBased.UIElements.Books;
using PotionCraft.ObjectBased.UIElements.Books.RecipeBook;
using PotionCraft.ObjectBased.UIElements.PotionCustomizationPanel;
using PotionCraft.ObjectBased.UIElements.Tooltip;
using PotionCraft.SaveFileSystem;
using PotionCraft.SaveLoadSystem;
using PotionCraft.ScriptableObjects;
using PotionCraft.ScriptableObjects.Potion;
using PotionCraft.Settings;
using PotionCraftBookmarkOrganizer.Scripts.ClassOverrides;
using PotionCraftBookmarkOrganizer.Scripts.Services;
using PotionCraftBookmarkOrganizer.Scripts.Storage;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PotionCraftBookmarkOrganizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Recipe Waypoint Mod for Potion Craft")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PotionCraftBookmarkOrganizer")]
[assembly: AssemblyTitle("PotionCraftBookmarkOrganizer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PotionCraftBookmarkOrganizer
{
	[BepInPlugin("com.fahlgorithm.potioncraftbookmarkorganizer", "PotionCraftBookmarkOrganizer", "1.1.0.0")]
	[BepInProcess("Potion Craft.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PLUGIN_GUID = "com.fahlgorithm.potioncraftbookmarkorganizer";

		public const string PLUGIN_VERSION = "1.1.0.0";

		public static ManualLogSource PluginLogger { get; private set; }

		private void Awake()
		{
			PluginLogger = ((BaseUnityPlugin)this).Logger;
			PluginLogger.LogInfo((object)"Plugin com.fahlgorithm.potioncraftbookmarkorganizer is loaded!");
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.fahlgorithm.potioncraftbookmarkorganizer");
			PluginLogger.LogInfo((object)"Plugin com.fahlgorithm.potioncraftbookmarkorganizer: Patch Succeeded!");
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "PotionCraftBookmarkOrganizer";

		public const string PLUGIN_NAME = "PotionCraftBookmarkOrganizer";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace PotionCraftBookmarkOrganizer.Scripts
{
	public static class Ex
	{
		public static bool RunSafe(Func<bool> action, Func<bool> errorAction = null, bool logErrorToStorage = false)
		{
			try
			{
				return action();
			}
			catch (Exception ex)
			{
				LogException(ex, logErrorToStorage);
			}
			return errorAction?.Invoke() ?? true;
		}

		public static void RunSafe(Action action, Action errorAction = null, bool logErrorToStorage = false)
		{
			try
			{
				action();
			}
			catch (Exception ex)
			{
				LogException(ex, logErrorToStorage);
				errorAction?.Invoke();
			}
		}

		public static void LogException(Exception ex, bool logErrorToStorage = false)
		{
			string exceptionText = GetExceptionText(ex);
			Plugin.PluginLogger.LogError((object)exceptionText);
			if (logErrorToStorage)
			{
				StaticStorage.ErrorLog.Add(exceptionText);
			}
		}

		public static string GetExceptionText(Exception ex)
		{
			return $"{DateTime.UtcNow}: {ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}\r\n{ex.InnerException?.Message}";
		}

		public static void SaveErrorMessage(string errorMessage)
		{
			StaticStorage.ErrorLog.Add($"{DateTime.UtcNow}: {errorMessage}");
		}
	}
}
namespace PotionCraftBookmarkOrganizer.Scripts.Storage
{
	public class BookmarkOrganizerManager : MonoBehaviour
	{
		public Dictionary<int, List<BookmarkStorage>> BookmarkGroups => StaticStorage.BookmarkGroups;

		public List<int> SavedRecipePositions => StaticStorage.SavedRecipePositions;

		public List<Transform> SubRailLayers => StaticStorage.SubRailLayers;

		public Transform InvisiRailLayer => StaticStorage.InvisiRailLayer;

		public BookmarkRail SubRail => StaticStorage.SubRail;

		public GameObject SubRailActiveBookmarkLayer => StaticStorage.SubRailActiveBookmarkLayer;

		public BookmarkRail InvisiRail => StaticStorage.InvisiRail;

		public bool AddedListeners => StaticStorage.AddedListeners;

		private void Update()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Invalid comparison between Unknown and I4
			if (StaticStorage.HotkeyDown == null || StaticStorage.HotkeyUp == null)
			{
				return;
			}
			if ((int)Commands.roomDown.State == 2)
			{
				RecipeBookService.FlipPageToNextGroup(flipForward: false);
				StaticStorage.HotkeyDown.SetAction((Action<CommandInvokeRepeater>)delegate
				{
					RecipeBookService.FlipPageToNextGroup(flipForward: false);
				}).StopWhen((Func<bool>)(() => !CanHotkeysBeUsed()), (Action<CommandInvokeRepeater>)null);
			}
			else if ((int)Commands.roomUp.State == 2)
			{
				RecipeBookService.FlipPageToNextGroup(flipForward: true);
				StaticStorage.HotkeyUp.SetAction((Action<CommandInvokeRepeater>)delegate
				{
					RecipeBookService.FlipPageToNextGroup(flipForward: true);
				}).StopWhen((Func<bool>)(() => !CanHotkeysBeUsed()), (Action<CommandInvokeRepeater>)null);
			}
		}

		private bool CanHotkeysBeUsed()
		{
			return !Managers.Input.HasInputGotToBeDisabled();
		}
	}
	public class BookmarkStorage
	{
		public int recipeIndex;

		public SerializedBookmark SerializedBookmark;
	}
	public static class StaticStorage
	{
		public class SavedStaticStorage
		{
			public Dictionary<int, List<BookmarkStorage>> BookmarkGroups { get; set; }

			public List<int> SavedRecipePositions { get; set; }

			public List<string> ErrorLog { get; set; }

			public string BookmarkManagerVersion { get; set; }
		}

		public const string BookmarkGroupsJsonSaveName = "FahlgorithmBookmarkOrganizer";

		public const string SubRailName = "BottomToTopSubRail";

		public const string InvisiRailName = "BottomToTopInvisiRail";

		public const string CornerIconGameObjectName = "GroupCornerIcon";

		public static Dictionary<int, List<BookmarkStorage>> BookmarkGroups = new Dictionary<int, List<BookmarkStorage>>();

		public static List<int> SavedRecipePositions;

		public static List<string> ErrorLog = new List<string>();

		public static BookmarkRail SubRail;

		public static GameObject SubRailPages;

		public static BookmarkRail InvisiRail;

		public static Bookmark StaticBookmark;

		public static Dictionary<RecipeBookLeftPageContent, (GameObject, GameObject)> StaticRails = new Dictionary<RecipeBookLeftPageContent, (GameObject, GameObject)>();

		public static List<Transform> SubRailLayers = new List<Transform>();

		public static Transform InvisiRailLayer;

		public static GameObject SubRailActiveBookmarkLayer;

		public static bool AddedListeners;

		public static bool IsLoaded;

		public static CommandInvokeRepeater HotkeyUp;

		public static CommandInvokeRepeater HotkeyDown;

		public static string StateJsonString;
	}
}
namespace PotionCraftBookmarkOrganizer.Scripts.Services
{
	public static class RecipeBookService
	{
		private static bool ignoreBookmarkRearrangeForSubBookmarkPromotion;

		private static bool currentlyRearranging;

		private static ConcurrentQueue<List<int>> rearrangeQueue = new ConcurrentQueue<List<int>>();

		private static int recipeIndexForCurrentGroupUpdate = -1;

		public static void SetupListeners()
		{
			if (!StaticStorage.AddedListeners)
			{
				StaticStorage.AddedListeners = true;
				((UnityEvent<BookmarkController, List<int>>)(object)((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.onBookmarksRearranged).AddListener((UnityAction<BookmarkController, List<int>>)BookmarksRearranged);
				((Component)Managers.Potion).gameObject.AddComponent<BookmarkOrganizerManager>();
				CursorManagerSettings asset = Settings<CursorManagerSettings>.Asset;
				StaticStorage.HotkeyUp = CommandInvokeRepeater.GetNewCommandInvokeRepeater(asset.invokeRepeaterSettings, new List<Command> { Commands.roomUp }, false);
				StaticStorage.HotkeyDown = CommandInvokeRepeater.GetNewCommandInvokeRepeater(asset.invokeRepeaterSettings, new List<Command> { Commands.roomDown }, false);
			}
		}

		public static void FlipPageToNextGroup(bool flipForward)
		{
			if (((Behaviour)Managers.Potion.recipeBook).isActiveAndEnabled)
			{
				int nextNonSubRecipeIndex = GetNextNonSubRecipeIndex(flipForward);
				if (nextNonSubRecipeIndex != ((Book)Managers.Potion.recipeBook).currentPageIndex)
				{
					FlipPageToIndex(nextNonSubRecipeIndex);
				}
			}
		}

		private static int GetNextNonSubRecipeIndex(bool moveForward)
		{
			RecipeBook recipeBook = Managers.Potion.recipeBook;
			int currentPageIndex = ((Book)recipeBook).currentPageIndex;
			int pagesCount = ((Book)recipeBook).GetPagesCount();
			int result = currentPageIndex;
			for (int i = 1; i <= pagesCount; i++)
			{
				int num = i * (moveForward ? 1 : (-1));
				int num2 = (currentPageIndex + num + pagesCount) % pagesCount;
				GetBookmarkStorageRecipeIndex(num2, out var indexIsParent);
				if (!indexIsParent)
				{
					result = num2;
					break;
				}
			}
			return result;
		}

		public static void PromoteIndexToParent(int subBookmarkIndex)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			bool indexIsParent;
			int groupIndex = GetBookmarkStorageRecipeIndex(subBookmarkIndex, out indexIsParent);
			if (!indexIsParent)
			{
				return;
			}
			List<Bookmark> allBookmarksList = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetAllBookmarksList();
			Bookmark subBookmark = allBookmarksList[subBookmarkIndex];
			Bookmark groupBookmark = allBookmarksList[groupIndex];
			Vector2 groupBookmarkPosition = groupBookmark.GetNormalizedPosition();
			Vector2 subBookmarkPosition = subBookmark.GetNormalizedPosition();
			BookmarkController bookmarkController = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers[0].bookmarkController;
			int i = 0;
			(from rail in bookmarkController.rails.ToList()
				select (rail, rail.railBookmarks.ToList())).ToList().ForEach(delegate((BookmarkRail rail, List<Bookmark> bookmarks) rail)
			{
				rail.bookmarks.ForEach(delegate
				{
					//IL_0034: 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 (i == groupIndex)
					{
						rail.rail.Connect(subBookmark, groupBookmarkPosition);
					}
					else if (i == subBookmarkIndex)
					{
						rail.rail.Connect(groupBookmark, subBookmarkPosition);
					}
					int num = i;
					i = num + 1;
				});
			});
			ignoreBookmarkRearrangeForSubBookmarkPromotion = true;
			bookmarkController.CallOnBookmarksRearrangeIfNecessary(allBookmarksList);
			ignoreBookmarkRearrangeForSubBookmarkPromotion = false;
			Managers.Potion.recipeBook.UpdateBookmarkIcon(groupIndex);
			Managers.Potion.recipeBook.UpdateBookmarkIcon(subBookmarkIndex);
			SubRailService.UpdateStaticBookmark();
			UpdateBookmarkGroupsForCurrentRecipe();
			ShowHideGroupBookmarkIcon(groupBookmark, show: false);
			SubRailService.UpdateSubBookmarksActiveState();
		}

		private static async void BookmarksRearranged(BookmarkController bookmarkController, List<int> intList)
		{
			if (ignoreBookmarkRearrangeForSubBookmarkPromotion)
			{
				return;
			}
			rearrangeQueue.Enqueue(intList);
			if (currentlyRearranging)
			{
				while (currentlyRearranging)
				{
					await Task.Delay(100);
				}
			}
			BookmarksRearranged();
		}

		private static async void BookmarksRearranged()
		{
			currentlyRearranging = true;
			try
			{
				while (rearrangeQueue.Count > 0)
				{
					List<int> intList;
					bool flag = rearrangeQueue.TryDequeue(out intList);
					while (!flag)
					{
						await Task.Delay(10);
						flag = rearrangeQueue.TryDequeue(out var result);
						if (flag)
						{
							intList = result;
						}
					}
					Dictionary<int, List<BookmarkStorage>> bookmarkGroups = StaticStorage.BookmarkGroups;
					Dictionary<int, List<BookmarkStorage>> dictionary = new Dictionary<int, List<BookmarkStorage>>();
					List<BookmarkStorage> oldStoredBookmarksList = bookmarkGroups.SelectMany((KeyValuePair<int, List<BookmarkStorage>> b) => b.Value).ToList();
					List<IGrouping<int, BookmarkStorage>> list = (from b in oldStoredBookmarksList
						group b by b.recipeIndex into bg
						where bg.Count() > 1
						select bg).ToList();
					if (list.Any())
					{
						Plugin.PluginLogger.LogError((object)"ERROR: somehow the same bookmark is in two different groups!");
						list.ForEach(delegate(IGrouping<int, BookmarkStorage> dbg)
						{
							oldStoredBookmarksList.Remove(dbg.Last());
						});
					}
					Dictionary<int, BookmarkStorage> dictionary2 = oldStoredBookmarksList.ToDictionary((BookmarkStorage b) => b.recipeIndex);
					for (int i = 0; i < intList.Count; i++)
					{
						int num = intList[i];
						if (recipeIndexForCurrentGroupUpdate == num)
						{
							recipeIndexForCurrentGroupUpdate = i;
						}
						if (bookmarkGroups.ContainsKey(num))
						{
							dictionary[i] = bookmarkGroups[num];
						}
						if (num != i && dictionary2.TryGetValue(num, out var value))
						{
							value.recipeIndex = i;
						}
					}
					StaticStorage.BookmarkGroups = dictionary;
					intList = null;
				}
				DoOrphanedBookmarkFailsafe();
			}
			catch (Exception ex)
			{
				Ex.LogException(ex);
			}
			currentlyRearranging = false;
		}

		private static void DoOrphanedBookmarkFailsafe()
		{
			((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetAllBookmarksList().ForEach(delegate(Bookmark bookmark)
			{
				//IL_0093: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)bookmark.rail == (Object)null || !bookmark.rail.railBookmarks.Contains(bookmark))
				{
					Plugin.PluginLogger.LogError((object)"ERROR: An orphaned bookmark has been found! This is the result of another error!");
					BookmarkController bookmarkController = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController;
					Tuple<BookmarkRail, Vector2> tuple = SubRailService.GetSpawnPosition(bookmarkController, (SpaceType)3) ?? SubRailService.GetSpawnPosition(bookmarkController, (SpaceType)2) ?? SubRailService.GetSpawnPosition(bookmarkController, (SpaceType)1) ?? SubRailService.GetSpawnPosition(bookmarkController, (SpaceType)0);
					if (tuple == null)
					{
						Plugin.PluginLogger.LogError((object)"DoOrphanedBookmarkFailsafe - There is no empty space for bookmark! Change settings!");
					}
					else
					{
						SubRailService.ConnectBookmarkToRail(tuple.Item1, bookmark, tuple.Item2);
					}
				}
			});
		}

		public static async void UpdateBookmarkGroupsForCurrentRecipe()
		{
			recipeIndexForCurrentGroupUpdate = GetBookmarkStorageRecipeIndexForSelectedRecipe();
			var subRailBookmarks = StaticStorage.SubRail.railBookmarks.Select((Bookmark b) => new
			{
				bookmark = b,
				serialized = b.GetSerialized()
			}).ToList();
			if (currentlyRearranging || rearrangeQueue.Count > 0)
			{
				while (currentlyRearranging || rearrangeQueue.Count > 0)
				{
					await Task.Delay(100);
				}
			}
			currentlyRearranging = true;
			List<Bookmark> allBookmarks = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetAllBookmarksList();
			IEnumerable<BookmarkStorage> source = subRailBookmarks.Select(bookmark => new BookmarkStorage
			{
				recipeIndex = allBookmarks.IndexOf(bookmark.bookmark),
				SerializedBookmark = bookmark.serialized
			});
			StaticStorage.BookmarkGroups[recipeIndexForCurrentGroupUpdate] = source.ToList();
			currentlyRearranging = false;
			ShowHideGroupBookmarkIcon(((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetBookmarkByIndex(recipeIndexForCurrentGroupUpdate), source.Any());
		}

		public static int GetBookmarkStorageRecipeIndexForSelectedRecipe()
		{
			bool indexIsParent;
			return GetBookmarkStorageRecipeIndex(((Book)Managers.Potion.recipeBook).currentPageIndex, out indexIsParent);
		}

		public static int GetBookmarkStorageRecipeIndexForSelectedRecipe(out bool indexIsParent)
		{
			return GetBookmarkStorageRecipeIndex(((Book)Managers.Potion.recipeBook).currentPageIndex, out indexIsParent);
		}

		public static int GetBookmarkStorageRecipeIndex(int recipeIndex)
		{
			bool indexIsParent;
			return GetBookmarkStorageRecipeIndex(recipeIndex, out indexIsParent);
		}

		public static int GetBookmarkStorageRecipeIndex(int recipeIndex, out bool indexIsParent)
		{
			indexIsParent = false;
			List<int> source = StaticStorage.BookmarkGroups.Keys.Where((int k) => StaticStorage.BookmarkGroups[k].Any((BookmarkStorage b) => b.recipeIndex == recipeIndex)).ToList();
			if (source.Any())
			{
				recipeIndex = source.First();
				indexIsParent = true;
			}
			return recipeIndex;
		}

		public static bool IsBookmarkGroupParent(int index)
		{
			if (!StaticStorage.BookmarkGroups.TryGetValue(index, out var value))
			{
				return false;
			}
			return value.Count > 0;
		}

		public static void FlipPageToIndex(int nextIndex)
		{
			if (!(Managers.Cursor.grabbedInteractiveItem is BookmarkButtonInactive))
			{
				RecipeBook recipeBook = Managers.Potion.recipeBook;
				int pagesCount = ((Book)recipeBook).GetPagesCount();
				((Book)recipeBook).curlPageController.HotkeyClicked((nextIndex > ((Book)recipeBook).currentPageIndex) ? (IntExtension.Distance(((Book)recipeBook).currentPageIndex, nextIndex) <= IntExtension.Distance(nextIndex, ((Book)recipeBook).currentPageIndex + pagesCount)) : (IntExtension.Distance(((Book)recipeBook).currentPageIndex, nextIndex) >= IntExtension.Distance(((Book)recipeBook).currentPageIndex, nextIndex + pagesCount)), true, nextIndex);
			}
		}

		public static void ShowHideGroupBookmarkIcon(Bookmark bookmark, bool show)
		{
			Transform obj = ((Component)bookmark).transform.Find("GroupCornerIcon");
			GameObject val = ((obj != null) ? ((Component)obj).gameObject : null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.PluginLogger.LogError((object)"ERROR: Bookmark does not have a group corner icon setup!");
			}
			else
			{
				val.gameObject.SetActive(show);
			}
		}
	}
	public static class SaveLoadService
	{
		private class BookmarkGroupsDeserialized
		{
			[JsonProperty("FahlgorithmBookmarkOrganizer")]
			public StaticStorage.SavedStaticStorage SavedStaticStorage { get; set; }
		}

		public static void StoreBookmarkGroups(ref string result)
		{
			string modifiedResult = null;
			string savedStateJson = result;
			Ex.RunSafe(delegate
			{
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_004f: Expected O, but got Unknown
				if (StaticStorage.BookmarkGroups.Any())
				{
					string text = JsonConvert.SerializeObject((object)new StaticStorage.SavedStaticStorage
					{
						BookmarkGroups = StaticStorage.BookmarkGroups,
						SavedRecipePositions = StaticStorage.SavedRecipePositions,
						ErrorLog = StaticStorage.ErrorLog,
						BookmarkManagerVersion = "1.1.0.0"
					}, new JsonSerializerSettings
					{
						ReferenceLoopHandling = (ReferenceLoopHandling)1
					});
					string value = ",\"FahlgorithmBookmarkOrganizer\":" + text;
					int startIndex = savedStateJson.LastIndexOf('}');
					modifiedResult = savedStateJson.Insert(startIndex, value);
				}
			});
			if (!string.IsNullOrEmpty(modifiedResult))
			{
				result = modifiedResult;
			}
		}

		public static bool RetreiveStoredBookmarkGroups(Type type)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			if (type != typeof(ProgressState))
			{
				return true;
			}
			string stateJsonString = StaticStorage.StateJsonString;
			StaticStorage.StateJsonString = null;
			if (string.IsNullOrEmpty(stateJsonString))
			{
				Plugin.PluginLogger.LogInfo((object)"Error: stateJsonString is empty. Cannot load bookmark groups.");
				return true;
			}
			if (stateJsonString.IndexOf("FahlgorithmBookmarkOrganizer") == -1)
			{
				Plugin.PluginLogger.LogInfo((object)"No existing bookmark groups found during load");
				return true;
			}
			BookmarkGroupsDeserialized bookmarkGroupsDeserialized = JsonConvert.DeserializeObject<BookmarkGroupsDeserialized>(stateJsonString, new JsonSerializerSettings
			{
				ReferenceLoopHandling = (ReferenceLoopHandling)1
			});
			if (bookmarkGroupsDeserialized.SavedStaticStorage?.BookmarkGroups == null)
			{
				Plugin.PluginLogger.LogError((object)"Error: An error occured during bookmark group deserialization");
				return true;
			}
			StaticStorage.BookmarkGroups = bookmarkGroupsDeserialized.SavedStaticStorage.BookmarkGroups;
			StaticStorage.SavedRecipePositions = bookmarkGroupsDeserialized.SavedStaticStorage.SavedRecipePositions;
			return true;
		}

		public static bool RetrieveStateJsonString(File instance)
		{
			StaticStorage.StateJsonString = instance.StateJsonString;
			return true;
		}

		public static bool ClearFileSpecificDataOnFileLoad()
		{
			StaticStorage.BookmarkGroups.Clear();
			StaticStorage.SavedRecipePositions = null;
			return true;
		}

		public static Sprite GenerateSpriteFromImage(string path, Vector2? pivot = null, bool createComplexMesh = false)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(path);
			byte[] array;
			using (MemoryStream memoryStream = new MemoryStream())
			{
				manifestResourceStream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			Texture2D val = ((!createComplexMesh) ? new Texture2D(0, 0, (GraphicsFormat)8, (TextureCreationFlags)0)
			{
				filterMode = (FilterMode)1
			} : new Texture2D(0, 0, (TextureFormat)5, false, false)
			{
				filterMode = (FilterMode)1
			});
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).wrapModeU = (TextureWrapMode)1;
			((Texture)val).wrapModeV = (TextureWrapMode)1;
			((Texture)val).wrapModeW = (TextureWrapMode)1;
			if (!ImageConversion.LoadImage(val, array))
			{
				Plugin.PluginLogger.LogError((object)"ERROR: Failed to load Bookmark_organizer_recipe_slot.png.");
				return null;
			}
			Vector2 val2 = (Vector2)(pivot.HasValue ? pivot.Value : new Vector2(0.5f, 0.5f));
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), val2, 100f, 1u, (SpriteMeshType)(createComplexMesh ? 1 : 0));
		}
	}
	public static class SubRailService
	{
		public static bool IsSubRail(BookmarkRail rail)
		{
			if ((Object)(object)rail == (Object)null)
			{
				return false;
			}
			return ((Object)((Component)rail).gameObject).name == "BottomToTopSubRail";
		}

		public static bool IsActiveBookmark(Bookmark bookmark)
		{
			if (bookmark.IsActiveBookmark())
			{
				return true;
			}
			return false;
		}

		public static bool IsInvisiRail(BookmarkRail rail)
		{
			if ((Object)(object)rail == (Object)null)
			{
				return false;
			}
			return ((Object)((Component)rail).gameObject).name == "BottomToTopInvisiRail";
		}

		public static void UpdateSubRailForSelectedIndex(int pageIndex)
		{
			UpdateStaticBookmark(pageIndex);
			int bookmarkStorageRecipeIndex = RecipeBookService.GetBookmarkStorageRecipeIndex(pageIndex);
			List<Bookmark> allBookmarks = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetAllBookmarksList();
			var list = (from s in GetSubRailRecipesForIndex(bookmarkStorageRecipeIndex)
				select new
				{
					savedBookmark = s,
					bookmark = allBookmarks[s.recipeIndex],
					isActive = (pageIndex == s.recipeIndex)
				}).ToList();
			if (list.Any(sb => StaticStorage.SubRail.railBookmarks.Any((Bookmark rb) => (Object)(object)sb.bookmark == (Object)(object)rb)))
			{
				list.ForEach(savedBookmark =>
				{
					savedBookmark.bookmark.CurrentVisualState = (VisualState)(savedBookmark.isActive ? 1 : 0);
				});
				return;
			}
			StaticStorage.SubRail.railBookmarks.ToList().ForEach(delegate(Bookmark b)
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				Vector2? nextEmptySpaceOnRail = GetNextEmptySpaceOnRail(StaticStorage.InvisiRail);
				if (!nextEmptySpaceOnRail.HasValue)
				{
					throw new Exception("PotionCraftBookmarkOrganizer - Somehow the InvisiRail ran out of space. All hope is lost!");
				}
				ConnectBookmarkToRail(StaticStorage.InvisiRail, b, nextEmptySpaceOnRail.Value);
			});
			list.ForEach(savedBookmark =>
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				ConnectBookmarkToRail(StaticStorage.SubRail, savedBookmark.bookmark, savedBookmark.savedBookmark.SerializedBookmark.position);
				savedBookmark.bookmark.CurrentVisualState = (VisualState)(savedBookmark.isActive ? 1 : 0);
			});
		}

		public static List<BookmarkStorage> GetSubRailRecipesForIndex(int nextPageIndex)
		{
			int bookmarkStorageRecipeIndex = RecipeBookService.GetBookmarkStorageRecipeIndex(nextPageIndex);
			if (!StaticStorage.BookmarkGroups.TryGetValue(bookmarkStorageRecipeIndex, out var value))
			{
				return new List<BookmarkStorage>();
			}
			return value;
		}

		public static Vector2? GetNextEmptySpaceOnRail(BookmarkRail rail, bool getMax = false)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			List<MinMaxFloat> emptySegments = rail.GetEmptySegments((SpaceType)0, (Bookmark)null);
			object? value = typeof(BookmarkController).GetField("spawnHeight", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(rail.bookmarkController);
			MinMaxFloat val = (MinMaxFloat)((value is MinMaxFloat) ? value : null);
			if (emptySegments.Any())
			{
				return new Vector2((rail.inverseSpawnOrder || getMax) ? ((MinMax<float, float>)(object)emptySegments.Last()).max : ((MinMax<float, float>)(object)emptySegments.First()).min, ((MinMax<float, float>)(object)val).min);
			}
			return null;
		}

		public static void UpdateStaticBookmark(int nextPageIndex = -1)
		{
			RecipeBook recipeBook = Managers.Potion.recipeBook;
			if (nextPageIndex == -1)
			{
				nextPageIndex = ((Book)recipeBook).currentPageIndex;
			}
			bool indexIsParent;
			int bookmarkStorageRecipeIndex = RecipeBookService.GetBookmarkStorageRecipeIndex(nextPageIndex, out indexIsParent);
			if ((Object)(object)recipeBook.savedRecipes[bookmarkStorageRecipeIndex] == (Object)null)
			{
				((Component)StaticStorage.StaticBookmark).gameObject.SetActive(false);
				return;
			}
			((Component)StaticStorage.StaticBookmark).gameObject.SetActive(true);
			Bookmark bookmarkByIndex = ((Book)recipeBook).bookmarkControllersGroupController.GetBookmarkByIndex(bookmarkStorageRecipeIndex);
			StaticStorage.StaticBookmark.SetBookmarkContent(((SpriteChangingButton)bookmarkByIndex.activeBookmarkButton).normalSpriteIcon, ((SpriteChangingButton)bookmarkByIndex.inactiveBookmarkButton).normalSpriteIcon, (Sprite)null, "");
			StaticStorage.StaticBookmark.CurrentVisualState = (VisualState)((!indexIsParent) ? 1 : 0);
		}

		public static int GetPagesCountWithoutSpecialRails()
		{
			return ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController.rails.Except((IEnumerable<BookmarkRail>)(object)new BookmarkRail[2]
			{
				StaticStorage.SubRail,
				StaticStorage.InvisiRail
			}).Sum((BookmarkRail r) => r.railBookmarks.Count);
		}

		public static void ConnectBookmarkToRail(BookmarkRail rail, Bookmark bookmark, Vector2 position)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			List<Bookmark> allBookmarksList = StaticStorage.SubRail.bookmarkController.GetAllBookmarksList();
			rail.Connect(bookmark, position);
			rail.SortBookmarksInClockwiseOrder();
			rail.bookmarkController.CallOnBookmarksRearrangeIfNecessary(allBookmarksList);
		}

		public static void ShowSubRailAfterFlip()
		{
			((Component)StaticStorage.SubRail).gameObject.SetActive(true);
			StaticStorage.SubRailPages.gameObject.SetActive(true);
			StaticStorage.StaticRails.Values.ToList().ForEach(delegate((GameObject, GameObject) r)
			{
				r.Item1.SetActive(false);
				r.Item2.SetActive(false);
			});
		}

		public static void UpdateNonSubBookmarksActiveState()
		{
			int bookmarkStorageRecipeIndexForSelectedRecipe = RecipeBookService.GetBookmarkStorageRecipeIndexForSelectedRecipe();
			List<Bookmark> list = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController.rails.Except((IEnumerable<BookmarkRail>)(object)new BookmarkRail[2]
			{
				StaticStorage.SubRail,
				StaticStorage.InvisiRail
			}).SelectMany((BookmarkRail r) => r.railBookmarks).ToList();
			for (int i = 0; i < list.Count; i++)
			{
				list[i].CurrentVisualState = (VisualState)((i == bookmarkStorageRecipeIndexForSelectedRecipe) ? 1 : 0);
			}
		}

		public static void UpdateSubBookmarksActiveState()
		{
			List<Bookmark> railBookmarks = StaticStorage.SubRail.railBookmarks;
			Bookmark val = railBookmarks.FirstOrDefault();
			if (!((Object)(object)val == (Object)null))
			{
				int num = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetAllBookmarksList().IndexOf(val);
				for (int i = 0; i < railBookmarks.Count; i++)
				{
					int num2 = i + num;
					railBookmarks[i].CurrentVisualState = (VisualState)((num2 == ((Book)Managers.Potion.recipeBook).currentPageIndex) ? 1 : 0);
				}
			}
		}

		public static Tuple<BookmarkRail, Vector2> GetSpawnPosition(BookmarkController bookmarkController, SpaceType spaceType)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			foreach (BookmarkRail item in bookmarkController.rails.Except((IEnumerable<BookmarkRail>)(object)new BookmarkRail[2]
			{
				StaticStorage.SubRail,
				StaticStorage.InvisiRail
			}).ToList())
			{
				List<MinMaxFloat> emptySegments = item.GetEmptySegments(spaceType, (Bookmark)null);
				if (emptySegments.Any())
				{
					float num = (item.inverseSpawnOrder ? ((MinMax<float, float>)(object)emptySegments.Last()).max : ((MinMax<float, float>)(object)emptySegments.First()).min);
					object? value = typeof(BookmarkController).GetField("spawnHeight", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(bookmarkController);
					float random = ((MinMax<float, float>)((value is MinMaxFloat) ? value : null)).GetRandom();
					return new Tuple<BookmarkRail, Vector2>(item, new Vector2(num, random));
				}
			}
			return null;
		}
	}
}
namespace PotionCraftBookmarkOrganizer.Scripts.Patches
{
	public class AddGroupCornerIconToBookmarkPatch
	{
		[HarmonyPatch(typeof(Bookmark), "UpdateVisualState")]
		public class Bookmark_UpdateVisualState
		{
			private static void Postfix(Bookmark __instance)
			{
				Ex.RunSafe(delegate
				{
					UpdateCornerIconScaleToMatchActiveState(__instance);
				});
			}
		}

		[HarmonyPatch(typeof(BookmarkButtonInactive), "OnGrabPrimary")]
		public class InactiveBookmarkButton_OnGrabPrimary
		{
			private static void Postfix(BookmarkButtonInactive __instance)
			{
				Ex.RunSafe(delegate
				{
					UpdateCornerIconScaleToMatchActiveState(__instance, isGrabbed: true);
				});
			}
		}

		[HarmonyPatch(typeof(BookmarkButtonInactive), "OnReleasePrimary")]
		public class InactiveBookmarkButton_OnReleasePrimary
		{
			private static void Postfix(BookmarkButtonInactive __instance)
			{
				Ex.RunSafe(delegate
				{
					UpdateCornerIconScaleToMatchActiveState(__instance, isGrabbed: false);
				});
			}
		}

		[HarmonyPatch(typeof(RecipeBook), "UpdateBookmarkIcon")]
		public class RecipeBook_UpdateBookmarkIcon
		{
			private static void Postfix(RecipeBook __instance, int index)
			{
				Ex.RunSafe(delegate
				{
					AddGroupBookmarkIconToBookmarkOnFirstContentUpdate(__instance, index);
				});
			}
		}

		private static Vector2 CornerIconLocation = new Vector2(0.245f, 0.675f);

		private static Sprite LoadedCornerSprite;

		private static Sprite LoadedCornerMaskSprite;

		private static void AddGroupBookmarkIconToBookmarkOnFirstContentUpdate(RecipeBook instance, int index)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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_0106: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			Bookmark bookmarkByIndex = ((Book)instance).bookmarkControllersGroupController.GetBookmarkByIndex(index);
			if (!((Object)(object)((Component)bookmarkByIndex).gameObject.transform.Find("GroupCornerIcon") != (Object)null))
			{
				if ((Object)(object)LoadedCornerSprite == (Object)null)
				{
					LoadedCornerSprite = SaveLoadService.GenerateSpriteFromImage("PotionCraftBookmarkOrganizer.InGameImages.Group_bookmark_icon.png", null, createComplexMesh: true);
				}
				if ((Object)(object)LoadedCornerMaskSprite == (Object)null)
				{
					LoadedCornerMaskSprite = SaveLoadService.GenerateSpriteFromImage("PotionCraftBookmarkOrganizer.InGameImages.Group_bookmark_icon_mask.png", null, createComplexMesh: true);
				}
				GameObject val = new GameObject("GroupCornerIcon");
				val.transform.parent = ((Component)bookmarkByIndex).transform;
				SpriteRenderer obj = val.AddComponent<SpriteRenderer>();
				obj.sprite = LoadedCornerSprite;
				SpriteRenderer spriteRendererIcon = ((SpriteChangingButton)bookmarkByIndex.activeBookmarkButton).spriteRendererIcon;
				((Renderer)obj).sortingLayerID = ((Renderer)spriteRendererIcon).sortingLayerID;
				((Renderer)obj).sortingLayerName = ((Renderer)spriteRendererIcon).sortingLayerName;
				((Renderer)obj).sortingOrder = ((Renderer)spriteRendererIcon).sortingOrder;
				SpriteMask obj2 = val.AddComponent<SpriteMask>();
				obj2.sprite = LoadedCornerMaskSprite;
				((Renderer)obj2).sortingLayerID = ((Renderer)spriteRendererIcon).sortingLayerID;
				((Renderer)obj2).sortingLayerName = ((Renderer)spriteRendererIcon).sortingLayerName;
				((Renderer)obj2).sortingOrder = ((Renderer)spriteRendererIcon).sortingOrder;
				val.transform.localPosition = Vector2.op_Implicit(CornerIconLocation);
				val.transform.rotation = Quaternion.identity;
				val.transform.localEulerAngles = Vector3.zero;
				bool active = RecipeBookService.IsBookmarkGroupParent(index);
				val.SetActive(active);
			}
		}

		private static void UpdateCornerIconScaleToMatchActiveState(Bookmark instance)
		{
			UpdateCornerIconScaleToMatchActiveState(instance.inactiveBookmarkButton, isGrabbed: false, instance);
		}

		private static void UpdateCornerIconScaleToMatchActiveState(BookmarkButtonInactive instance, bool isGrabbed, Bookmark bookmark = null)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Invalid comparison between Unknown and I4
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Invalid comparison between Unknown and I4
			//IL_0078: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)bookmark == (Object)null)
			{
				bookmark = ((Component)instance).GetComponentInParent<Bookmark>();
			}
			if (!((Object)(object)bookmark == (Object)null) && !((Object)(object)bookmark == (Object)(object)StaticStorage.StaticBookmark))
			{
				Transform obj = ((Component)bookmark).transform.Find("GroupCornerIcon");
				GameObject val = ((obj != null) ? ((Component)obj).gameObject : null);
				if (!((Object)(object)val == (Object)null))
				{
					float num = 1.03f;
					float num2 = 0.97f;
					val.transform.localScale = (Vector3)(((int)bookmark.CurrentVisualState == 1) ? new Vector3(num, num, num) : (isGrabbed ? new Vector3(num2, num2, num2) : Vector3.one));
					val.transform.localPosition = Vector2.op_Implicit(CornerIconLocation + (Vector2)(((int)bookmark.CurrentVisualState == 1) ? new Vector2(0.01f, 0.01f) : (isGrabbed ? new Vector2(-0.006f, -0.006f) : Vector2.zero)));
				}
			}
		}
	}
	public class MoveSubRailBookmarkOnRecipeErasePatch
	{
		[HarmonyPatch(typeof(RecipeBook), "EraseRecipe")]
		public class RecipeBook_EraseRecipe
		{
			private static bool Prefix(RecipeBook __instance, ref Potion potion)
			{
				return MoveSubRailBookmarkOnRecipeErase(__instance, ref potion);
			}

			private static void Postfix()
			{
				Ex.RunSafe(delegate
				{
					SubRailService.UpdateStaticBookmark();
				});
			}
		}

		[HarmonyPatch(typeof(Book), "GoToTheFirstNotEmptyPage")]
		public class Book_GoToTheFirstNotEmptyPage
		{
			private static bool Prefix(Book __instance)
			{
				return Ex.RunSafe(() => ShowPageAfterRecipeDelete(__instance));
			}
		}

		private static int? ShowPageAfterRecipeDeleteIndex;

		private static bool MoveSubRailBookmarkOnRecipeErase(RecipeBook instance, ref Potion potion)
		{
			Potion localPotion = potion;
			Ex.RunSafe(delegate
			{
				int num = instance.savedRecipes.IndexOf(localPotion);
				bool indexIsParent;
				int bookmarkStorageRecipeIndex = RecipeBookService.GetBookmarkStorageRecipeIndex(num, out indexIsParent);
				bool flag = RecipeBookService.IsBookmarkGroupParent(num);
				if (indexIsParent || flag)
				{
					ShowPageAfterRecipeDeleteIndex = bookmarkStorageRecipeIndex;
					if (flag)
					{
						_ = ((Book)instance).bookmarkControllersGroupController.controllers.First().bookmarkController;
						BookmarkStorage bookmarkStorage = (from b in StaticStorage.BookmarkGroups[num]
							orderby (Object)(object)instance.savedRecipes[b.recipeIndex] != (Object)null descending, b.SerializedBookmark.position.x descending
							select b).FirstOrDefault();
						num = bookmarkStorage.recipeIndex;
						RecipeBookService.PromoteIndexToParent(bookmarkStorage.recipeIndex);
						localPotion = Managers.Potion.recipeBook.savedRecipes[bookmarkStorage.recipeIndex];
					}
				}
			});
			if ((Object)(object)localPotion != (Object)(object)potion)
			{
				potion = localPotion;
			}
			return true;
		}

		private static bool ShowPageAfterRecipeDelete(Book instance)
		{
			RecipeBook val = (RecipeBook)(object)((instance is RecipeBook) ? instance : null);
			if (val == null)
			{
				return true;
			}
			if (!ShowPageAfterRecipeDeleteIndex.HasValue)
			{
				return true;
			}
			int num = ShowPageAfterRecipeDeleteIndex.Value;
			ShowPageAfterRecipeDeleteIndex = null;
			if ((Object)(object)val.savedRecipes[num] == (Object)null)
			{
				int count = val.savedRecipes.Count;
				for (int i = 0; i < count; i++)
				{
					int num2 = (num + i * -1 + count) % count;
					RecipeBookService.GetBookmarkStorageRecipeIndex(num2, out var indexIsParent);
					if ((Object)(object)val.savedRecipes[num2] != (Object)null && !indexIsParent)
					{
						num = num2;
						break;
					}
				}
			}
			instance.UpdateCurrentPageIndex(num);
			return false;
		}
	}
	public class RemoveBottomLeftPageTurnButtonPatch
	{
		[HarmonyPatch(typeof(Book), "Awake")]
		public class Book_Awake
		{
			private static void Postfix(Book __instance)
			{
				Ex.RunSafe(delegate
				{
					RemoveBottomLeftPageTurnButton(__instance);
				});
			}
		}

		private static void RemoveBottomLeftPageTurnButton(Book instance)
		{
			RecipeBook val = (RecipeBook)(object)((instance is RecipeBook) ? instance : null);
			if (val != null)
			{
				((Component)((Book)val).curlPageController.bottomLeftCorner).gameObject.SetActive(false);
			}
		}
	}
	public class ClearFileSpecificDataOnFileLoadPatch
	{
		[HarmonyPatch(typeof(SaveLoadManager), "LoadFile")]
		public class SaveLoadManager_LoadFile
		{
			private static bool Prefix()
			{
				return Ex.RunSafe(() => SaveLoadService.ClearFileSpecificDataOnFileLoad());
			}
		}
	}
	public class InjectBookmarkGroupingDataIntoSaveFilePatch
	{
		[HarmonyPatch(typeof(SavedState), "ToJson")]
		public class SavedState_ToJson
		{
			private static void Postfix(ref string __result)
			{
				SaveLoadService.StoreBookmarkGroups(ref __result);
			}
		}
	}
	public class LoadBookmarkGroupingDataFromSaveFilePatch
	{
		[HarmonyPatch(typeof(SaveLoadManager), "LoadSelectedState")]
		public class SaveLoadManager_LoadSelectedState
		{
			private static bool Prefix(Type type)
			{
				return Ex.RunSafe(() => SaveLoadService.RetreiveStoredBookmarkGroups(type));
			}
		}
	}
	public class RemoveSubBookmarksFromMainRailsOnLoadPatch
	{
		[HarmonyPatch(typeof(BookmarkController), "LoadFrom")]
		public class BookmarkController_LoadFrom
		{
			private static void Postfix(BookmarkController __instance)
			{
				Ex.RunSafe(delegate
				{
					RemoveSubBookmarksFromMainRailsOnLoad(__instance);
				}, null, logErrorToStorage: true);
			}
		}

		private static void RemoveSubBookmarksFromMainRailsOnLoad(BookmarkController instance)
		{
			if (!((Object)(object)instance != (Object)(object)((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController))
			{
				if (StaticStorage.SavedRecipePositions == null || !StaticStorage.SavedRecipePositions.Any())
				{
					DoHardResetIfNeeded(instance);
					return;
				}
				RemoveSubBookmarksFromMainRails(instance);
				ReorganizeSavedRecipes();
				DoIncorrectCountFailsafe();
				StaticStorage.SavedRecipePositions = null;
				ReparentOrphanedBookmarksFailsafe(instance);
			}
		}

		private static void RemoveSubBookmarksFromMainRails(BookmarkController instance)
		{
			List<BookmarkRail> list = instance.rails.Except((IEnumerable<BookmarkRail>)(object)new BookmarkRail[2]
			{
				StaticStorage.SubRail,
				StaticStorage.InvisiRail
			}).ToList();
			int num = list.Sum((BookmarkRail r) => r.railBookmarks.Count);
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			while (num2 < num)
			{
				BookmarkRail val = list[0];
				while (num4 >= val.railBookmarks.Count)
				{
					num4 = 0;
					list.RemoveAt(0);
					val = list[0];
				}
				bool num6 = StaticStorage.SavedRecipePositions[num5] != num3;
				RecipeBookService.GetBookmarkStorageRecipeIndex(num2, out var indexIsParent);
				if (!num6 && !indexIsParent)
				{
					num4++;
					num2++;
					num5++;
					num3++;
				}
				else
				{
					Bookmark obj = val.railBookmarks[num4];
					val.railBookmarks.RemoveAt(num4);
					Object.Destroy((Object)(object)((Component)obj).gameObject);
					num3++;
					num--;
				}
			}
		}

		private static void ReorganizeSavedRecipes()
		{
			ProgressState selectedProgressState = Managers.SaveLoad.SelectedProgressState;
			List<SerializedPotionRecipe> list = new List<SerializedPotionRecipe>();
			for (int i = 0; i < selectedProgressState.savedRecipes.Count; i++)
			{
				list.Add(selectedProgressState.savedRecipes[StaticStorage.SavedRecipePositions[i]]);
			}
			selectedProgressState.savedRecipes = list;
		}

		private static void DoIncorrectCountFailsafe()
		{
			int num = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetAllBookmarksList().Count;
			int num2 = StaticStorage.SavedRecipePositions?.Count ?? Managers.Potion.recipeBook.savedRecipes.Count;
			if (num == num2)
			{
				return;
			}
			string text = "ERROR: There is an incorrect ammount of bookmarks saved. Running failsafe to fix file! - 2";
			Plugin.PluginLogger.LogError((object)text);
			Ex.SaveErrorMessage(text);
			while (num > num2)
			{
				int count = StaticStorage.InvisiRail.railBookmarks.Count;
				if (count == 0)
				{
					Plugin.PluginLogger.LogError((object)"ERROR: Incorrect count failsafe failed to find enough bookmarks on the invisirail to fix count. To recover save file post it in the Potion Craft discord modding channel!");
					break;
				}
				Bookmark obj = StaticStorage.InvisiRail.railBookmarks[count - 1];
				StaticStorage.InvisiRail.railBookmarks.RemoveAt(count - 1);
				Object.Destroy((Object)(object)((Component)obj).gameObject);
				num--;
			}
		}

		private static void DoHardResetIfNeeded(BookmarkController instance)
		{
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			int i = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetAllBookmarksList().Count;
			int count = Managers.SaveLoad.SelectedProgressState.savedRecipes.Count;
			if (i == count)
			{
				return;
			}
			string text = "ERROR: There is an incorrect ammount of bookmarks saved. Running failsafe to fix file! - 1";
			Plugin.PluginLogger.LogError((object)text);
			Ex.SaveErrorMessage(text);
			StaticStorage.BookmarkGroups.SelectMany((KeyValuePair<int, List<BookmarkStorage>> b) => b.Value).Count();
			List<BookmarkRail> source = instance.rails.Except((IEnumerable<BookmarkRail>)(object)new BookmarkRail[2]
			{
				StaticStorage.SubRail,
				StaticStorage.InvisiRail
			}).ToList();
			while (StaticStorage.InvisiRail.railBookmarks.Count > 0 || StaticStorage.SubRail.railBookmarks.Count > 0 || i > count)
			{
				BookmarkRail val = null;
				val = (BookmarkRail)((StaticStorage.InvisiRail.railBookmarks.Count > 0) ? StaticStorage.InvisiRail : ((StaticStorage.SubRail.railBookmarks.Count <= 0) ? ((object)((IEnumerable<BookmarkRail>)source).LastOrDefault((Func<BookmarkRail, bool>)((BookmarkRail r) => r.railBookmarks.Count > 0))) : ((object)StaticStorage.SubRail)));
				if ((Object)(object)val == (Object)null)
				{
					Plugin.PluginLogger.LogError((object)"ERROR: Incorrect count failsafe failed to find enough bookmarks on any rails to fix count. To recover save file post it in the Potion Craft discord modding channel!");
					return;
				}
				Bookmark obj = val.railBookmarks[val.railBookmarks.Count - 1];
				val.railBookmarks.RemoveAt(val.railBookmarks.Count - 1);
				Object.Destroy((Object)(object)((Component)obj).gameObject);
				i--;
			}
			StaticStorage.BookmarkGroups.Clear();
			StaticStorage.SavedRecipePositions = null;
			for (; i < count; i++)
			{
				Tuple<BookmarkRail, Vector2> tuple = SubRailService.GetSpawnPosition(instance, (SpaceType)3) ?? SubRailService.GetSpawnPosition(instance, (SpaceType)2) ?? SubRailService.GetSpawnPosition(instance, (SpaceType)1) ?? SubRailService.GetSpawnPosition(instance, (SpaceType)0);
				if (tuple == null)
				{
					Plugin.PluginLogger.LogError((object)"There is no empty space for bookmark! Change settings!");
					break;
				}
				tuple.Item1.SpawnBookmarkAt(0, tuple.Item2, false);
			}
		}

		private static void ReparentOrphanedBookmarksFailsafe(BookmarkController instance)
		{
			List<Bookmark> allBookmarkList = instance.GetAllBookmarksList();
			StaticStorage.InvisiRail.railBookmarks.ToList().ForEach(delegate(Bookmark invisiBookmark)
			{
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				int index = allBookmarkList.IndexOf(invisiBookmark);
				if (!StaticStorage.BookmarkGroups.Any((KeyValuePair<int, List<BookmarkStorage>> bg) => bg.Value.Any((BookmarkStorage b) => b.recipeIndex == index)))
				{
					Plugin.PluginLogger.LogError((object)$"ERROR: Orphaned bookmark found at index: {index}. Moving orphaned bookmark to main rails!");
					Tuple<BookmarkRail, Vector2> tuple = SubRailService.GetSpawnPosition(instance, (SpaceType)3) ?? SubRailService.GetSpawnPosition(instance, (SpaceType)2) ?? SubRailService.GetSpawnPosition(instance, (SpaceType)1) ?? SubRailService.GetSpawnPosition(instance, (SpaceType)0) ?? new Tuple<BookmarkRail, Vector2>(instance.rails[0], Vector2.zero);
					SubRailService.ConnectBookmarkToRail(tuple.Item1, invisiBookmark, tuple.Item2);
					allBookmarkList = instance.GetAllBookmarksList();
				}
			});
		}
	}
	public class RetrieveStateJsonStringPatch
	{
		[HarmonyPatch(typeof(File), "Load")]
		public class File_Load
		{
			private static bool Prefix(File __instance)
			{
				return Ex.RunSafe(() => SaveLoadService.RetrieveStateJsonString(__instance));
			}
		}
	}
	public class StoreSubRecipesOntoAvailableRailsOnSavePatch
	{
		[HarmonyPatch(typeof(BookmarkController), "GetSerialized")]
		public class BookmarkController_GetSerialized
		{
			private static bool Prefix(ref SerializedBookmarkController __result, BookmarkController __instance)
			{
				return AssignSubBookmarksToRailsBeforeSerialization(ref __result, __instance);
			}
		}

		private static bool AssignSubBookmarksToRailsBeforeSerialization(ref SerializedBookmarkController result, BookmarkController instance)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			if ((Object)(object)instance != (Object)(object)((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController)
			{
				return true;
			}
			SerializedBookmarkController serialized = new SerializedBookmarkController();
			Ex.RunSafe(delegate
			{
				Dictionary<BookmarkRail, List<(SerializedBookmark, int)>> subBookmarkPositions = new Dictionary<BookmarkRail, List<(SerializedBookmark, int)>>();
				List<BookmarkStorage> list = StaticStorage.BookmarkGroups.SelectMany((KeyValuePair<int, List<BookmarkStorage>> bg) => bg.Value).ToList();
				Tuple<BookmarkRail, Vector2> defaultSpawnPosition = null;
				list.ForEach(delegate(BookmarkStorage bookmark)
				{
					//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
					//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
					//IL_00f1: 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_00fb: 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_011e: Expected O, but got Unknown
					//IL_009a: Unknown result type (might be due to invalid IL or missing references)
					Tuple<BookmarkRail, Vector2> tuple = GetSpawnPosition(instance, (SpaceType)3, subBookmarkPositions) ?? GetSpawnPosition(instance, (SpaceType)2, subBookmarkPositions) ?? GetSpawnPosition(instance, (SpaceType)1, subBookmarkPositions) ?? GetSpawnPosition(instance, (SpaceType)0, subBookmarkPositions);
					if (defaultSpawnPosition == null)
					{
						defaultSpawnPosition = tuple;
					}
					if (tuple == null)
					{
						if (defaultSpawnPosition == null)
						{
							defaultSpawnPosition = new Tuple<BookmarkRail, Vector2>(instance.rails[0], Vector2.zero);
						}
						tuple = defaultSpawnPosition;
					}
					defaultSpawnPosition = MoveDefaultSpawnPosition(defaultSpawnPosition);
					if (!subBookmarkPositions.ContainsKey(tuple.Item1))
					{
						subBookmarkPositions[tuple.Item1] = new List<(SerializedBookmark, int)>();
					}
					SerializedBookmark item = new SerializedBookmark
					{
						position = tuple.Item2,
						prefabIndex = bookmark.SerializedBookmark.prefabIndex,
						isMirrored = bookmark.SerializedBookmark.isMirrored
					};
					subBookmarkPositions[tuple.Item1].Add((item, bookmark.recipeIndex));
				});
				serialized.serializedRails = instance.rails.Select((BookmarkRail rail) => GetSerialized(rail, subBookmarkPositions)).ToList();
				DoFailsafeIfNeeded(instance, serialized, subBookmarkPositions, defaultSpawnPosition);
				RearrangeSavedBookmarksToWorkForBookmarkOrder(serialized, subBookmarkPositions);
			}, null, logErrorToStorage: true);
			result = serialized;
			return false;
		}

		private static Tuple<BookmarkRail, Vector2> MoveDefaultSpawnPosition(Tuple<BookmarkRail, Vector2> defaultSpawnPosition)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			defaultSpawnPosition = new Tuple<BookmarkRail, Vector2>(defaultSpawnPosition.Item1, new Vector2(defaultSpawnPosition.Item2.x + 0.09952f, defaultSpawnPosition.Item2.y));
			return defaultSpawnPosition;
		}

		private static void DoFailsafeIfNeeded(BookmarkController instance, SerializedBookmarkController serialized, Dictionary<BookmarkRail, List<(SerializedBookmark, int)>> subBookmarkPositions, Tuple<BookmarkRail, Vector2> defaultSpawnPosition)
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			int num = Managers.SaveLoad.SelectedProgressState.savedRecipes.Count - serialized.serializedRails.Take(serialized.serializedRails.Count - 2).Sum((SerializedBookmarkRail r) => r.serializedBookmarks.Count);
			if (num <= 0)
			{
				return;
			}
			string text = "ERROR: There are not enough saved bookmarks when saving. Running failsafe to fix file!";
			Plugin.PluginLogger.LogError((object)text);
			Ex.SaveErrorMessage(text);
			for (int i = 0; i < num; i++)
			{
				defaultSpawnPosition = MoveDefaultSpawnPosition(defaultSpawnPosition);
				Tuple<BookmarkRail, Vector2> tuple = GetSpawnPosition(instance, (SpaceType)3, subBookmarkPositions) ?? GetSpawnPosition(instance, (SpaceType)2, subBookmarkPositions) ?? GetSpawnPosition(instance, (SpaceType)1, subBookmarkPositions) ?? GetSpawnPosition(instance, (SpaceType)0, subBookmarkPositions) ?? defaultSpawnPosition ?? new Tuple<BookmarkRail, Vector2>(instance.rails[0], Vector2.zero);
				if (!subBookmarkPositions.ContainsKey(tuple.Item1))
				{
					subBookmarkPositions[tuple.Item1] = new List<(SerializedBookmark, int)>();
				}
				SerializedBookmark item = new SerializedBookmark
				{
					position = tuple.Item2,
					prefabIndex = 0,
					isMirrored = false
				};
				subBookmarkPositions[tuple.Item1].Add((item, -1));
			}
			serialized.serializedRails = instance.rails.Select((BookmarkRail rail) => GetSerialized(rail, subBookmarkPositions)).ToList();
		}

		private static void RearrangeSavedBookmarksToWorkForBookmarkOrder(SerializedBookmarkController serialized, Dictionary<BookmarkRail, List<(SerializedBookmark, int)>> subBookmarkPositions)
		{
			List<(SerializedBookmark, int)> subBookmarkList = subBookmarkPositions.Values.SelectMany((List<(SerializedBookmark, int)> v) => v).ToList();
			List<(int, int)> intList = new List<(int, int)>();
			int addedSubBookmarks = 0;
			int num = serialized.serializedRails.Count - 2;
			for (int j = 0; j < num; j++)
			{
				serialized.serializedRails[j].serializedBookmarks.ForEach(delegate(SerializedBookmark sb)
				{
					int count = intList.Count;
					if (!TryFirst(subBookmarkList, ((SerializedBookmark, int) b) => b.Item1 == sb, out var first))
					{
						intList.Add((intList.Count - addedSubBookmarks, count));
					}
					else
					{
						intList.Add((first.Item2, count));
						addedSubBookmarks++;
					}
				});
			}
			List<SerializedPotionRecipe> list = new List<SerializedPotionRecipe>();
			for (int k = 0; k < Managers.SaveLoad.SelectedProgressState.savedRecipes.Count; k++)
			{
				int item = intList[k].Item1;
				item = GetRecipeIndexForFailsafeIfNeeded(intList, k, item);
				list.Add(Managers.SaveLoad.SelectedProgressState.savedRecipes[item]);
			}
			Managers.SaveLoad.SelectedProgressState.savedRecipes = list;
			StaticStorage.SavedRecipePositions = (from i in intList
				orderby i.Item1
				select i.Item2).ToList();
		}

		private static int GetRecipeIndexForFailsafeIfNeeded(List<(int, int)> intList, int newIndex, int oldIndex)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_00d5: 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_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			if (oldIndex == -1)
			{
				int i2;
				for (i2 = 0; i2 < Managers.SaveLoad.SelectedProgressState.savedRecipes.Count; i2++)
				{
					if (!intList.Any(((int, int) item) => item.Item1 == i2))
					{
						oldIndex = i2;
						intList[newIndex] = (i2, intList[newIndex].Item2);
						List<BookmarkStorage> list = StaticStorage.BookmarkGroups.Values.FirstOrDefault();
						if (list == null)
						{
							list = new List<BookmarkStorage>();
							StaticStorage.BookmarkGroups[0] = list;
						}
						Vector2 val = (Vector2)(((??)list.FirstOrDefault()?.SerializedBookmark?.position) ?? new Vector2(0.5f, 0f));
						((Vector2)(ref val))..ctor(val.x + 0.09953334f, val.y);
						list.Add(new BookmarkStorage
						{
							recipeIndex = i2,
							SerializedBookmark = new SerializedBookmark
							{
								position = val,
								prefabIndex = 0,
								isMirrored = false
							}
						});
						break;
					}
				}
			}
			return oldIndex;
		}

		private static SerializedBookmarkRail GetSerialized(BookmarkRail rail, Dictionary<BookmarkRail, List<(SerializedBookmark, int)>> subBookmarks)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			List<SerializedBookmark> list = rail.railBookmarks.Select((Bookmark bookmark) => bookmark.GetSerialized()).ToList();
			if (subBookmarks.TryGetValue(rail, out var value))
			{
				list = list.Concat(value.Select(((SerializedBookmark, int) b) => b.Item1)).ToList();
				list.Sort((SerializedBookmark b1, SerializedBookmark b2) => b1.position.x.CompareTo(b2.position.x));
			}
			return new SerializedBookmarkRail
			{
				serializedBookmarks = list
			};
		}

		private static bool TryFirst<T>(List<T> values, Func<T, bool> compareFunc, out T first)
		{
			first = default(T);
			foreach (T value in values)
			{
				if (compareFunc(value))
				{
					first = value;
					return true;
				}
			}
			return false;
		}

		public static Tuple<BookmarkRail, Vector2> GetSpawnPosition(BookmarkController bookmarkController, SpaceType spaceType, Dictionary<BookmarkRail, List<(SerializedBookmark, int)>> subBookmarks)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			foreach (BookmarkRail item in bookmarkController.rails.Except((IEnumerable<BookmarkRail>)(object)new BookmarkRail[2]
			{
				StaticStorage.SubRail,
				StaticStorage.InvisiRail
			}).ToList())
			{
				List<MinMaxFloat> emptySegments = GetEmptySegments(item, spaceType, subBookmarks);
				if (emptySegments.Any())
				{
					float num = (item.inverseSpawnOrder ? ((MinMax<float, float>)(object)emptySegments.Last()).max : ((MinMax<float, float>)(object)emptySegments.First()).min);
					object? value = typeof(BookmarkController).GetField("spawnHeight", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(bookmarkController);
					float random = ((MinMax<float, float>)((value is MinMaxFloat) ? value : null)).GetRandom();
					return new Tuple<BookmarkRail, Vector2>(item, new Vector2(num, random));
				}
			}
			return null;
		}

		private static List<MinMaxFloat> GetEmptySegments(BookmarkRail rail, SpaceType spaceType, Dictionary<BookmarkRail, List<(SerializedBookmark, int)>> subBookmarks)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			List<MinMaxFloat> emptySegments = rail.GetEmptySegments(spaceType, (Bookmark)null);
			if (!subBookmarks.ContainsKey(rail))
			{
				return emptySegments;
			}
			List<(SerializedBookmark, int)> list = subBookmarks[rail].OrderBy(((SerializedBookmark, int) b) => b.Item1.position.x).ToList();
			for (int i = 0; i < emptySegments.Count; i++)
			{
				if (!list.Any())
				{
					break;
				}
				MinMaxFloat val = emptySegments[i];
				(SerializedBookmark, int) tuple = list[0];
				if (!(tuple.Item1.position.x < ((MinMax<float, float>)(object)val).min))
				{
					MinMaxFloat item = new MinMaxFloat(((MinMax<float, float>)(object)val).min, tuple.Item1.position.x);
					int index = i + 1;
					emptySegments.Insert(index, item);
					list.RemoveAt(0);
				}
			}
			return emptySegments;
		}
	}
	public class UpdateUIOnRecipeBookLoadPatch
	{
		[HarmonyPatch(typeof(RecipeBook), "OnLoad")]
		public class RecipeBook_OnLoad
		{
			private static void Postfix()
			{
				Ex.RunSafe(delegate
				{
					UpdateUIOnRecipeBookLoad();
				});
			}
		}

		private static void UpdateUIOnRecipeBookLoad()
		{
			StaticStorage.IsLoaded = true;
			SubRailService.UpdateStaticBookmark();
			EnsureSubBookmarksAreShowingFailsafe();
		}

		private static void EnsureSubBookmarksAreShowingFailsafe()
		{
			int pageIndex = ((Book)Managers.Potion.recipeBook).currentPageIndex;
			int bookmarkStorageRecipeIndex = RecipeBookService.GetBookmarkStorageRecipeIndex(pageIndex);
			List<Bookmark> allBookmarks = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.GetAllBookmarksList();
			var list = (from sb in (from s in SubRailService.GetSubRailRecipesForIndex(bookmarkStorageRecipeIndex)
					select new
					{
						savedBookmark = s,
						bookmark = allBookmarks[s.recipeIndex],
						isActive = (pageIndex == s.recipeIndex)
					}).ToList()
				where StaticStorage.SubRail.railBookmarks.All((Bookmark rb) => (Object)(object)sb.bookmark != (Object)(object)rb)
				select sb).ToList();
			if (list.Any())
			{
				Plugin.PluginLogger.LogError((object)"ERROR:  Not all bookmarks are showing on subrail on load! Adding missing bookmarks to subrail.");
				list.ForEach(savedBookmark =>
				{
					//IL_0025: Unknown result type (might be due to invalid IL or missing references)
					Plugin.PluginLogger.LogMessage((object)"Adding bookmark");
					SubRailService.ConnectBookmarkToRail(StaticStorage.SubRail, savedBookmark.bookmark, savedBookmark.savedBookmark.SerializedBookmark.position);
					savedBookmark.bookmark.CurrentVisualState = (VisualState)(savedBookmark.isActive ? 1 : 0);
				});
			}
		}
	}
	public class CreateSubRailForRecipePagePatch
	{
		[HarmonyPatch(typeof(PotionCustomizationPanel), "OnPanelContainerStart")]
		public class PotionCustomizationPanel_OnPanelContainerStart
		{
			private static void Postfix(PotionCustomizationPanel __instance)
			{
				Ex.RunSafe(delegate
				{
					CreateSubRailForRecipePage(__instance);
				});
			}
		}

		private static void CreateSubRailForRecipePage(PotionCustomizationPanel instance)
		{
			RecipeBookLeftPageContent componentInParent = ((Component)instance).gameObject.GetComponentInParent<RecipeBookLeftPageContent>();
			if (!((Object)(object)componentInParent == (Object)null))
			{
				if ((Object)(object)StaticStorage.SubRail != (Object)null)
				{
					CreateStaticRailCopyForPage(componentInParent);
					ShrinkDescriptionBox(componentInParent);
					return;
				}
				RecipeBookService.SetupListeners();
				SetupInvisiRail();
				(GameObject, GameObject) tuple = SetupBookmarkContainer(componentInParent);
				SetupRail(tuple.Item1, tuple.Item2);
				CreateStaticRailCopyForPage(componentInParent);
				ShrinkDescriptionBox(componentInParent);
			}
		}

		private static void SetupRail(GameObject subRailPages, GameObject subRailBookmarkContainer)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			BookmarkController bookmarkController = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController;
			GameObject val = new GameObject("BottomToTopSubRail");
			val.transform.parent = ((Component)bookmarkController).gameObject.transform;
			val.transform.localPosition = subRailBookmarkContainer.transform.localPosition;
			BookmarkRail val2 = (StaticStorage.SubRail = val.AddComponent<BookmarkRail>());
			BookmarkController bookmarkController2 = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController;
			val.transform.parent = ((Component)bookmarkController2).transform;
			subRailBookmarkContainer.transform.parent = ((Component)val2).transform;
			subRailBookmarkContainer.transform.localPosition = Vector2.op_Implicit(Vector2.zero);
			val2.bookmarksContainer = subRailBookmarkContainer.transform;
			Transform val3 = StaticStorage.SubRailLayers.First();
			typeof(BookmarkRail).GetField("activeLayer", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(val2, val3);
			val3.localPosition = Vector3.zero;
			val3.localEulerAngles = Vector3.zero;
			SpriteRenderer componentInChildren = subRailPages.GetComponentInChildren<SpriteRenderer>();
			val2.size = new Vector2(componentInChildren.size.y - 0.5f, 0.7f);
			val2.direction = (Direction)3;
			val2.bottomLine = typeof(BookmarkRail).GetMethod("GetBottomLine", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(val2, null) as Tuple<Vector2, Vector2>;
			typeof(BookmarkRail).GetMethod("OnValidate", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(val2, null);
			val2.bookmarkController.rails.Add(val2);
			SetupStaticBookmark(val2);
		}

		private static void SetupStaticBookmark(BookmarkRail subRail)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			Vector2 value = SubRailService.GetNextEmptySpaceOnRail(subRail, getMax: true).Value;
			float num = subRail.bookmarkController.minBookmarksSpace * 0.75f;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(value.x + num, -0.04f);
			StaticStorage.StaticBookmark = subRail.SpawnBookmarkAt(0, val, false);
			((Component)StaticStorage.StaticBookmark).transform.parent = StaticStorage.SubRailActiveBookmarkLayer.transform;
			subRail.railBookmarks.Clear();
		}

		private static void SetupInvisiRail()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			BookmarkController bookmarkController = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController;
			GameObject val = new GameObject("BottomToTopInvisiRail");
			val.transform.localPosition = Vector2.op_Implicit(new Vector2(0f, 300f));
			val.transform.parent = ((Component)bookmarkController).gameObject.transform;
			GameObject val2 = new GameObject("InvisiRailBookmarkContainer");
			GameObject val3 = new GameObject("Layer 0");
			val3.transform.parent = val2.transform;
			StaticStorage.InvisiRailLayer = val3.transform;
			BookmarkRail val4 = (StaticStorage.InvisiRail = val.AddComponent<BookmarkRail>());
			BookmarkController bookmarkController2 = ((Book)Managers.Potion.recipeBook).bookmarkControllersGroupController.controllers.First().bookmarkController;
			val.transform.parent = ((Component)bookmarkController2).transform;
			val2.transform.parent = ((Component)val4).transform;
			val2.transform.localPosition = Vector2.op_Implicit(Vector2.zero);
			val4.bookmarksContainer = val2.transform;
			Transform transform = val3.transform;
			typeof(BookmarkRail).GetField("activeLayer", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(val4, transform);
			transform.localPosition = Vector3.zero;
			transform.localEulerAngles = Vector3.zero;
			val4.size = new Vector2(100f, 0.7f);
			val4.direction = (Direction)3;
			val4.bottomLine = typeof(BookmarkRail).GetMethod("GetBottomLine", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(val4, null) as Tuple<Vector2, Vector2>;
			typeof(BookmarkRail).GetMethod("OnValidate", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(val4, null);
			val4.bookmarkController.rails.Add(val4);
		}

		private static (GameObject, GameObject) SetupBookmarkContainer(RecipeBookLeftPageContent page)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("SubRailBookmarkContainer");
			GameObject val2 = (StaticStorage.SubRailPages = new GameObject("SubRailPages"));
			Transform parent = ((Component)Managers.Potion.recipeBook).transform.Find("ContentContainer").Find("BackgroundPages");
			val2.transform.parent = parent;
			Sprite val3 = SaveLoadService.GenerateSpriteFromImage("PotionCraftBookmarkOrganizer.InGameImages.Bookmark_organizer_recipe_slot_bottom_left_mask.png", null, createComplexMesh: true);
			if ((Object)(object)val3 == (Object)null)
			{
				return (null, null);
			}
			object? value = typeof(RecipeBookLeftPageContent).GetField("titleDecorLeftRenderer", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(page);
			object? obj = ((value is SpriteRenderer) ? value : null);
			int sortingLayerID = ((Renderer)obj).sortingLayerID;
			string sortingLayerName = ((Renderer)obj).sortingLayerName;
			int num = 511;
			for (int num2 = 4 - 1; num2 >= 0; num2--)
			{
				GameObject val4 = new GameObject($"Layer{num2}");
				val4.transform.parent = val2.transform;
				val4.AddComponent<PageLayer>();
				SpriteRenderer val5 = val4.AddComponent<SpriteRenderer>();
				Sprite val6 = SaveLoadService.GenerateSpriteFromImage($"PotionCraftBookmarkOrganizer.InGameImages.Bookmark_organizer_recipe_slot_{num2}.png");
				if ((Object)(object)val6 == (Object)null)
				{
					return (null, null);
				}
				val5.sprite = val6;
				((Renderer)val5).sortingLayerID = sortingLayerID;
				((Renderer)val5).sortingLayerName = sortingLayerName;
				((Renderer)val5).sortingOrder = num;
				BoxCollider2D val7 = val4.AddComponent<BoxCollider2D>();
				if (num2 == 0)
				{
					((Collider2D)val7).isTrigger = true;
				}
				val7.size = val5.size;
				(GameObject, GameObject) tuple = CreateBookmarkLayer(val, val3, sortingLayerID, sortingLayerName, num, $"Layer {num2}");
				StaticStorage.SubRailLayers.Add(tuple.Item1.transform);
				StaticStorage.SubRailLayers.Add(tuple.Item1.transform);
				StaticStorage.SubRailLayers.Add(tuple.Item1.transform);
				if (num2 == 0)
				{
					GameObject item = tuple.Item2;
					SpriteRenderer val8 = item.AddComponent<SpriteRenderer>();
					val8.sprite = val3;
					DummyInteractiveItem dummyInteractiveItem = item.AddComponent<DummyInteractiveItem>();
					((InteractiveItem)dummyInteractiveItem).raycastPriorityLevel = -11000f;
					((InteractiveItem)dummyInteractiveItem).cursorRadiusCollision = false;
					((InteractiveItem)dummyInteractiveItem).showOnlyFingerWhenInteracting = true;
					item.AddComponent<PolygonCollider2D>();
					item.layer = 5;
					((Renderer)val8).enabled = false;
				}
				num += 20;
			}
			StaticStorage.SubRailActiveBookmarkLayer = CreateBookmarkLayer(val, val3, sortingLayerID, sortingLayerName, num - 11, "ActiveBookmarkLayer").Item1;
			StaticStorage.SubRailLayers.Reverse();
			val.transform.localPosition = Vector2.op_Implicit(new Vector2(-7.594f, -4.13f));
			val2.transform.localPosition = Vector2.op_Implicit(new Vector2(-7.165f, -4.07f));
			return (val2, val);
		}

		private static void CreateStaticRailCopyForPage(RecipeBookLeftPageContent parentPage)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("StaticSubRail");
			val.transform.parent = ((Component)parentPage).transform;
			val.transform.localPosition = Vector2.op_Implicit(new Vector2(-3.443f, -4.07f));
			SpriteRenderer component = ((Component)((Component)parentPage).transform.parent.Find("Scratches")).GetComponent<SpriteRenderer>();
			SortingGroup obj = val.AddComponent<SortingGroup>();
			obj.sortingLayerID = ((Renderer)component).sortingLayerID;
			obj.sortingLayerName = ((Renderer)component).sortingLayerName;
			obj.sortingOrder = ((Renderer)component).sortingOrder + 1;
			GameObject val2 = Object.Instantiate<GameObject>(StaticStorage.SubRailPages, val.transform);
			val2.transform.localPosition = Vector2.op_Implicit(new Vector2(0.2933f, 0f));
			val2.SetActive(false);
			GameObject val3 = new GameObject("StaticBookmarkContainer");
			val3.transform.parent = val.transform;
			val3.transform.localPosition = Vector2.op_Implicit(new Vector2(-0.1365f, -0.06f));
			val3.SetActive(false);
			StaticStorage.StaticRails[parentPage] = (val3, val2);
		}

		private static (GameObject, GameObject) CreateBookmarkLayer(GameObject subRailBookmarkContainer, Sprite maskSprite, int sortingLayerId, string sortingLayerName, int currentSortOrder, string layerName)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(layerName);
			val.transform.parent = subRailBookmarkContainer.transform;
			SortingGroup obj = val.AddComponent<SortingGroup>();
			obj.sortingLayerID = sortingLayerId;
			obj.sortingLayerName = sortingLayerName;
			obj.sortingOrder = currentSortOrder - 10;
			GameObject val2 = new GameObject("BookmarkMask");
			val2.transform.parent = val.transform;
			val2.AddComponent<SpriteMask>().sprite = maskSprite;
			val2.transform.rotation = Quaternion.Euler(0f, 0f, 270f);
			Transform transform = val2.transform;
			transform.localPosition += new Vector3(0.06f, -1.61f, 0.96f);
			return (val, val2);
		}

		private static void ShrinkDescriptionBox(RecipeBookLeftPageContent parentPage)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: 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_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)((Component)parentPage.potionCustomizationPanel).transform.Find("InputFieldCanvas")).transform;
			Transform val = transform.Find("DescriptionTop");
			Transform val2 = transform.Find("DescriptionBottom");
			Transform val3 = transform.Find("DescriptionBackground Top");
			Transform val4 = transform.Find("DescriptionBackground Bottom");
			Transform val5 = transform.Find("DescriptionBackground Left");
			Transform val6 = transform.Find("DescriptionBackground Right");
			Transform obj = transform.Find("Maskable");
			Vector3 val7 = default(Vector3);
			((Vector3)(ref val7))..ctor(0.3309f, 0f);
			Transform transform2 = ((Component)val3).transform;
			transform2.localPosition += val7;
			Transform transform3 = ((Component)val4).transform;
			transform3.localPosition += val7;
			Transform transform4 = ((Component)val).transform;
			transform4.localPosition += val7;
			Transform transform5 = ((Component)val2).transform;
			transform5.localPosition += val7;
			Transform transform6 = ((Component)obj).transform;
			transform6.localPosition += val7;
			((Vector3)(ref val7))..ctor(0.02f, 0f);
			Transform transform7 = ((Component)val6).transform;
			transform7.localPosition += val7;
			((Vector3)(ref val7))..ctor(0.64f, 0f);
			Transform transform8 = ((Component)val5).transform;
			transform8.localPosition += val7;
			Vector3 localScale = ((Component)val3).transform.localScale;
			Vector3 localScale2 = default(Vector3);
			((Vector3)(ref localScale2))..ctor(localScale.x * 0.9018f, localScale.y, localScale.z);
			((Component)val3).transform.localScale = localScale2;
			((Component)val4).transform.localScale = localScale2;
			((Component)val).transform.localScale = localScale2;
			((Component)val2).transform.localScale = localScale2;
			Vector3 localScale3 = default(Vector3);
			((Vector3)(ref localScale3))..ctor(0.9018f, 1f, 1f);
			((Component)obj).transform.localScale = localScale3;
		}
	}
	public class DisableGrabbingForInactiveStaticBookmarkPatch
	{
		[HarmonyPatch(typeof(BookmarkButtonInactive), "OnGrabPrimary")]
		public class InactiveBookmarkButton_OnGrabPrimary
		{
			private static bool Prefix(BookmarkButtonInactive __instance)
			{
				return Ex.RunSafe(() => DisableGrabbingForInactiveStaticBookmark(__instance));
			}
		}

		private static bool DisableGrabbingForInactiveStaticBookmark(BookmarkButtonInactive instance)
		{
			return (Object)(object)((BookmarkButton)instance).bookmark != (Object)(object)StaticStorage.StaticBookmark;
		}
	}
	public class DisableGrabbingForStaticBookmarkPatch
	{
		[HarmonyPatch(typeof(Bookmark), "UpdateMovingState")]
		public class Bookmark_UpdateMovingState
		{
			private static bool Prefix(Bookmark __instance, MovingState value)
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				return Ex.RunSafe(() => DisableGrabbingForStaticBookmark(__instance, value));
			}
		}

		private static bool DisableGrabbingForStaticBookmark(Bookmark instance, MovingState value)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			if ((Object)(object)instance != (Object)(object)StaticStorage.StaticBookmark)
			{
				return true;
			}
			if ((int)value == 1)
			{
				return false;
			}
			return true;
		}
	}
	public class DisableMovingBookmarkToOwnSubRailPatch
	{
		[HarmonyPatch(typeof(BookmarkRail), "Connect")]
		public class BookmarkRail_Connect
		{
			private static bool Prefix(BookmarkRail __instance, Bookmark bookmark)
			{
				return Ex.RunSafe(() => DisableMovingBookmarkToOwnSubRail(__instance, bookmark));
			}
		}

		private static bool DisableMovingBookmarkToOwnSubRail(BookmarkRail instance, Bookmark bookmark)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			if (!StaticStorage.IsLoaded)
			{
				return true;
			}
			if ((Object)(object)instance != (Object)(object)StaticStorage.SubRail)
			{
				return true;
			}
			if ((int)bookmark.CurrentMovingState == 0)
			{
				return true;
			}
			int num = (typeof(Bookmark).GetField("bookmarksListBeforeMoving", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(bookmark) as List<Bookmark>).IndexOf(bookmark);
			bool flag = RecipeBookService.IsBookmarkGroupParent(num);
			if (num != ((Book)Managers.Potion.recipeBook).currentPageIndex && !flag)
			{
				return true;
			}
			Vector2 mouseWorldPosition = Managers.Input.controlsProvider.CurrentMouseWorldPosition;
			BookmarkRail val = (from bRail in instance.bookmarkController.rails.Except((IEnumerable<BookmarkRail>)(object)new BookmarkRail[1] { instance })
				orderby VectorExtension.GetDistanceToLineSegment(mouseWorldPosition - Vector2.op_Implicit(((Component)bRail).transform.position), bRail.bottomLine.Item1, bRail.bottomLine.Item2)
				select bRail).First();
			Dictionary<BookmarkRail, List<MinMaxFloat>> dictionary = typeof(Bookmark).GetField("emptySegmentsForMoving", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(bookmark) as Dictionary<BookmarkRail, List<MinMaxFloat>>;
			if ((Object)(object)val == (Object)(object)bookmark.rail || dictionary[val].Count == 0)
			{
				return false;
			}
			val.Connect(bookmark, val.GetMovingToNormalizedPosition(dictionary[val], bookmark));
			return false;
		}
	}
	public class DisableRecipeBookHotkeysWhileDraggingBookmarkPatch
	{
		[HarmonyPatch(typeof(CurlPageCornerController), "FlipPage")]
		public class CurlPageCornerController_FlipPage
		{
			private static bool Prefix()
			{
				return Ex.RunSafe(() => DisableRecipeBookHotkeysWhileDraggingBookmark());
			}
		}

		private static bool DisableRecipeBookHotkeysWhileDraggingBookmark()
		{
			return !(Managers.Cursor.grabbedInteractiveItem is BookmarkButtonInactive);
		}
	}
	public class DisallowSpawningOfNewBookmarksOnSpecialRailsPatch
	{
		[HarmonyPatch(typeof(BookmarkController), "AddNewBookmark")]
		public class Bookmark_AddNewBookmark
		{
			private static bool Prefix()
			{
				return Ex.RunSafe(() => SetAddingBookmarkFlag(adding: true));
			}

			private static void Postfix()
			{
				Ex.RunSafe(() => SetAddingBookmarkFlag(adding: false));
			}
		}

		[HarmonyPatch(typeof(BookmarkRail), "GetEmptySegments")]
		public class BookmarkRail_GetEmptySegments
		{
			private static bool Prefix(ref List<MinMaxFloat> __result, BookmarkRail __instance)
			{
				return DisallowSpawningOfNewBookmarksOnSpecialRails(ref __result, __instance);
			}
		}

		private static bool addingNewBookmark;

		private static bool SetAddingBookmarkFlag(bool adding)
		{
			addingNewBookmark = adding;
			return true;
		}

		private static bool DisallowSpawningOfNewBookmarksOnSpecialRails(ref List<MinMaxFloat> result, BookmarkRail instance)
		{
			if (!addingNewBookmark)
			{
				return true;
			}
			if ((Object)(object)instance != (Object)(object)StaticStorage.SubRail && (Object)(object)instance != (Object)(object)StaticStorage.InvisiRail)
			{
				return true;
			}
			result = new List<MinMaxFloat>();
			return false;
		}
	}
	public class HideStaticRailsAndShowRealRailAfterFlipAnimationPatch
	{
		[HarmonyPatch(typeof(CurlPageController), "OnPageFlippingEnd")]
		public class CurlPageController_OnPageFlippingEnd
		{
			private static void Postfix(CurlPageController __instance)
			{
				Ex.RunSafe(delegate
				{
					HideStaticRailsAndShowRealRailAfterFlipAnimation(__instance);
				});
			}
		}

		private static void HideStaticRailsAndShowRealRailAfterFlipAnimation(CurlPageController instance)
		{
			SubRailService.ShowSubRailAfterFlip();
			SubRailService.UpdateNonSubBookmarksActiveState();
		}
	}
	public class OpenParentRecipeOnInactiveStaticBookmarkClickPatch
	{
		[HarmonyPatch(typeof(BookmarkButtonInactive), "OnReleasePrimary")]
		public class InactiveBookmarkButton_OnReleasePrimary
		{
			private static bool Prefix(BookmarkButtonInactive __instance)
			{
				return Ex.RunSafe(() => DisableGrabbingForInactiveStaticBookmark(__instance));
			}
		}

		private static bool DisableGrabbingForInactiveStaticBookmark(BookmarkButtonInactive instance)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			Bookmark bookmark = ((BookmarkButton)instance).bookmark;
			if ((Object)(object)bookmark != (Object)(object)StaticStorage.StaticBookmark)
			{
				return true;
			}
			int bookmarkStorageRecipeIndexForSelectedRecipe = RecipeBookService.GetBookmarkStorageRecipeIndexForSelectedRecipe();
			Bookmark bookmarkByIndex = bookmark.rail.bookmarkController.GetBookmarkByIndex(bookmarkStorageRecipeIndexForSelectedRecipe);
			bookmark.rail.bookmarkController.bookmarkControllersGroupController.SetActiveBookmark(bookmarkByIndex, true);
			bookmark.CurrentVisualState = (VisualState)1;
			((BookmarkButton)bookmark.activeBookmarkButton).slot.Selected = true;
			Bounds bounds = ((Button)bookmark.activeBookmarkButton).thisCollider.bounds;
			if (((Bounds)(ref bounds)).Contains(Vector2.op_Implicit(Managers.Input.controlsProvider.CurrentMouseWorldPosition)))
			{
				((Button)bookmark.activeBookmarkButton).SetHovered(true);
			}
			typeof(BookmarkButtonInactive).GetField("grabConditionChecked", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(instance, true);
			return false;
		}
	}
	public class OverrideGetTooltipContentForStaticBookmarkPatch
	{
		[HarmonyPatch(typeof(RecipeBookBookmarkTooltipContentContainer), "GetTooltipContent")]
		public class RecipeBookBookmarkTooltipContentContainer_GetTooltipContent
		{
			private static bool Prefix(ref TooltipContent __result, Bookmark ___bookmark)
			{
				return OverrideGetTooltipContentForStaticBookmark(ref __result, ___bookmark);
			}
		}

		private static bool OverrideGetTooltipContentForStaticBookmark(ref TooltipContent __result, Bookmark bookmark)
		{
			if ((Object)(object)bookmark != (Object)(object)StaticStorage.StaticBookmark)
			{
				return true;
			}
			int bookmarkStorageRecipeIndexForSelectedRecipe = RecipeBookService.GetBookmarkStorageRecipeIndexForSelectedRecipe();
			Potion obj = Managers.Potion.recipeBook.savedRecipes[bookmarkStorageRecipeIndexForSelectedRecipe];
			__result = ((obj != null) ? ((InventoryItem)obj).GetTooltipContent(1, (ItemsPanel)null, false) : null);
			return false;
		}
	}
	public class OverrideNextIndexWhenSwitchingPagesPatch
	{
		[HarmonyPatch(typeof(Book), "OnBookmarksRearranged")]
		public class Book_OnBookmarksRearranged
		{
			private static void Postfix(Book __instance, List<int> newIndexes)
			{
				Ex.RunSafe(delegate
				{
					UpdatePageIndexesOnRearrange(__instance, newIndexes);
				});
			}
		}

		[HarmonyPatch(typeof(Book), "GetNextPageIndex")]
		public class Book_GetNextPageIndex
		{
			private static bool Prefix(ref int __result, Book __instance)
			{
				return OverrideNextIndexWhenSwitchingPages(ref __result, __instance, next: true);
			}
		}

		[HarmonyPatch(typeof(Book), "GetPreviousPageIndex")]
		public class Book_GetPreviousPageIndex
		{
			private static bool Prefix(ref int __result, Book __instance)
			{
				return OverrideNextIndexWhenSwitchingPages(ref __result, __instance, next: false);
			}
		}

		private static bool OverrideNextIndexWhenSwitchingPages(ref int result, Book instance, bool next)
		{
			if (!(instance is RecipeBook))
			{
				return true;
			}
			int newResult = -1;
			Ex.RunSafe(delegate
			{
				int currentPageIndex = instance.currentPageIndex;
				bool indexIsParent;
				int bookmarkStorageRecipeIndex = RecipeBookService.GetBookmarkStorageRecipeIndex(currentPageIndex, out indexIsParent);
				bool flag = RecipeBookService.IsBookmarkGroupParent(currentPageIndex);
				if (!indexIsParent && !flag)
				{
					if (next)
					{
						int pagesCountWithoutSpecialRails = SubRailService.GetPagesCountWithoutSpecialRails();
						newResult = (currentPageIndex + 1 + pagesCountWithoutSpecialRails) % pagesCountWithoutSpecialRails;
					}
					else
					{
						newResult = GetPreviousIndexForPreviousGroup(bookmarkStorageRecipeIndex);
					}
				}
				else
				{
					List<BookmarkStorage> currentBookmarkGroup = StaticStorage.BookmarkGroups[bookmarkStorageRecipeIndex];
					BookmarkStorage bookmarkStorage = null;
					if (indexIsParent)
					{
						bookmarkStorage = GetNextBookmarkFromGroup(currentBookmarkGroup, currentPageIndex, next, isParent: false);
						if (bookmarkStorage != null)
						{
							newResult = bookmarkStorage.recipeIndex;
						}
						else if (next)
						{
							int pagesCountWithoutSpecialRails2 = SubRailService.GetPagesCountWithoutSpecialRails();
							newResult = (bookmarkStorageRecipeIndex + 1 + pagesCountWithoutSpecialRails2) % pagesCountWithoutSpecialRails2;
						}
						else
						{
							newResult = bookmarkStorageRecipeIndex;
						}
					}
					else if (next)
					{
						newResult = GetNextBookmarkFromGroup(currentBookmarkGroup, currentPageIndex, next, isParent: true).recipeIndex;
					}
					else
					{
						newResult = GetPreviousIndexForPreviousGroup(bookmarkStorageRecipeIndex);
					}
				}
			});
			if (newResult >= 0)
			{
				result = newResult;
				return false;
			}
			return true;
		}

		private static int GetPreviousIndexForPreviousGroup(int groupIndex)
		{
			int pagesCountWithoutSpecialRails = SubRailService.GetPagesCountWithoutSpecialRails();
			int num = (groupIndex - 1 + pagesCountWithoutSpecialRails) % pagesCountWithoutSpecialRails;
			return (RecipeBookService.IsBookmarkGroupParent(num) ? StaticStorage.BookmarkGroups[num] : new List<BookmarkStorage>()).OrderBy((BookmarkStorage b) => b.recipeIndex).FirstOrDefault()?.recipeIndex ?? num;
		}

		private static BookmarkStorage GetNextBookmarkFromGroup(List<BookmarkStorage> currentBookmarkGroup, int currentBookIndex, bool next, bool isParent)
		{
			if (!isParent)
			{
				currentBookmarkGroup = currentBookmarkGroup.Where((BookmarkStorage b) => (!next) ? (b.recipeIndex > currentBookIndex) : (b.recipeIndex < currentBookIndex)).ToList();
			}
			if (next)
			{
				return currentBookmarkGroup.OrderByDescending((BookmarkStorage b) => b.recipeIndex).FirstOrDefault();
			}
			return currentBookmarkGroup.OrderBy((BookmarkStorage b) => b.recipeIndex).FirstOrDefault();
		}

		private static void UpdatePageIndexesOnRearrange(Book instance, List<int> newIndexes)
		{
			UpdatePageIndexesOnRearrange(instance.curlPageController.frontLeftPage, newIndexes);
			UpdatePageIndexesOnRearrange(instance.curlPageController.frontRightPage, newIndexes);
			UpdatePageIndexesOnRearrange(instance.curlPageController.backLeftPage, newIndexes);
			UpdatePageIndexesOnRearrange(instance.curlPageController.backRightPage, newIndexes);
		}

		private static void UpdatePageIndexesOnRearrange(Page page, List<int> newIndexes)
		{
			page.pageIndex = newIndexes.IndexOf(page.pageIndex);
		}
	}
	public class OverrideSubRailLayersPatch
	{
		[HarmonyPatch(typeof(BookmarkRail), "SpawnLayers")]
		public class BookmarkRail_SpawnLayers
		{
			private static bool Prefix(BookmarkRail __instance)
			{
				return Ex.RunSafe(() => OverrideSubRailLayers(__instance));
			}
		}

		private static bool OverrideSubRailLayers(BookmarkRail instance)
		{
			if (SubRailService.IsInvisiRail(instance))
			{
				Transform[] value = (Transform[])(object)new Transform[1] { StaticStorage.InvisiRailLayer };
				typeof(BookmarkRail).GetField("layers", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(instance, value);
				return false;
			}
			if (!SubRailService.IsSubRail(instance))
			{
				return true;
			}
			Transform[] value2 = StaticStorage.SubRailLayers.ToArray();
			typeof(BookmarkRail).GetField("layers", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(instance, value2);
			return false;
		}
	}
	public class ReturnCustomLayersToSubRailPatch
	{
		[HarmonyPatch(typeof(BookmarkController), "GetLayer")]
		public class BookmarkController_GetLayer
		{
			private static bool Prefix(ref BookmarkLayer __result, BookmarkController __instance, int index)
			{
				return ReturnCustomLayersToSubRail(ref __result, __instance, index);
			}
		}

		private static bool ReturnCustomLayersToSubRail(ref BookmarkLayer __result, BookmarkController instance, int index)
		{
			return true;
		}
	}
	public class SetupBookmarkMaskingPatch
	{
		[HarmonyPatch(typeof(Bookmark