using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using EntityStates;
using HG;
using RoR2.EntitlementManagement;
using RoR2.ExpansionManagement;
using RoR2.Stats;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

namespace RoR2.UI.LogBook;

public class LogBookController : MonoBehaviour
{
	private class NavigationPageInfo
	{
		public CategoryDef categoryDef;

		public Entry[] entries;

		public int index;

		public int indexInCategory;
	}

	private class LogBookState : EntityState
	{
		protected LogBookController logBookController;

		protected float unscaledAge;

		public override void OnEnter()
		{
			base.OnEnter();
			logBookController = GetComponent<LogBookController>();
		}

		public override void Update()
		{
			base.Update();
			unscaledAge += Time.unscaledDeltaTime;
		}
	}

	private class FadeState : LogBookState
	{
		private CanvasGroup canvasGroup;

		public float duration = 0.5f;

		public float endValue;

		public override void OnEnter()
		{
			base.OnEnter();
			canvasGroup = GetComponent<CanvasGroup>();
			if ((bool)canvasGroup)
			{
				canvasGroup.alpha = 0f;
			}
		}

		public override void OnExit()
		{
			if ((bool)canvasGroup)
			{
				canvasGroup.alpha = endValue;
			}
			base.OnExit();
		}

		public override void Update()
		{
			if ((bool)canvasGroup)
			{
				canvasGroup.alpha = unscaledAge / duration;
				if (canvasGroup.alpha >= 1f)
				{
					outer.SetNextState(new Idle());
				}
			}
			base.Update();
		}
	}

	private class ChangeEntriesPageState : LogBookState
	{
		private int oldPageIndex;

		public NavigationPageInfo newNavigationPageInfo;

		public float duration = 0.1f;

		public Vector2 moveDirection;

		private GameObject oldPage;

		private GameObject newPage;

		private Vector2 oldPageTargetPosition;

		private Vector2 newPageTargetPosition;

		private Vector2 containerSize = Vector2.zero;

		public override void OnEnter()
		{
			base.OnEnter();
			if ((bool)logBookController)
			{
				oldPageIndex = logBookController.currentPageIndex;
				oldPage = logBookController.currentEntriesPageObject;
				newPage = logBookController.BuildEntriesPage(newNavigationPageInfo);
				containerSize = logBookController.entryPageContainer.rect.size;
			}
			SetPagePositions(0f);
		}

		public override void OnExit()
		{
			base.OnExit();
			EntityState.Destroy(oldPage);
			if ((bool)logBookController)
			{
				logBookController.currentEntriesPageObject = newPage;
				logBookController.currentPageIndex = newNavigationPageInfo.indexInCategory;
			}
		}

		private void SetPagePositions(float t)
		{
			Vector2 vector = new Vector2(containerSize.x * (0f - moveDirection.x), containerSize.y * moveDirection.y);
			Vector2 vector2 = vector * t;
			if ((bool)oldPage)
			{
				oldPage.transform.localPosition = vector2;
			}
			if ((bool)newPage)
			{
				newPage.transform.localPosition = vector2 - vector;
			}
		}

		public override void Update()
		{
			base.Update();
			float num = Mathf.Clamp01(unscaledAge / duration);
			SetPagePositions(num);
			if (num == 1f)
			{
				outer.SetNextState(new Idle());
			}
		}
	}

	private class ChangeCategoryState : LogBookState
	{
		private int oldCategoryIndex;

		public int newCategoryIndex;

		public bool goToLastPage;

		public float duration = 0.1f;

		private GameObject oldPage;

		private GameObject newPage;

		private Vector2 oldPageTargetPosition;

		private Vector2 newPageTargetPosition;

		private Vector2 moveDirection;

		private Vector2 containerSize = Vector2.zero;

		private NavigationPageInfo[] newNavigationPages;

		private int destinationPageIndex;

		private NavigationPageInfo newNavigationPageInfo;

		private int frame;

		public override void OnEnter()
		{
			base.OnEnter();
			if ((bool)logBookController)
			{
				oldCategoryIndex = logBookController.currentCategoryIndex;
				oldPage = logBookController.currentEntriesPageObject;
				newNavigationPages = logBookController.GetCategoryPages(newCategoryIndex);
				destinationPageIndex = newNavigationPages[0].index;
				if (goToLastPage)
				{
					destinationPageIndex = newNavigationPages[newNavigationPages.Length - 1].index;
					Debug.LogFormat("goToLastPage=true destinationPageIndex={0}", destinationPageIndex);
				}
				newNavigationPageInfo = logBookController.allNavigationPages[destinationPageIndex];
				newPage = logBookController.BuildEntriesPage(newNavigationPageInfo);
				containerSize = logBookController.entryPageContainer.rect.size;
				moveDirection = new Vector2(Mathf.Sign(newCategoryIndex - oldCategoryIndex), 0f);
			}
			SetPagePositions(0f);
		}

		public override void OnExit()
		{
			EntityState.Destroy(oldPage);
			if ((bool)logBookController)
			{
				logBookController.currentEntriesPageObject = newPage;
				logBookController.currentPageIndex = newNavigationPageInfo.indexInCategory;
				logBookController.desiredPageIndex = newNavigationPageInfo.indexInCategory;
				logBookController.currentCategoryIndex = newCategoryIndex;
				logBookController.availableNavigationPages = newNavigationPages;
				logBookController.currentCategoryLabel.token = categories[newCategoryIndex].nameToken;
				logBookController.categoryHightlightRect.SetParent(logBookController.navigationCategoryButtonAllocator.elements[newCategoryIndex].transform, worldPositionStays: false);
				logBookController.categoryHightlightRect.gameObject.SetActive(value: false);
				logBookController.categoryHightlightRect.gameObject.SetActive(value: true);
				if (logBookController.moveNavigationPageIndicatorContainerToCategoryButton)
				{
					logBookController.navigationPageIndicatorContainer.SetParent(logBookController.navigationCategoryButtonAllocator.elements[newCategoryIndex].transform, worldPositionStays: false);
				}
			}
			base.OnExit();
		}

		private void SetPagePositions(float t)
		{
			Vector2 vector = new Vector2(containerSize.x * (0f - moveDirection.x), containerSize.y * moveDirection.y);
			Vector2 vector2 = vector * t;
			if ((bool)oldPage)
			{
				oldPage.transform.localPosition = vector2;
			}
			if ((bool)newPage)
			{
				newPage.transform.localPosition = vector2 - vector;
				if (frame == 4)
				{
					newPage.GetComponent<GridLayoutGroup>().enabled = false;
				}
			}
		}

		public override void Update()
		{
			base.Update();
			frame++;
			float num = Mathf.Clamp01(unscaledAge / duration);
			SetPagePositions(num);
			if (num == 1f)
			{
				outer.SetNextState(new Idle());
			}
		}
	}

	private class EnterLogViewState : LogBookState
	{
		public Texture iconTexture;

		public RectTransform startRectTransform;

		public RectTransform endRectTransform;

		public Entry entry;

		private GameObject flyingIcon;

		private RectTransform flyingIconTransform;

		private RawImage flyingIconImage;

		private float duration = 0.75f;

		private Rect startRect;

		private Rect midRect;

		private Rect endRect;

		private bool submittedViewEntry;

		public override void OnEnter()
		{
			base.OnEnter();
			flyingIcon = new GameObject("FlyingIcon", typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage));
			flyingIconTransform = (RectTransform)flyingIcon.transform;
			flyingIconTransform.SetParent(logBookController.transform, worldPositionStays: false);
			flyingIconTransform.localScale = Vector3.one;
			flyingIconImage = flyingIconTransform.GetComponent<RawImage>();
			flyingIconImage.texture = iconTexture;
			Vector3[] array = new Vector3[4];
			startRectTransform.GetWorldCorners(array);
			startRect = GetRectRelativeToParent(array);
			midRect = new Rect(((RectTransform)logBookController.transform).rect.center, startRect.size);
			endRectTransform.GetWorldCorners(array);
			endRect = GetRectRelativeToParent(array);
			SetIconRect(startRect);
		}

		private void SetIconRect(Rect rect)
		{
			flyingIconTransform.position = rect.position;
			flyingIconTransform.offsetMin = rect.min;
			flyingIconTransform.offsetMax = rect.max;
		}

		private Rect GetRectRelativeToParent(Vector3[] corners)
		{
			for (int i = 0; i < 4; i++)
			{
				corners[i] = logBookController.transform.InverseTransformPoint(corners[i]);
			}
			Rect result = default(Rect);
			result.xMin = corners[0].x;
			result.xMax = corners[2].x;
			result.yMin = corners[0].y;
			result.yMax = corners[2].y;
			return result;
		}

		private static Rect RectFromWorldCorners(Vector3[] corners)
		{
			Rect result = default(Rect);
			result.xMin = corners[0].x;
			result.xMax = corners[2].x;
			result.yMin = corners[0].y;
			result.yMax = corners[2].y;
			return result;
		}

		private static Rect LerpRect(Rect a, Rect b, float t)
		{
			Rect result = default(Rect);
			result.min = Vector2.LerpUnclamped(a.min, b.min, t);
			result.max = Vector2.LerpUnclamped(a.max, b.max, t);
			return result;
		}

		public override void OnExit()
		{
			EntityState.Destroy(flyingIcon);
			base.OnExit();
		}

		public override void Update()
		{
			base.Update();
			float num = Mathf.Min(unscaledAge / duration, 1f);
			if (num < 0.1f)
			{
				Util.Remap(num, 0f, 0.1f, 0f, 1f);
				SetIconRect(startRect);
			}
			if (num < 0.2f)
			{
				float t = Util.Remap(num, 0.1f, 0.2f, 0f, 1f);
				SetIconRect(LerpRect(startRect, midRect, t));
			}
			else if (num < 0.4f)
			{
				Util.Remap(num, 0.2f, 0.4f, 0f, 1f);
				SetIconRect(midRect);
			}
			else if (num < 0.6f)
			{
				float t2 = Util.Remap(num, 0.4f, 0.6f, 0f, 1f);
				SetIconRect(LerpRect(midRect, endRect, t2));
			}
			else if (num < 1f)
			{
				float num2 = Util.Remap(num, 0.6f, 1f, 0f, 1f);
				flyingIconImage.color = new Color(1f, 1f, 1f, 1f - num2);
				SetIconRect(endRect);
				if (!submittedViewEntry)
				{
					submittedViewEntry = true;
					logBookController.ViewEntry(entry);
				}
			}
			else
			{
				outer.SetNextState(new Idle());
			}
		}
	}

	[Header("Navigation")]
	public GameObject navigationPanel;

	public RectTransform categoryContainer;

	public GameObject categorySpaceFiller;

	public int categorySpaceFillerCount;

	public Color spaceFillerColor;

	private UIElementAllocator<MPButton> navigationCategoryButtonAllocator;

	public RectTransform entryPageContainer;

	public GameObject entryPagePrefab;

	public RectTransform navigationPageIndicatorContainer;

	public GameObject navigationPageIndicatorPrefab;

	public bool moveNavigationPageIndicatorContainerToCategoryButton;

	private UIElementAllocator<MPButton> navigationPageIndicatorAllocator;

	public MPButton previousPageButton;

	public MPButton nextPageButton;

	public LanguageTextMeshController currentCategoryLabel;

	private RectTransform categoryHightlightRect;

	[Header("PageViewer")]
	public UnityEvent OnViewEntry;

	public GameObject pageViewerPanel;

	public MPButton pageViewerBackButton;

	[Header("Misc")]
	public GameObject categoryButtonPrefab;

	public GameObject headerHighlightPrefab;

	public LanguageTextMeshController hoverLanguageTextMeshController;

	public string hoverDescriptionFormatString;

	private EntityStateMachine stateMachine;

	private UILayerKey uiLayerKey;

	public static CategoryDef[] categories;

	public static ResourceAvailability availability;

	private static bool IsInitialized;

	public static bool IsStaticDataReady;

	private NavigationPageInfo[] _availableNavigationPages = Array.Empty<NavigationPageInfo>();

	private GameObject currentEntriesPageObject;

	private int currentCategoryIndex;

	private int desiredCategoryIndex;

	private int currentPageIndex;

	private int desiredPageIndex;

	private bool goToEndOfNextCategory;

	private NavigationPageInfo[] allNavigationPages;

	private NavigationPageInfo[][] navigationPagesByCategory;

	private static List<EntitlementDef> lastBuiltEntitlements;

	private NavigationPageInfo[] availableNavigationPages
	{
		get
		{
			return _availableNavigationPages;
		}
		set
		{
			int num = _availableNavigationPages.Length;
			_availableNavigationPages = value;
			if (num != availableNavigationPages.Length)
			{
				navigationPageIndicatorAllocator.AllocateElements(availableNavigationPages.Length);
			}
		}
	}

	public static event Action OnViewablesRegistered;

	private void Awake()
	{
		EntitlementManager.UpdateLocalUsersEntitlements();
		navigationCategoryButtonAllocator = new UIElementAllocator<MPButton>(categoryContainer, categoryButtonPrefab);
		navigationPageIndicatorAllocator = new UIElementAllocator<MPButton>(navigationPageIndicatorContainer, navigationPageIndicatorPrefab);
		navigationPageIndicatorAllocator.onCreateElement = delegate(int index, MPButton button)
		{
			button.onClick.AddListener(delegate
			{
				desiredPageIndex = index;
			});
		};
		previousPageButton.onClick.AddListener(OnLeftButton);
		nextPageButton.onClick.AddListener(OnRightButton);
		pageViewerBackButton.onClick.AddListener(ReturnToNavigation);
		stateMachine = base.gameObject.AddComponent<EntityStateMachine>();
		uiLayerKey = base.gameObject.GetComponent<UILayerKey>();
		stateMachine.initialStateType = default(SerializableEntityStateType);
		stateMachine.AllowStartWithoutNetworker = true;
		stateMachine.ShouldStateTransitionOnUpdate = true;
		categoryHightlightRect = (RectTransform)UnityEngine.Object.Instantiate(headerHighlightPrefab, base.transform.parent).transform;
		categoryHightlightRect.gameObject.SetActive(value: false);
	}

	private void Start()
	{
		GeneratePages(LocalUserManager.GetFirstLocalUser()?.userProfile);
		BuildCategoriesButtons();
		stateMachine.SetNextState(new ChangeCategoryState
		{
			newCategoryIndex = 0
		});
	}

	private void BuildCategoriesButtons()
	{
		navigationCategoryButtonAllocator.AllocateElements(categories.Length);
		ReadOnlyCollection<MPButton> elements = navigationCategoryButtonAllocator.elements;
		for (int i = 0; i < categories.Length; i++)
		{
			int categoryIndex = i;
			MPButton mPButton = elements[i];
			CategoryDef categoryDef = categories[i];
			mPButton.GetComponentInChildren<TextMeshProUGUI>().text = Language.GetString(categoryDef.nameToken);
			mPButton.onClick.RemoveAllListeners();
			mPButton.onClick.AddListener(delegate
			{
				OnCategoryClicked(categoryIndex);
			});
			mPButton.requiredTopLayer = uiLayerKey;
			ViewableTag viewableTag = mPButton.gameObject.GetComponent<ViewableTag>();
			if (!viewableTag)
			{
				viewableTag = mPButton.gameObject.AddComponent<ViewableTag>();
			}
			viewableTag.viewableName = categoryDef.viewableNode.fullName;
		}
		if ((bool)categorySpaceFiller)
		{
			for (int j = 0; j < categorySpaceFillerCount; j++)
			{
				UnityEngine.Object.Instantiate(categorySpaceFiller, categoryContainer).gameObject.SetActive(value: true);
			}
		}
	}

	public static IEnumerator Init()
	{
		if (LocalUserManager.isAnyUserSignedIn)
		{
			BuildStaticData();
		}
		yield return null;
		LocalUserManager.onUserSignIn += OnUserSignIn;
		BaseUserEntitlementTracker<LocalUser>.OnUserEntitlementsUpdated = (Action)Delegate.Combine(BaseUserEntitlementTracker<LocalUser>.OnUserEntitlementsUpdated, new Action(BuildStaticData));
		IsInitialized = true;
	}

	private static void OnUserSignIn(LocalUser obj)
	{
		BuildStaticData();
	}

	private static EntryStatus GetPickupStatus(in Entry entry, UserProfile viewerProfile)
	{
		UnlockableDef unlockableDef = null;
		PickupIndex pickupIndex = (PickupIndex)entry.extraData;
		PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex);
		ItemIndex itemIndex = pickupDef?.itemIndex ?? ItemIndex.None;
		EquipmentIndex equipmentIndex = pickupDef?.equipmentIndex ?? EquipmentIndex.None;
		if (itemIndex != ItemIndex.None)
		{
			unlockableDef = ItemCatalog.GetItemDef(itemIndex).unlockableDef;
		}
		else
		{
			if (equipmentIndex == EquipmentIndex.None)
			{
				return EntryStatus.Unimplemented;
			}
			unlockableDef = EquipmentCatalog.GetEquipmentDef(equipmentIndex).unlockableDef;
		}
		if (!viewerProfile.HasUnlockable(unlockableDef))
		{
			return EntryStatus.Locked;
		}
		if (!viewerProfile.HasDiscoveredPickup(pickupIndex))
		{
			return EntryStatus.Unencountered;
		}
		return EntryStatus.Available;
	}

	private static TooltipContent GetPickupTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status)
	{
		UnlockableDef unlockableDef = PickupCatalog.GetPickupDef((PickupIndex)entry.extraData).unlockableDef;
		TooltipContent result = default(TooltipContent);
		if (status >= EntryStatus.Available)
		{
			result.overrideTitleText = entry.GetDisplayName(userProfile);
			result.titleColor = entry.color;
			if (unlockableDef != null)
			{
				result.overrideBodyText = unlockableDef.getUnlockedString();
			}
			result.bodyToken = "";
			result.bodyColor = ColorCatalog.GetColor(ColorCatalog.ColorIndex.Unlockable);
		}
		else
		{
			result.titleToken = "UNIDENTIFIED";
			result.titleColor = Color.gray;
			result.bodyToken = "";
			switch (status)
			{
			case EntryStatus.Unimplemented:
				result.titleToken = "TOOLTIP_WIP_CONTENT_NAME";
				result.bodyToken = "TOOLTIP_WIP_CONTENT_DESCRIPTION";
				break;
			case EntryStatus.Unencountered:
				result.overrideBodyText = Language.GetString("LOGBOOK_UNLOCK_ITEM_LOG");
				break;
			case EntryStatus.Locked:
				result.overrideBodyText = unlockableDef.getHowToUnlockString();
				break;
			}
			result.bodyColor = Color.white;
		}
		return result;
	}

	private static TooltipContent GetMonsterTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status)
	{
		TooltipContent result = default(TooltipContent);
		result.titleColor = entry.color;
		if (status >= EntryStatus.Available)
		{
			result.overrideTitleText = entry.GetDisplayName(userProfile);
			result.titleColor = entry.color;
			result.bodyToken = "";
		}
		else
		{
			result.titleToken = "UNIDENTIFIED";
			result.titleColor = Color.gray;
			result.bodyToken = "LOGBOOK_UNLOCK_ITEM_MONSTER";
		}
		return result;
	}

	private static TooltipContent GetStageTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status)
	{
		TooltipContent result = default(TooltipContent);
		result.titleColor = entry.color;
		if (status >= EntryStatus.Available)
		{
			result.overrideTitleText = entry.GetDisplayName(userProfile);
			result.titleColor = entry.color;
			result.bodyToken = "";
		}
		else
		{
			result.titleToken = "UNIDENTIFIED";
			result.titleColor = Color.gray;
			result.bodyToken = "LOGBOOK_UNLOCK_ITEM_STAGE";
		}
		return result;
	}

	private static TooltipContent GetSurvivorTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status)
	{
		TooltipContent result = default(TooltipContent);
		UnlockableDef unlockableDef = SurvivorCatalog.FindSurvivorDefFromBody(((CharacterBody)entry.extraData).gameObject).unlockableDef;
		if (status >= EntryStatus.Available)
		{
			result.overrideTitleText = entry.GetDisplayName(userProfile);
			result.titleColor = entry.color;
			result.bodyToken = "";
		}
		else
		{
			result.titleToken = "UNIDENTIFIED";
			result.bodyToken = "";
			result.titleColor = Color.gray;
			switch (status)
			{
			case EntryStatus.Unencountered:
				result.overrideBodyText = Language.GetString("LOGBOOK_UNLOCK_ITEM_SURVIVOR");
				break;
			case EntryStatus.Locked:
				result.overrideBodyText = unlockableDef.getHowToUnlockString();
				break;
			}
		}
		return result;
	}

	private static TooltipContent GetAchievementTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status)
	{
		TooltipContent result = default(TooltipContent);
		UnlockableDef unlockableDef = UnlockableCatalog.GetUnlockableDef(((AchievementDef)entry.extraData).unlockableRewardIdentifier);
		result.titleColor = entry.color;
		result.bodyToken = "";
		if (unlockableDef == null)
		{
			result.overrideTitleText = entry.GetDisplayName(userProfile);
			result.titleColor = Color.gray;
			result.overrideBodyText = "ACHIEVEMENT HAS NO UNLOCKABLE DEFINED";
			result.bodyColor = Color.white;
			return result;
		}
		if (status >= EntryStatus.Available)
		{
			result.titleToken = entry.GetDisplayName(userProfile);
			result.titleColor = entry.color;
			result.overrideBodyText = unlockableDef.getUnlockedString();
		}
		else
		{
			result.titleToken = "UNIDENTIFIED";
			result.titleColor = Color.gray;
			if (status == EntryStatus.Locked)
			{
				result.overrideBodyText = Language.GetString("UNIDENTIFIED_DESCRIPTION");
			}
			else
			{
				result.overrideBodyText = unlockableDef.getHowToUnlockString();
			}
		}
		return result;
	}

	private static TooltipContent GetWIPTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status)
	{
		TooltipContent result = default(TooltipContent);
		result.titleColor = ColorCatalog.GetColor(ColorCatalog.ColorIndex.WIP);
		result.titleToken = "TOOLTIP_WIP_CONTENT_NAME";
		result.bodyToken = "TOOLTIP_WIP_CONTENT_DESCRIPTION";
		return result;
	}

	private static EntryStatus GetAlwaysAvailable(UserProfile userProfile, Entry entry)
	{
		return EntryStatus.Available;
	}

	private static EntryStatus GetUnimplemented(in Entry entry, UserProfile viewerProfile)
	{
		return EntryStatus.Unimplemented;
	}

	private static EntryStatus GetStageStatus(in Entry entry, UserProfile viewerProfile)
	{
		UnlockableDef unlockableLogFromBaseSceneName = SceneCatalog.GetUnlockableLogFromBaseSceneName((entry.extraData as SceneDef).baseSceneName);
		if (unlockableLogFromBaseSceneName != null && viewerProfile.HasUnlockable(unlockableLogFromBaseSceneName))
		{
			return EntryStatus.Available;
		}
		return EntryStatus.Unencountered;
	}

	private static EntryStatus GetMonsterStatus(in Entry entry, UserProfile viewerProfile)
	{
		CharacterBody characterBody = (CharacterBody)entry.extraData;
		UnlockableDef unlockableDef = characterBody.GetComponent<DeathRewards>()?.logUnlockableDef;
		if (!unlockableDef)
		{
			return EntryStatus.None;
		}
		if (viewerProfile.HasUnlockable(unlockableDef))
		{
			return EntryStatus.Available;
		}
		if (viewerProfile.statSheet.GetStatValueULong(PerBodyStatDef.killsAgainst, characterBody.gameObject.name) != 0)
		{
			return EntryStatus.Unencountered;
		}
		return EntryStatus.Locked;
	}

	private static EntryStatus GetSurvivorStatus(in Entry entry, UserProfile viewerProfile)
	{
		CharacterBody characterBody = (CharacterBody)entry.extraData;
		SurvivorDef survivorDef = SurvivorCatalog.FindSurvivorDefFromBody(characterBody.gameObject);
		if (!viewerProfile.HasUnlockable(survivorDef.unlockableDef))
		{
			return EntryStatus.Locked;
		}
		if (viewerProfile.statSheet.GetStatValueULong(PerBodyStatDef.totalWins, characterBody.gameObject.name) == 0L)
		{
			return EntryStatus.Unencountered;
		}
		return EntryStatus.Available;
	}

	private static EntryStatus GetAchievementStatus(in Entry entry, UserProfile viewerProfile)
	{
		string identifier = ((AchievementDef)entry.extraData).identifier;
		bool flag = viewerProfile.HasAchievement(identifier);
		if (!viewerProfile.CanSeeAchievement(identifier))
		{
			return EntryStatus.Locked;
		}
		if (!flag)
		{
			return EntryStatus.Unencountered;
		}
		return EntryStatus.Available;
	}

	private static void BuildStaticData()
	{
		RoR2Application.instance.StartCoroutine(WaitForBuildingStaticData());
	}

	static LogBookController()
	{
		categories = Array.Empty<CategoryDef>();
		availability = default(ResourceAvailability);
		IsInitialized = false;
		IsStaticDataReady = false;
		lastBuiltEntitlements = null;
	}

	private NavigationPageInfo[] GetCategoryPages(int categoryIndex)
	{
		if (navigationPagesByCategory.GetLength(0) <= categoryIndex)
		{
			return new NavigationPageInfo[0];
		}
		return navigationPagesByCategory[categoryIndex];
	}

	public void OnLeftButton()
	{
		desiredPageIndex--;
	}

	public void OnRightButton()
	{
		desiredPageIndex++;
	}

	private void OnCategoryClicked(int categoryIndex)
	{
		desiredCategoryIndex = categoryIndex;
		goToEndOfNextCategory = false;
	}

	public void OnCategoryLeftButton()
	{
		desiredCategoryIndex--;
		goToEndOfNextCategory = false;
	}

	public void OnCategoryRightButton()
	{
		desiredCategoryIndex++;
		goToEndOfNextCategory = false;
	}

	private void GeneratePages(UserProfile viewerProfile)
	{
		if (!IsInitialized)
		{
			Init();
		}
		navigationPagesByCategory = new NavigationPageInfo[categories.Length][];
		IEnumerable<NavigationPageInfo> enumerable = Array.Empty<NavigationPageInfo>();
		int num = 0;
		for (int i = 0; i < categories.Length; i++)
		{
			CategoryDef categoryDef = categories[i];
			Entry[] array = categoryDef.BuildEntries(viewerProfile);
			bool fullWidth = categoryDef.fullWidth;
			Vector2 size = entryPageContainer.rect.size;
			if (fullWidth)
			{
				categoryDef.iconSize.x = size.x;
			}
			int num2 = Mathf.FloorToInt(Mathf.Max(size.x / categoryDef.iconSize.x, 1f));
			int num3 = Mathf.FloorToInt(Mathf.Max(size.y / categoryDef.iconSize.y, 1f));
			int num4 = num2 * num3;
			int num5 = Mathf.CeilToInt((float)array.Length / (float)num4);
			if (num5 <= 0)
			{
				num5 = 1;
			}
			NavigationPageInfo[] array2 = new NavigationPageInfo[num5];
			for (int j = 0; j < num5; j++)
			{
				int num6 = j * num4;
				int num7 = array.Length - num6;
				int num8 = num4;
				if (num8 > num7)
				{
					num8 = num7;
				}
				Entry[] array3 = new Entry[num4];
				Array.Copy(array, num6, array3, 0, num8);
				NavigationPageInfo navigationPageInfo = new NavigationPageInfo();
				navigationPageInfo.categoryDef = categoryDef;
				navigationPageInfo.entries = array3;
				navigationPageInfo.index = num++;
				navigationPageInfo.indexInCategory = j;
				array2[j] = navigationPageInfo;
			}
			navigationPagesByCategory[i] = array2;
			enumerable = enumerable.Concat(array2);
		}
		allNavigationPages = enumerable.ToArray();
	}

	private void Update()
	{
		if (desiredPageIndex > availableNavigationPages.Length - 1)
		{
			desiredPageIndex = availableNavigationPages.Length - 1;
			desiredCategoryIndex++;
			goToEndOfNextCategory = false;
		}
		if (desiredPageIndex < 0)
		{
			desiredCategoryIndex--;
			desiredPageIndex = 0;
			goToEndOfNextCategory = true;
		}
		if (desiredCategoryIndex > categories.Length - 1)
		{
			desiredCategoryIndex = 0;
			goToEndOfNextCategory = false;
		}
		if (desiredCategoryIndex < 0)
		{
			desiredCategoryIndex = categories.Length - 1;
			goToEndOfNextCategory = true;
		}
		foreach (MPButton element in navigationPageIndicatorAllocator.elements)
		{
			ColorBlock colors = element.colors;
			colors.colorMultiplier = 1f;
			element.colors = colors;
		}
		if (currentPageIndex < navigationPageIndicatorAllocator.elements.Count)
		{
			MPButton mPButton = navigationPageIndicatorAllocator.elements[currentPageIndex];
			ColorBlock colors2 = mPButton.colors;
			colors2.colorMultiplier = 2f;
			mPButton.colors = colors2;
		}
		if (desiredCategoryIndex != currentCategoryIndex)
		{
			if (stateMachine.state is Idle)
			{
				int num = ((desiredCategoryIndex > currentCategoryIndex) ? 1 : (-1));
				stateMachine.SetNextState(new ChangeCategoryState
				{
					newCategoryIndex = currentCategoryIndex + num,
					goToLastPage = goToEndOfNextCategory
				});
			}
		}
		else if (desiredPageIndex != currentPageIndex && stateMachine.state is Idle)
		{
			int num2 = ((desiredPageIndex > currentPageIndex) ? 1 : (-1));
			stateMachine.SetNextState(new ChangeEntriesPageState
			{
				newNavigationPageInfo = GetCategoryPages(currentCategoryIndex)[currentPageIndex + num2],
				moveDirection = new Vector2(num2, 0f)
			});
		}
	}

	private UserProfile LookUpUserProfile()
	{
		return LocalUserManager.readOnlyLocalUsersList.FirstOrDefault((LocalUser v) => v != null)?.userProfile;
	}

	private GameObject BuildEntriesPage(NavigationPageInfo navigationPageInfo)
	{
		Entry[] entries = navigationPageInfo.entries;
		CategoryDef categoryDef = navigationPageInfo.categoryDef;
		GameObject gameObject = UnityEngine.Object.Instantiate(entryPagePrefab, entryPageContainer);
		GridLayoutGroup component = gameObject.GetComponent<GridLayoutGroup>();
		component.cellSize = categoryDef.iconSize;
		component.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
		float num = entryPageContainer.rect.width - (float)component.padding.left - (float)component.padding.right;
		component.constraintCount = (int)(num / (component.cellSize.x + component.spacing.x));
		UIElementAllocator<RectTransform> uIElementAllocator = new UIElementAllocator<RectTransform>((RectTransform)gameObject.transform, categoryDef.iconPrefab);
		uIElementAllocator.AllocateElements(entries.Length);
		UserProfile userProfile = LookUpUserProfile();
		ReadOnlyCollection<RectTransform> elements = uIElementAllocator.elements;
		for (int i = 0; i < elements.Count; i++)
		{
			RectTransform rectTransform = elements[i];
			HGButton component2 = rectTransform.GetComponent<HGButton>();
			Entry entry = ((i < entries.Length) ? entries[i] : null);
			EntryStatus entryStatus = entry?.GetStatus(userProfile) ?? EntryStatus.None;
			if (entryStatus != 0)
			{
				TooltipContent tooltipContent = entry.GetTooltipContent(userProfile, entryStatus);
				categoryDef.initializeElementGraphics?.Invoke(rectTransform.gameObject, entry, entryStatus, userProfile);
				if ((bool)component2)
				{
					UnityEngine.UI.Navigation navigation = component2.navigation;
					navigation.mode = UnityEngine.UI.Navigation.Mode.Explicit;
					int index = (i + elements.Count - 1) % elements.Count;
					navigation.selectOnLeft = elements[index].GetComponent<HGButton>();
					int index2 = (i + elements.Count + 1) % elements.Count;
					navigation.selectOnRight = elements[index2].GetComponent<HGButton>();
					if (i - component.constraintCount >= 0)
					{
						navigation.selectOnUp = elements[i - component.constraintCount].GetComponent<HGButton>();
					}
					if (i + component.constraintCount < elements.Count)
					{
						navigation.selectOnDown = elements[i + component.constraintCount].GetComponent<HGButton>();
					}
					component2.navigation = navigation;
					bool flag = entryStatus >= EntryStatus.Available;
					component2.interactable = true;
					component2.disableGamepadClick = component2.disableGamepadClick || !flag;
					component2.disablePointerClick = component2.disablePointerClick || !flag;
					component2.imageOnInteractable = (flag ? component2.imageOnInteractable : null);
					component2.requiredTopLayer = uiLayerKey;
					component2.updateTextOnHover = true;
					component2.hoverLanguageTextMeshController = hoverLanguageTextMeshController;
					string titleText = tooltipContent.GetTitleText();
					string bodyText = tooltipContent.GetBodyText();
					Color titleColor = tooltipContent.titleColor;
					titleColor.a = 0.2f;
					component2.hoverToken = Language.GetStringFormatted("LOGBOOK_HOVER_DESCRIPTION_FORMAT", titleText, bodyText, ColorUtility.ToHtmlStringRGBA(titleColor));
				}
				if (entry.viewableNode != null)
				{
					ViewableTag viewableTag = rectTransform.gameObject.GetComponent<ViewableTag>();
					if (!viewableTag)
					{
						viewableTag = rectTransform.gameObject.AddComponent<ViewableTag>();
						viewableTag.viewableVisualStyle = ViewableTag.ViewableVisualStyle.Icon;
					}
					viewableTag.viewableName = entry.viewableNode.fullName;
				}
			}
			if (entryStatus >= EntryStatus.Available && (bool)component2)
			{
				component2.onClick.AddListener(delegate
				{
					ViewEntry(entry);
				});
			}
			if (entryStatus == EntryStatus.None)
			{
				if ((bool)component2)
				{
					component2.enabled = false;
					component2.targetGraphic.color = spaceFillerColor;
				}
				else
				{
					rectTransform.GetComponent<Image>().color = spaceFillerColor;
				}
				for (int num2 = rectTransform.childCount - 1; num2 >= 0; num2--)
				{
					UnityEngine.Object.Destroy(rectTransform.GetChild(num2).gameObject);
				}
			}
			if ((bool)component2 && i == 0)
			{
				component2.defaultFallbackButton = true;
			}
		}
		gameObject.gameObject.SetActive(value: true);
		GridLayoutGroup gridLayoutGroup = gameObject.GetComponent<GridLayoutGroup>();
		Action destroyLayoutGroup = null;
		int frameTimer = 2;
		destroyLayoutGroup = delegate
		{
			int num3 = frameTimer - 1;
			frameTimer = num3;
			if (frameTimer <= 0)
			{
				gridLayoutGroup.enabled = false;
				RoR2Application.onLateUpdate -= destroyLayoutGroup;
			}
		};
		RoR2Application.onLateUpdate += destroyLayoutGroup;
		return gameObject;
	}

	private void ViewEntry(Entry entry)
	{
		OnViewEntry.Invoke();
		LogBookPage component = pageViewerPanel.GetComponent<LogBookPage>();
		component.SetEntry(LookUpUserProfile(), entry);
		component.modelPanel.SetAnglesForCharacterThumbnailForSeconds(0.5f);
		ViewableTrigger.TriggerView(entry.viewableNode?.fullName);
	}

	private void ReturnToNavigation()
	{
		navigationPanel.SetActive(value: true);
		pageViewerPanel.SetActive(value: false);
	}

	private static bool UnlockableExists(UnlockableDef unlockableDef)
	{
		return unlockableDef;
	}

	private static bool IsEntryBodyWithoutLore(in Entry entry)
	{
		CharacterBody obj = (CharacterBody)entry.extraData;
		bool flag = false;
		string text = "";
		string baseNameToken = obj.baseNameToken;
		if (!string.IsNullOrEmpty(baseNameToken))
		{
			text = baseNameToken.Replace("_NAME", "_LORE");
			if (Language.english.TokenIsRegistered(text))
			{
				flag = true;
			}
		}
		return !flag;
	}

	private static bool IsEntryPickupItemWithoutLore(in Entry entry)
	{
		ItemDef itemDef = ItemCatalog.GetItemDef(PickupCatalog.GetPickupDef((PickupIndex)entry.extraData).itemIndex);
		return !Language.english.TokenIsRegistered(itemDef.loreToken);
	}

	private static bool IsEntryPickupEquipmentWithoutLore(in Entry entry)
	{
		EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(PickupCatalog.GetPickupDef((PickupIndex)entry.extraData).equipmentIndex);
		return !Language.english.TokenIsRegistered(equipmentDef.loreToken);
	}

	private static bool CanSelectItemEntry(ItemDef itemDef, Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		if (itemDef != null)
		{
			ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(itemDef.tier);
			if ((bool)itemTierDef && itemTierDef.isDroppable)
			{
				if (!(itemDef.requiredExpansion == null) && expansionAvailability.ContainsKey(itemDef.requiredExpansion))
				{
					return expansionAvailability[itemDef.requiredExpansion];
				}
				return true;
			}
			return false;
		}
		return false;
	}

	private static bool CanSelectEquipmentEntry(EquipmentDef equipmentDef, Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		if (equipmentDef != null)
		{
			if (equipmentDef.canDrop)
			{
				if (!(equipmentDef.requiredExpansion == null) && expansionAvailability.ContainsKey(equipmentDef.requiredExpansion))
				{
					return expansionAvailability[equipmentDef.requiredExpansion];
				}
				return true;
			}
			return false;
		}
		return false;
	}

	private static Entry[] BuildPickupEntries(Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		new Entry
		{
			nameToken = "TOOLTIP_WIP_CONTENT_NAME",
			color = Color.white,
			iconTexture = LegacyResourcesAPI.Load<Texture>("Textures/MiscIcons/texWIPIcon"),
			getStatusImplementation = GetUnimplemented,
			getTooltipContentImplementation = GetWIPTooltipContent
		};
		IEnumerable<Entry> first = from pickupDef in PickupCatalog.allPickups
			select ItemCatalog.GetItemDef(pickupDef.itemIndex) into itemDef
			where CanSelectItemEntry(itemDef, expansionAvailability)
			orderby (int)(itemDef.tier + ((itemDef.tier == ItemTier.Lunar) ? 100 : 0))
			select new Entry
			{
				nameToken = itemDef.nameToken,
				color = ColorCatalog.GetColor(itemDef.darkColorIndex),
				iconTexture = itemDef.pickupIconTexture,
				bgTexture = itemDef.bgIconTexture,
				extraData = PickupCatalog.FindPickupIndex(itemDef.itemIndex),
				modelPrefab = itemDef.pickupModelPrefab,
				getStatusImplementation = GetPickupStatus,
				getTooltipContentImplementation = GetPickupTooltipContent,
				pageBuilderMethod = PageBuilder.SimplePickup,
				isWIPImplementation = IsEntryPickupItemWithoutLore
			};
		IEnumerable<Entry> second = from pickupDef in PickupCatalog.allPickups
			select EquipmentCatalog.GetEquipmentDef(pickupDef.equipmentIndex) into equipmentDef
			where CanSelectEquipmentEntry(equipmentDef, expansionAvailability)
			orderby !equipmentDef.isLunar
			select new Entry
			{
				nameToken = equipmentDef.nameToken,
				color = ColorCatalog.GetColor(equipmentDef.colorIndex),
				iconTexture = equipmentDef.pickupIconTexture,
				bgTexture = equipmentDef.bgIconTexture,
				extraData = PickupCatalog.FindPickupIndex(equipmentDef.equipmentIndex),
				modelPrefab = equipmentDef.pickupModelPrefab,
				getStatusImplementation = GetPickupStatus,
				getTooltipContentImplementation = GetPickupTooltipContent,
				pageBuilderMethod = PageBuilder.SimplePickup,
				isWIPImplementation = IsEntryPickupEquipmentWithoutLore
			};
		return first.Concat(second).ToArray();
	}

	private static bool CanSelectMonsterEntry(CharacterBody characterBody, Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		if ((bool)characterBody)
		{
			ExpansionRequirementComponent component = characterBody.GetComponent<ExpansionRequirementComponent>();
			if (!component || !component.requiredExpansion || !expansionAvailability.ContainsKey(component.requiredExpansion) || expansionAvailability[component.requiredExpansion])
			{
				return UnlockableExists(characterBody.GetComponent<DeathRewards>()?.logUnlockableDef);
			}
			return false;
		}
		return false;
	}

	private static Entry[] BuildMonsterEntries(Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		Entry[] array = (from characterBody in BodyCatalog.allBodyPrefabBodyBodyComponents
			where CanSelectMonsterEntry(characterBody, expansionAvailability)
			orderby characterBody.baseMaxHealth
			select new Entry
			{
				nameToken = characterBody.baseNameToken,
				color = ColorCatalog.GetColor(ColorCatalog.ColorIndex.HardDifficulty),
				iconTexture = characterBody.portraitIcon,
				extraData = characterBody,
				modelPrefab = characterBody.GetComponent<ModelLocator>()?.modelTransform?.gameObject,
				getStatusImplementation = GetMonsterStatus,
				getTooltipContentImplementation = GetMonsterTooltipContent,
				pageBuilderMethod = PageBuilder.MonsterBody,
				bgTexture = (characterBody.isChampion ? LegacyResourcesAPI.Load<Texture>("Textures/ItemIcons/BG/texTier3BGIcon") : LegacyResourcesAPI.Load<Texture>("Textures/ItemIcons/BG/texTier1BGIcon")),
				isWIPImplementation = IsEntryBodyWithoutLore
			}).ToArray();
		Dictionary<string, Entry> dictionary = new Dictionary<string, Entry>();
		Entry[] array2 = array;
		foreach (Entry entry in array2)
		{
			if (!dictionary.ContainsKey(entry.nameToken))
			{
				dictionary.Add(entry.nameToken, entry);
			}
		}
		return dictionary.Values.ToArray();
	}

	private static bool CanSelectStageEntry(SceneDef sceneDef, Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		if ((bool)sceneDef)
		{
			ExpansionDef requiredExpansion = sceneDef.requiredExpansion;
			if (!requiredExpansion || !expansionAvailability.ContainsKey(requiredExpansion) || expansionAvailability[requiredExpansion])
			{
				return sceneDef.shouldIncludeInLogbook;
			}
			return false;
		}
		return false;
	}

	private static Entry[] BuildStageEntries(Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		return (from sceneDef in SceneCatalog.allSceneDefs
			where CanSelectStageEntry(sceneDef, expansionAvailability)
			orderby sceneDef.stageOrder
			select new Entry
			{
				nameToken = sceneDef.nameToken,
				iconTexture = sceneDef.previewTexture,
				color = ColorCatalog.GetColor(ColorCatalog.ColorIndex.Tier2ItemDark),
				getStatusImplementation = GetStageStatus,
				modelPrefab = sceneDef.dioramaPrefab,
				getTooltipContentImplementation = GetStageTooltipContent,
				pageBuilderMethod = PageBuilder.Stage,
				extraData = sceneDef,
				isWIPImplementation = delegate(in Entry entry)
				{
					return !Language.english.TokenIsRegistered(((SceneDef)entry.extraData).loreToken);
				}
			}).ToArray();
	}

	private static bool CanSelectSurvivorBodyEntry(CharacterBody body, Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		if ((bool)body)
		{
			ExpansionRequirementComponent component = body.GetComponent<ExpansionRequirementComponent>();
			if ((bool)component && (bool)component.requiredExpansion && expansionAvailability.ContainsKey(component.requiredExpansion))
			{
				return expansionAvailability[component.requiredExpansion];
			}
			return true;
		}
		return false;
	}

	private static Entry[] BuildSurvivorEntries(Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		return (from survivorDef in SurvivorCatalog.orderedSurvivorDefs
			select BodyCatalog.GetBodyPrefabBodyComponent(SurvivorCatalog.GetBodyIndexFromSurvivorIndex(survivorDef.survivorIndex)) into body
			where CanSelectSurvivorBodyEntry(body, expansionAvailability)
			select body into characterBody
			select new Entry
			{
				nameToken = characterBody.baseNameToken,
				color = ColorCatalog.GetColor(ColorCatalog.ColorIndex.NormalDifficulty),
				iconTexture = characterBody.portraitIcon,
				bgTexture = LegacyResourcesAPI.Load<Texture>("Textures/ItemIcons/BG/texSurvivorBGIcon"),
				extraData = characterBody,
				modelPrefab = characterBody.GetComponent<ModelLocator>()?.modelTransform?.gameObject,
				getStatusImplementation = GetSurvivorStatus,
				getTooltipContentImplementation = GetSurvivorTooltipContent,
				pageBuilderMethod = PageBuilder.SurvivorBody,
				isWIPImplementation = IsEntryBodyWithoutLore
			}).ToArray();
	}

	private static bool CanSelectAchievementEntry(AchievementDef achievementDef, Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		if (achievementDef != null)
		{
			ExpansionDef expansionDefForUnlockable = UnlockableCatalog.GetExpansionDefForUnlockable(UnlockableCatalog.GetUnlockableDef(achievementDef.unlockableRewardIdentifier)?.index ?? UnlockableIndex.None);
			if ((bool)expansionDefForUnlockable && expansionAvailability.ContainsKey(expansionDefForUnlockable))
			{
				return expansionAvailability[expansionDefForUnlockable];
			}
			return true;
		}
		return false;
	}

	private static Entry[] BuildAchievementEntries(Dictionary<ExpansionDef, bool> expansionAvailability)
	{
		return (from achievementDef in AchievementManager.allAchievementDefs
			where CanSelectAchievementEntry(achievementDef, expansionAvailability)
			select new Entry
			{
				nameToken = achievementDef.nameToken,
				color = ColorCatalog.GetColor(ColorCatalog.ColorIndex.NormalDifficulty),
				iconTexture = achievementDef.GetAchievedIcon()?.texture,
				extraData = achievementDef,
				modelPrefab = null,
				getStatusImplementation = GetAchievementStatus,
				getTooltipContentImplementation = GetAchievementTooltipContent
			}).ToArray();
	}

	private static Entry[] BuildProfileEntries(UserProfile viewerProfile)
	{
		List<Entry> entries = new List<Entry>();
		if (true)
		{
			foreach (string availableProfileName in PlatformSystems.saveSystem.GetAvailableProfileNames())
			{
				AddProfileStatsEntry(PlatformSystems.saveSystem.GetProfile(availableProfileName));
			}
		}
		else if (viewerProfile != null)
		{
			AddProfileStatsEntry(viewerProfile);
		}
		return entries.ToArray();
		void AddProfileStatsEntry(UserProfile userProfile)
		{
			Entry item = new Entry
			{
				pageBuilderMethod = PageBuilder.StatsPanel,
				getStatusImplementation = delegate
				{
					return EntryStatus.Available;
				},
				extraData = userProfile,
				getDisplayNameImplementation = delegate(in Entry entry, UserProfile _viewerProfile)
				{
					return ((UserProfile)entry.extraData).name;
				},
				iconTexture = userProfile.portraitTexture
			};
			entries.Add(item);
		}
	}

	private static Entry[] BuildMorgueEntries(UserProfile viewerProfile)
	{
		List<Entry> entries = CollectionPool<Entry, List<Entry>>.RentCollection();
		List<RunReport> list = CollectionPool<RunReport, List<RunReport>>.RentCollection();
		MorgueManager.LoadHistoryRunReports(list);
		foreach (RunReport item2 in list)
		{
			AddRunReportEntry(item2);
		}
		CollectionPool<RunReport, List<RunReport>>.ReturnCollection(list);
		Entry[] result = entries.ToArray();
		CollectionPool<Entry, List<Entry>>.ReturnCollection(entries);
		return result;
		void AddRunReportEntry(RunReport runReport)
		{
			GetPrimaryPlayerInfo(runReport, out var _, out var primaryPlayerBody3);
			Entry item = new Entry
			{
				pageBuilderMethod = PageBuilder.RunReportPanel,
				getStatusImplementation = GetEntryStatus,
				extraData = runReport,
				getDisplayNameImplementation = GetDisplayName,
				getTooltipContentImplementation = GetTooltipContent,
				iconTexture = primaryPlayerBody3?.portraitIcon
			};
			entries.Add(item);
		}
		static string GetDisplayName(in Entry entry, UserProfile _viewerProfile)
		{
			return ((RunReport)entry.extraData).snapshotTimeUtc.ToLocalTime().ToString("G", CultureInfo.CurrentCulture);
		}
		static EntryStatus GetEntryStatus(in Entry entry, UserProfile _viewerProfile)
		{
			return EntryStatus.Available;
		}
		void GetPrimaryPlayerInfo(RunReport _runReport, out RunReport.PlayerInfo primaryPlayerInfo, out CharacterBody primaryPlayerBody)
		{
			primaryPlayerInfo = _runReport.FindPlayerInfo(viewerProfile) ?? _runReport.FindFirstPlayerInfo();
			primaryPlayerBody = BodyCatalog.GetBodyPrefabBodyComponent(primaryPlayerInfo?.bodyIndex ?? BodyIndex.None);
		}
		TooltipContent GetTooltipContent(in Entry entry, UserProfile _viewerProfile, EntryStatus entryStatus)
		{
			RunReport runReport2 = (RunReport)entry.extraData;
			GetPrimaryPlayerInfo(runReport2, out var _, out var primaryPlayerBody2);
			TooltipContent result2 = default(TooltipContent);
			result2.overrideTitleText = Language.GetStringFormatted("LOGBOOK_ENTRY_RUNREPORT_TOOLTIP_TITLE_FORMAT", runReport2.snapshotTimeUtc.ToLocalTime().ToString("G", CultureInfo.CurrentCulture));
			result2.overrideBodyText = Language.GetStringFormatted("LOGBOOK_ENTRY_RUNREPORT_TOOLTIP_BODY_FORMAT", Language.GetString(primaryPlayerBody2?.baseNameToken ?? string.Empty), Language.GetString(runReport2.gameMode?.nameToken ?? string.Empty), Language.GetString(runReport2.gameEnding?.endingTextToken ?? string.Empty));
			return result2;
		}
	}

	private static IEnumerator WaitForBuildingStaticData()
	{
		IsStaticDataReady = false;
		Dictionary<ExpansionDef, bool> expansionAvailability = new Dictionary<ExpansionDef, bool>();
		List<EntitlementDef> list = new List<EntitlementDef>();
		foreach (ExpansionDef expansionDef in ExpansionCatalog.expansionDefs)
		{
			expansionAvailability.Add(expansionDef, !expansionDef.requiredEntitlement || EntitlementManager.localUserEntitlementTracker.AnyUserHasEntitlement(expansionDef.requiredEntitlement));
			if (expansionAvailability[expansionDef])
			{
				list.Add(expansionDef.requiredEntitlement);
			}
		}
		if (lastBuiltEntitlements != null && lastBuiltEntitlements.SequenceEqual(list))
		{
			lastBuiltEntitlements = list;
			IsStaticDataReady = true;
			yield break;
		}
		lastBuiltEntitlements = list;
		yield return null;
		Entry[] pickupEntries = BuildPickupEntries(expansionAvailability);
		yield return null;
		Entry[] monsterEntries = BuildMonsterEntries(expansionAvailability);
		yield return null;
		Entry[] stageEntries = BuildStageEntries(expansionAvailability);
		yield return null;
		Entry[] survivorEntries = BuildSurvivorEntries(expansionAvailability);
		yield return null;
		Entry[] achievementEntries = BuildAchievementEntries(expansionAvailability);
		yield return null;
		categories = new CategoryDef[7]
		{
			new CategoryDef
			{
				nameToken = "LOGBOOK_CATEGORY_ITEMANDEQUIPMENT",
				iconPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/UI/Logbook/ItemEntryIcon"),
				buildEntries = (UserProfile viewerProfile) => pickupEntries,
				navButtons = new CategoryDef.NavButtons
				{
					navigate = "LOGBOOK_ZOOMINOUT"
				}
			},
			new CategoryDef
			{
				nameToken = "LOGBOOK_CATEGORY_MONSTER",
				iconPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/UI/Logbook/MonsterEntryIcon"),
				buildEntries = (UserProfile viewerProfile) => monsterEntries,
				navButtons = new CategoryDef.NavButtons
				{
					navigate = "LOGBOOK_ZOOMINOUT"
				}
			},
			new CategoryDef
			{
				nameToken = "LOGBOOK_CATEGORY_STAGE",
				iconPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/UI/Logbook/StageEntryIcon"),
				buildEntries = (UserProfile viewerProfile) => stageEntries
			},
			new CategoryDef
			{
				nameToken = "LOGBOOK_CATEGORY_SURVIVOR",
				iconPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/UI/Logbook/SurvivorEntryIcon"),
				buildEntries = (UserProfile viewerProfile) => survivorEntries
			},
			new CategoryDef
			{
				nameToken = "LOGBOOK_CATEGORY_ACHIEVEMENTS",
				iconPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/UI/Logbook/AchievementEntryIcon"),
				buildEntries = (UserProfile viewerProfile) => achievementEntries,
				initializeElementGraphics = CategoryDef.InitializeChallenge
			},
			new CategoryDef
			{
				nameToken = "LOGBOOK_CATEGORY_STATS",
				iconPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/UI/Logbook/StatsEntryIcon"),
				buildEntries = BuildProfileEntries,
				initializeElementGraphics = CategoryDef.InitializeStats,
				navButtons = new CategoryDef.NavButtons
				{
					scroll = "",
					rotate = "LOGBOOK_PAN"
				}
			},
			new CategoryDef
			{
				nameToken = "LOGBOOK_CATEGORY_MORGUE",
				iconPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/UI/Logbook/MorgueEntryIcon"),
				buildEntries = BuildMorgueEntries,
				initializeElementGraphics = CategoryDef.InitializeMorgue,
				navButtons = new CategoryDef.NavButtons
				{
					rotate = "",
					navigate = ""
				}
			}
		};
		RegisterViewables(categories);
		availability.MakeAvailable();
		IsStaticDataReady = true;
		EntitlementManager.UpdateLocalUsersEntitlements();
	}

	[ConCommand(commandName = "logbook_list_unfinished_lore", flags = ConVarFlags.None, helpText = "Prints all logbook entries which still have undefined lore.")]
	private static void CCLogbookListUnfinishedLore(ConCommandArgs args)
	{
		List<string> list = new List<string>();
		CategoryDef[] array = categories;
		for (int i = 0; i < array.Length; i++)
		{
			Entry[] array2 = array[i].BuildEntries(args.GetSenderLocalUser()?.userProfile);
			foreach (Entry entry in array2)
			{
				string text = "";
				if (entry.extraData is UnityEngine.Object @object)
				{
					text = @object.name;
				}
				if (entry.isWip)
				{
					list.Add(entry.extraData?.GetType()?.Name + " \"" + text + "\" \"" + Language.GetString(entry.nameToken) + "\"");
				}
			}
		}
		args.Log(string.Join("\n", list));
	}

	private static void RegisterViewables(CategoryDef[] categoriesToGenerateFrom)
	{
		ViewablesCatalog.Node node = new ViewablesCatalog.Node("Logbook", isFolder: true);
		CategoryDef[] array = categories;
		foreach (CategoryDef obj in array)
		{
			ViewablesCatalog.Node parent = (obj.viewableNode = new ViewablesCatalog.Node(obj.nameToken, isFolder: true, node));
			Entry[] array2 = obj.BuildEntries(null);
			foreach (Entry entry in array2)
			{
				string nameToken = entry.nameToken;
				ViewablesCatalog.Node entryNode;
				AchievementDef achievementDef;
				bool hasPrereq;
				if (!entry.isWip && !(nameToken == "TOOLTIP_WIP_CONTENT_NAME") && !string.IsNullOrEmpty(nameToken))
				{
					entryNode = new ViewablesCatalog.Node(nameToken, isFolder: false, parent);
					object extraData = entry.extraData;
					achievementDef = extraData as AchievementDef;
					if (achievementDef != null)
					{
						hasPrereq = !string.IsNullOrEmpty(achievementDef.prerequisiteAchievementIdentifier);
						entryNode.shouldShowUnviewed = Check;
					}
					else
					{
						entryNode.shouldShowUnviewed = Check;
					}
					entry.viewableNode = entryNode;
				}
				bool Check(UserProfile userProfile)
				{
					if (userProfile.HasViewedViewable(entryNode.fullName))
					{
						return false;
					}
					if (userProfile.HasAchievement(achievementDef.identifier))
					{
						return false;
					}
					if (hasPrereq)
					{
						return userProfile.HasAchievement(achievementDef.prerequisiteAchievementIdentifier);
					}
					return true;
				}
				bool Check(UserProfile viewerProfile)
				{
					if (viewerProfile.HasViewedViewable(entryNode.fullName))
					{
						return false;
					}
					return entry.GetStatus(viewerProfile) == EntryStatus.Available;
				}
			}
		}
		ViewablesCatalog.AddNodeToRoot(node);
		LogBookController.OnViewablesRegistered?.Invoke();
	}
}
