Decompiled source of NuageReport v1.0.0

NuageReport.Core.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.CodeAnalysis;
using NuageReport.Core.Model;

[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("TheoHay, GangDesNuages")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f7dcda716f6ccd2361b1be02417714f4cb5ee1f9")]
[assembly: AssemblyProduct("NuageReport.Core")]
[assembly: AssemblyTitle("NuageReport.Core")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TheoHay/NuageREPO")]
[assembly: AssemblyVersion("1.0.0.0")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 NuageReport.Core.Reporting
{
	public sealed class DisplayReportFormatter
	{
		public string Format(RunReport report, DisplayReportMode mode)
		{
			if (report == null)
			{
				throw new ArgumentNullException("report");
			}
			RunReportSummary summary = report.Summary;
			GlobalReportSummary global = summary.Global;
			StringBuilder stringBuilder = new StringBuilder();
			AppendSection(stringBuilder, "Global data");
			AppendCountAndValue(stringBuilder, "Objects in map", global.ObjectsAtStart, global.ObjectsAtStartValue);
			AppendCountAndValue(stringBuilder, "Objects extracted", global.ObjectsExtracted, global.ObjectsExtractedValue);
			stringBuilder.Append("Objects detected: ").Append(global.ObjectsDetected.ToString(CultureInfo.InvariantCulture)).Append(" / ")
				.Append(global.ObjectsAtStart.ToString(CultureInfo.InvariantCulture))
				.Append(" (")
				.Append(global.ObjectsDetectedPercent.ToString("0.##", CultureInfo.InvariantCulture))
				.AppendLine("%)");
			stringBuilder.Append("Objects missed: ").AppendLine(global.ObjectsMissed.ToString(CultureInfo.InvariantCulture));
			stringBuilder.Append("Objects damaged: ").AppendLine(global.ObjectsDamaged.ToString(CultureInfo.InvariantCulture));
			stringBuilder.Append("Objects destroyed: ").AppendLine(global.ObjectsDestroyed.ToString(CultureInfo.InvariantCulture));
			AppendValue(stringBuilder, "Total value lost", global.ObjectsValueLost);
			stringBuilder.AppendLine();
			AppendCountAndValue(stringBuilder, "Monster loot orbs collected", global.OrbsCollected, global.OrbsExtractedValue);
			stringBuilder.Append("Monster loot orbs destroyed: ").Append(global.OrbsDestroyed.ToString(CultureInfo.InvariantCulture)).Append(" ($")
				.Append(global.OrbsLostValue.ToString(CultureInfo.InvariantCulture))
				.AppendLine(" lost)");
			stringBuilder.AppendLine();
			stringBuilder.Append("Damage dealt to monsters: ").AppendLine(global.MonsterDamageTaken.ToString(CultureInfo.InvariantCulture));
			stringBuilder.Append("Monsters killed: ").AppendLine(global.MonstersKilled.ToString(CultureInfo.InvariantCulture));
			stringBuilder.Append("Damage taken by players: ").AppendLine(global.PlayerDamageTaken.ToString(CultureInfo.InvariantCulture));
			AppendSection(stringBuilder, "Player data");
			if (summary.Players.Count == 0)
			{
				stringBuilder.AppendLine("No player-specific stats recorded yet.");
			}
			else
			{
				foreach (PlayerReportSummary player in summary.Players)
				{
					string value = (string.IsNullOrWhiteSpace(player.DisplayName) ? player.PlayerId : player.DisplayName);
					stringBuilder.AppendLine(value);
					stringBuilder.Append("  Cart/cashout objects: ").Append(player.ObjectsPutInCartOrCashout.ToString(CultureInfo.InvariantCulture)).Append(" ($")
						.Append(player.ObjectsPutInCartOrCashoutValue.ToString(CultureInfo.InvariantCulture))
						.AppendLine(")");
					stringBuilder.Append("  Extracted weight: ").Append(player.ExtractedObjectWeight.ToString("0.##", CultureInfo.InvariantCulture)).Append(" total, ")
						.Append(player.AverageExtractedObjectWeight.ToString("0.##", CultureInfo.InvariantCulture))
						.AppendLine(" avg/object");
					stringBuilder.Append("  Average extracted value: $").Append(player.AverageExtractedObjectValue.ToString(CultureInfo.InvariantCulture)).AppendLine("/object");
					stringBuilder.Append("  Objects detected: ").Append(player.ObjectsDetected.ToString(CultureInfo.InvariantCulture)).Append(" ($")
						.Append(player.ObjectsDetectedValue.ToString(CultureInfo.InvariantCulture))
						.AppendLine(")");
					stringBuilder.Append("  Damage taken: ").AppendLine(player.DamageTaken.ToString(CultureInfo.InvariantCulture));
					stringBuilder.AppendLine();
				}
			}
			return stringBuilder.ToString().TrimEnd();
		}

		private static void AppendSection(StringBuilder builder, string title)
		{
			if (builder.Length > 0)
			{
				builder.AppendLine();
			}
			builder.AppendLine(title.ToUpperInvariant());
			builder.AppendLine("----------------------------------------");
		}

		private static void AppendValue(StringBuilder builder, string label, int value)
		{
			builder.Append(label).Append(": $").AppendLine(value.ToString(CultureInfo.InvariantCulture));
		}

		private static void AppendCountAndValue(StringBuilder builder, string label, int count, int value)
		{
			builder.Append(label).Append(": ").Append(count.ToString(CultureInfo.InvariantCulture))
				.Append(" ($")
				.Append(value.ToString(CultureInfo.InvariantCulture))
				.AppendLine(")");
		}
	}
	public enum DisplayReportMode
	{
		Summary,
		Live
	}
	public sealed class JsonReportFormatter
	{
		public string Format(RunReport report)
		{
			if (report == null)
			{
				throw new ArgumentNullException("report");
			}
			RunReportSummary summary = report.Summary;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("{");
			AppendProperty(stringBuilder, 1, "schemaVersion", "1", comma: true);
			AppendProperty(stringBuilder, 1, "runId", report.RunId, comma: true);
			AppendProperty(stringBuilder, 1, "startedAt", report.StartedAt.ToString("O", CultureInfo.InvariantCulture), comma: true);
			AppendProperty(stringBuilder, 1, "endedAt", report.EndedAt?.ToString("O", CultureInfo.InvariantCulture), comma: true);
			AppendGlobal(stringBuilder, summary.Global);
			stringBuilder.AppendLine(",");
			AppendPlayers(stringBuilder, summary);
			stringBuilder.AppendLine(",");
			AppendCosmeticObjects(stringBuilder, summary);
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("}");
			return stringBuilder.ToString();
		}

		private static void AppendGlobal(StringBuilder builder, GlobalReportSummary global)
		{
			Indent(builder, 1).AppendLine("\"global\": {");
			AppendProperty(builder, 2, "objectsAtStart", global.ObjectsAtStart, comma: true);
			AppendProperty(builder, 2, "objectsAtStartValue", global.ObjectsAtStartValue, comma: true);
			AppendProperty(builder, 2, "objectsExtracted", global.ObjectsExtracted, comma: true);
			AppendProperty(builder, 2, "objectsExtractedValue", global.ObjectsExtractedValue, comma: true);
			AppendProperty(builder, 2, "objectsDetected", global.ObjectsDetected, comma: true);
			AppendProperty(builder, 2, "objectsDetectedPercent", global.ObjectsDetectedPercent, comma: true);
			AppendProperty(builder, 2, "objectsMissed", global.ObjectsMissed, comma: true);
			AppendProperty(builder, 2, "objectsDamaged", global.ObjectsDamaged, comma: true);
			AppendProperty(builder, 2, "objectsDestroyed", global.ObjectsDestroyed, comma: true);
			AppendProperty(builder, 2, "objectsValueLost", global.ObjectsValueLost, comma: true);
			AppendProperty(builder, 2, "orbsCollected", global.OrbsCollected, comma: true);
			AppendProperty(builder, 2, "orbsDestroyed", global.OrbsDestroyed, comma: true);
			AppendProperty(builder, 2, "orbsExtractedValue", global.OrbsExtractedValue, comma: true);
			AppendProperty(builder, 2, "orbsLostValue", global.OrbsLostValue, comma: true);
			AppendProperty(builder, 2, "cosmeticObjectsExisting", global.CosmeticObjectsExisting, comma: true);
			AppendProperty(builder, 2, "cosmeticObjectsFound", global.CosmeticObjectsFound, comma: true);
			AppendProperty(builder, 2, "monsterDamageTaken", global.MonsterDamageTaken, comma: true);
			AppendProperty(builder, 2, "monstersKilled", global.MonstersKilled, comma: true);
			AppendProperty(builder, 2, "playerDamageTaken", global.PlayerDamageTaken, comma: false);
			Indent(builder, 1).Append('}');
		}

		private static void AppendPlayers(StringBuilder builder, RunReportSummary summary)
		{
			Indent(builder, 1).AppendLine("\"players\": [");
			for (int i = 0; i < summary.Players.Count; i++)
			{
				PlayerReportSummary playerReportSummary = summary.Players[i];
				Indent(builder, 2).AppendLine("{");
				AppendProperty(builder, 3, "playerId", playerReportSummary.PlayerId, comma: true);
				AppendProperty(builder, 3, "displayName", playerReportSummary.DisplayName, comma: true);
				AppendProperty(builder, 3, "objectsPutInCartOrCashout", playerReportSummary.ObjectsPutInCartOrCashout, comma: true);
				AppendProperty(builder, 3, "objectsPutInCartOrCashoutValue", playerReportSummary.ObjectsPutInCartOrCashoutValue, comma: true);
				AppendProperty(builder, 3, "extractedObjectWeight", playerReportSummary.ExtractedObjectWeight, comma: true);
				AppendProperty(builder, 3, "averageExtractedObjectWeight", playerReportSummary.AverageExtractedObjectWeight, comma: true);
				AppendProperty(builder, 3, "averageExtractedObjectValue", playerReportSummary.AverageExtractedObjectValue, comma: true);
				AppendProperty(builder, 3, "objectsDetected", playerReportSummary.ObjectsDetected, comma: true);
				AppendProperty(builder, 3, "objectsDetectedValue", playerReportSummary.ObjectsDetectedValue, comma: true);
				AppendProperty(builder, 3, "damageDealt", playerReportSummary.DamageDealt, comma: true);
				AppendProperty(builder, 3, "damageTaken", playerReportSummary.DamageTaken, comma: false);
				Indent(builder, 2).Append('}');
				if (i < summary.Players.Count - 1)
				{
					builder.Append(',');
				}
				builder.AppendLine();
			}
			Indent(builder, 1).Append(']');
		}

		private static void AppendCosmeticObjects(StringBuilder builder, RunReportSummary summary)
		{
			Indent(builder, 1).AppendLine("\"cosmeticObjects\": {");
			AppendValuableList(builder, 2, "existing", summary.CosmeticObjectsExisting, comma: true);
			AppendValuableList(builder, 2, "found", summary.CosmeticObjectsFound, comma: false);
			Indent(builder, 1).Append('}');
		}

		private static void AppendValuableList(StringBuilder builder, int depth, string name, IReadOnlyList<ValuableRef> values, bool comma)
		{
			Indent(builder, depth).Append('"').Append(Escape(name)).AppendLine("\": [");
			for (int i = 0; i < values.Count; i++)
			{
				ValuableRef valuableRef = values[i];
				Indent(builder, depth + 1).Append("{ ");
				AppendInlineProperty(builder, "runtimeId", valuableRef.RuntimeId, comma: true);
				AppendInlineProperty(builder, "name", valuableRef.Name, comma: true);
				AppendInlineProperty(builder, "valueAtEvent", valuableRef.ValueAtEvent, comma: true);
				AppendInlineProperty(builder, "weightAtEvent", valuableRef.WeightAtEvent, comma: false);
				builder.Append(" }");
				if (i < values.Count - 1)
				{
					builder.Append(',');
				}
				builder.AppendLine();
			}
			Indent(builder, depth).Append(']');
			if (comma)
			{
				builder.Append(',');
			}
			builder.AppendLine();
		}

		private static void AppendProperty(StringBuilder builder, int depth, string name, string? value, bool comma)
		{
			Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ");
			if (value == null)
			{
				builder.Append("null");
			}
			else
			{
				builder.Append('"').Append(Escape(value)).Append('"');
			}
			AppendLineEnding(builder, comma);
		}

		private static void AppendProperty(StringBuilder builder, int depth, string name, int? value, bool comma)
		{
			Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ");
			builder.Append(value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : "null");
			AppendLineEnding(builder, comma);
		}

		private static void AppendProperty(StringBuilder builder, int depth, string name, int value, bool comma)
		{
			Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ")
				.Append(value.ToString(CultureInfo.InvariantCulture));
			AppendLineEnding(builder, comma);
		}

		private static void AppendProperty(StringBuilder builder, int depth, string name, double value, bool comma)
		{
			Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ")
				.Append(value.ToString("0.##", CultureInfo.InvariantCulture));
			AppendLineEnding(builder, comma);
		}

		private static void AppendProperty(StringBuilder builder, int depth, string name, float value, bool comma)
		{
			Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ")
				.Append(value.ToString("0.###", CultureInfo.InvariantCulture));
			AppendLineEnding(builder, comma);
		}

		private static void AppendProperty(StringBuilder builder, int depth, string name, bool value, bool comma)
		{
			Indent(builder, depth).Append('"').Append(Escape(name)).Append("\": ")
				.Append(value ? "true" : "false");
			AppendLineEnding(builder, comma);
		}

		private static void AppendInlineProperty(StringBuilder builder, string name, string? value, bool comma)
		{
			builder.Append('"').Append(Escape(name)).Append("\": ");
			if (value == null)
			{
				builder.Append("null");
			}
			else
			{
				builder.Append('"').Append(Escape(value)).Append('"');
			}
			if (comma)
			{
				builder.Append(", ");
			}
		}

		private static void AppendInlineProperty(StringBuilder builder, string name, int? value, bool comma)
		{
			builder.Append('"').Append(Escape(name)).Append("\": ");
			builder.Append(value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : "null");
			if (comma)
			{
				builder.Append(", ");
			}
		}

		private static void AppendInlineProperty(StringBuilder builder, string name, float? value, bool comma)
		{
			builder.Append('"').Append(Escape(name)).Append("\": ");
			builder.Append(value.HasValue ? value.Value.ToString("0.###", CultureInfo.InvariantCulture) : "null");
			if (comma)
			{
				builder.Append(", ");
			}
		}

		private static void AppendLineEnding(StringBuilder builder, bool comma)
		{
			if (comma)
			{
				builder.Append(',');
			}
			builder.AppendLine();
		}

		private static StringBuilder Indent(StringBuilder builder, int depth)
		{
			return builder.Append(' ', depth * 2);
		}

		private static string Escape(string value)
		{
			StringBuilder stringBuilder = new StringBuilder(value.Length + 8);
			foreach (char c in value)
			{
				switch (c)
				{
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\b':
					stringBuilder.Append("\\b");
					continue;
				case '\f':
					stringBuilder.Append("\\f");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (char.IsControl(c))
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4", CultureInfo.InvariantCulture));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}
	}
	public sealed class LiveReportPayloadFormatter
	{
		public string Format(RunReport report)
		{
			if (report == null)
			{
				throw new ArgumentNullException("report");
			}
			return new JsonReportFormatter().Format(report);
		}
	}
	public sealed class TextReportFormatter
	{
		public string Format(RunReport report)
		{
			if (report == null)
			{
				throw new ArgumentNullException("report");
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Run: " + report.RunId);
			stringBuilder.AppendLine($"Started: {report.StartedAt:u}");
			stringBuilder.AppendLine("Ended: " + (report.EndedAt.HasValue ? report.EndedAt.Value.ToString("u") : "active"));
			stringBuilder.AppendLine($"Objects at start: {report.Summary.Global.ObjectsAtStart} (${report.Summary.Global.ObjectsAtStartValue})");
			stringBuilder.AppendLine($"Discovered valuables: {report.Summary.Global.ObjectsDetected} ({report.Summary.Global.ObjectsDetectedPercent:0.##}%)");
			stringBuilder.AppendLine($"First carted valuables: {report.FirstCartedCount}");
			stringBuilder.AppendLine($"Extracted value: ${report.Summary.Global.ObjectsExtractedValue}");
			stringBuilder.AppendLine($"Lost/destroyed value: ${report.Summary.Global.ObjectsValueLost}");
			stringBuilder.AppendLine($"Monster damage taken: {report.Summary.Global.MonsterDamageTaken}");
			stringBuilder.AppendLine($"Player damage taken: {report.Summary.Global.PlayerDamageTaken}");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("Events:");
			foreach (RunEvent @event in report.Events)
			{
				stringBuilder.Append("- ");
				stringBuilder.Append(@event.Timestamp.ToString("u"));
				stringBuilder.Append(' ');
				stringBuilder.Append(@event.Type);
				stringBuilder.Append(" [");
				stringBuilder.Append(@event.Confidence);
				stringBuilder.Append(']');
				if (@event.Valuable != null)
				{
					stringBuilder.Append(" valuable=");
					stringBuilder.Append(@event.Valuable);
				}
				if (@event.Player != null)
				{
					stringBuilder.Append(" player=");
					stringBuilder.Append(@event.Player);
				}
				if (!string.IsNullOrWhiteSpace(@event.Message))
				{
					stringBuilder.Append(" — ");
					stringBuilder.Append(@event.Message);
				}
				stringBuilder.AppendLine();
			}
			return stringBuilder.ToString();
		}
	}
}
namespace NuageReport.Core.Recording
{
	public interface IClock
	{
		DateTimeOffset UtcNow { get; }
	}
	public interface IRunEventSink
	{
		void Record(RunEvent runEvent);
	}
	public sealed class RunRecorder : IRunEventSink
	{
		private readonly IClock _clock;

		private readonly List<RunEvent> _events = new List<RunEvent>();

		private readonly Dictionary<string, ValuableRef> _spawnedValuables = new Dictionary<string, ValuableRef>(StringComparer.Ordinal);

		private readonly HashSet<string> _discoveredValuables = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _firstCartedValuables = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _extractedValuables = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _destroyedValuables = new HashSet<string>(StringComparer.Ordinal);

		private readonly Dictionary<string, PlayerRef> _lockedValuableAttribution = new Dictionary<string, PlayerRef>(StringComparer.Ordinal);

		private readonly HashSet<string> _spawnedOrbs = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _extractedOrbs = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _destroyedOrbs = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _cosmeticObjectsExisting = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _cosmeticObjectsFound = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _killedMonsters = new HashSet<string>(StringComparer.Ordinal);

		private string? _activeRunId;

		private DateTimeOffset _startedAt;

		private DateTimeOffset? _endedAt;

		public bool IsActive => _activeRunId != null;

		public string? ActiveRunId => _activeRunId;

		public int EventVersion { get; private set; }

		public event Action<RunEvent>? EventRecorded;

		public RunRecorder(IClock clock)
		{
			_clock = clock ?? throw new ArgumentNullException("clock");
		}

		public void StartRun(string runId, string? levelName)
		{
			if (string.IsNullOrWhiteSpace(runId))
			{
				throw new ArgumentException("Run id is required.", "runId");
			}
			_activeRunId = runId;
			_startedAt = _clock.UtcNow;
			_endedAt = null;
			EventVersion = 0;
			_events.Clear();
			_spawnedValuables.Clear();
			_discoveredValuables.Clear();
			_firstCartedValuables.Clear();
			_extractedValuables.Clear();
			_destroyedValuables.Clear();
			_lockedValuableAttribution.Clear();
			_spawnedOrbs.Clear();
			_extractedOrbs.Clear();
			_destroyedOrbs.Clear();
			_cosmeticObjectsExisting.Clear();
			_cosmeticObjectsFound.Clear();
			_killedMonsters.Clear();
			Record(new RunEvent(RunEventType.LevelStarted, _startedAt, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Message = "Level started."
			});
		}

		public bool TryRecordValuableSpawned(ValuableRef valuable, string? levelName = null)
		{
			if (valuable == null)
			{
				throw new ArgumentNullException("valuable");
			}
			if (!IsActive)
			{
				return false;
			}
			if (_spawnedValuables.TryGetValue(valuable.RuntimeId, out ValuableRef value))
			{
				_spawnedValuables[valuable.RuntimeId] = Merge(value, valuable);
				if ((valuable.ValueAtEvent.HasValue && value.ValueAtEvent != valuable.ValueAtEvent) || (valuable.WeightAtEvent.HasValue && value.WeightAtEvent != valuable.WeightAtEvent))
				{
					Record(new RunEvent(RunEventType.ValuableValueUpdated, _clock.UtcNow, ConfidenceLevel.High)
					{
						LevelName = levelName,
						Valuable = _spawnedValuables[valuable.RuntimeId],
						Message = "Valuable value/weight updated."
					});
				}
				return false;
			}
			_spawnedValuables.Add(valuable.RuntimeId, valuable);
			Record(new RunEvent(RunEventType.ValuableSpawned, _clock.UtcNow, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Valuable = valuable,
				Message = "Valuable spawned."
			});
			return true;
		}

		public bool TryRecordValuableDiscovered(ValuableRef valuable, PlayerRef? player = null, ConfidenceLevel confidence = ConfidenceLevel.High, string? levelName = null)
		{
			if (valuable == null)
			{
				throw new ArgumentNullException("valuable");
			}
			if (!IsActive || !_discoveredValuables.Add(valuable.RuntimeId))
			{
				return false;
			}
			Record(new RunEvent(RunEventType.ValuableDiscovered, _clock.UtcNow, confidence)
			{
				LevelName = levelName,
				Valuable = valuable,
				Player = player,
				Message = ((player == null) ? "Valuable discovered." : "Valuable discovered by player.")
			});
			return true;
		}

		public bool TryRecordFirstCarted(ValuableRef valuable, string? levelName = null)
		{
			return TryRecordFirstCarted(valuable, null, ConfidenceLevel.High, levelName);
		}

		public bool TryRecordFirstCarted(ValuableRef valuable, PlayerRef? player, ConfidenceLevel confidence = ConfidenceLevel.Medium, string? levelName = null)
		{
			if (valuable == null)
			{
				throw new ArgumentNullException("valuable");
			}
			if (!IsActive || !_firstCartedValuables.Add(valuable.RuntimeId))
			{
				return false;
			}
			LockAttributionIfAvailable(valuable.RuntimeId, player);
			Record(new RunEvent(RunEventType.ValuableFirstCarted, _clock.UtcNow, confidence)
			{
				LevelName = levelName,
				Valuable = valuable,
				Player = (GetLockedAttribution(valuable.RuntimeId) ?? player),
				Message = "Valuable entered a cart or cashout zone for the first confirmed time."
			});
			return true;
		}

		public bool TryRecordValuableExtracted(ValuableRef valuable, PlayerRef? player = null, ConfidenceLevel confidence = ConfidenceLevel.High, string? levelName = null)
		{
			if (valuable == null)
			{
				throw new ArgumentNullException("valuable");
			}
			if (!IsActive || !_extractedValuables.Add(valuable.RuntimeId))
			{
				return false;
			}
			LockAttributionIfAvailable(valuable.RuntimeId, player);
			Record(new RunEvent(RunEventType.ValuableExtracted, _clock.UtcNow, confidence)
			{
				LevelName = levelName,
				Valuable = valuable,
				Player = (GetLockedAttribution(valuable.RuntimeId) ?? player),
				Message = ((player == null) ? "Valuable extracted." : "Valuable extracted with player attribution.")
			});
			return true;
		}

		public void RecordValuableDamaged(ValuableRef valuable, int valueLost, string? levelName = null)
		{
			if (valuable == null)
			{
				throw new ArgumentNullException("valuable");
			}
			if (IsActive)
			{
				int num = Math.Max(0, valueLost);
				Record(new RunEvent(RunEventType.ValuableDamaged, _clock.UtcNow, ConfidenceLevel.High)
				{
					LevelName = levelName,
					Valuable = valuable,
					DeltaValue = ((num != 0) ? (-num) : 0),
					Message = ((num == 0) ? "Valuable damaged." : "Valuable lost value from damage.")
				});
			}
		}

		public bool TryRecordValuableDestroyed(ValuableRef valuable, string? levelName = null)
		{
			if (valuable == null)
			{
				throw new ArgumentNullException("valuable");
			}
			if (!IsActive || !_destroyedValuables.Add(valuable.RuntimeId))
			{
				return false;
			}
			Record(new RunEvent(RunEventType.ValuableDestroyed, _clock.UtcNow, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Valuable = valuable,
				Message = "Valuable destroyed."
			});
			return true;
		}

		public bool TryRecordOrbSpawned(ValuableRef orb, string? rarity = null, string? levelName = null)
		{
			if (orb == null)
			{
				throw new ArgumentNullException("orb");
			}
			if (!IsActive || !_spawnedOrbs.Add(orb.RuntimeId))
			{
				return false;
			}
			RunEvent runEvent = new RunEvent(RunEventType.OrbSpawned, _clock.UtcNow, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Valuable = orb,
				Message = "Monster loot orb spawned."
			};
			AddTag(runEvent, "rarity", rarity);
			Record(runEvent);
			return true;
		}

		public bool TryRecordOrbExtracted(ValuableRef orb, string? rarity = null, string? levelName = null)
		{
			if (orb == null)
			{
				throw new ArgumentNullException("orb");
			}
			if (!IsActive || !_extractedOrbs.Add(orb.RuntimeId))
			{
				return false;
			}
			RunEvent runEvent = new RunEvent(RunEventType.OrbExtracted, _clock.UtcNow, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Valuable = orb,
				Message = "Monster loot orb extracted."
			};
			AddTag(runEvent, "rarity", rarity);
			Record(runEvent);
			return true;
		}

		public bool TryRecordOrbDestroyed(ValuableRef orb, string? rarity = null, string? levelName = null)
		{
			if (orb == null)
			{
				throw new ArgumentNullException("orb");
			}
			if (!IsActive || !_destroyedOrbs.Add(orb.RuntimeId))
			{
				return false;
			}
			RunEvent runEvent = new RunEvent(RunEventType.OrbDestroyed, _clock.UtcNow, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Valuable = orb,
				Message = "Monster loot orb destroyed."
			};
			AddTag(runEvent, "rarity", rarity);
			Record(runEvent);
			return true;
		}

		public bool TryRecordCosmeticObjectExisting(ValuableRef cosmeticObject, string? rarity = null, string? levelName = null)
		{
			if (cosmeticObject == null)
			{
				throw new ArgumentNullException("cosmeticObject");
			}
			if (!IsActive || !_cosmeticObjectsExisting.Add(cosmeticObject.RuntimeId))
			{
				return false;
			}
			RunEvent runEvent = new RunEvent(RunEventType.CosmeticObjectExisting, _clock.UtcNow, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Valuable = cosmeticObject,
				Message = "Cosmetic object existed in the level."
			};
			AddTag(runEvent, "rarity", rarity);
			Record(runEvent);
			return true;
		}

		public bool TryRecordCosmeticObjectFound(ValuableRef cosmeticObject, string? rarity = null, string? levelName = null)
		{
			if (cosmeticObject == null)
			{
				throw new ArgumentNullException("cosmeticObject");
			}
			if (!IsActive || !_cosmeticObjectsFound.Add(cosmeticObject.RuntimeId))
			{
				return false;
			}
			RunEvent runEvent = new RunEvent(RunEventType.CosmeticObjectFound, _clock.UtcNow, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Valuable = cosmeticObject,
				Message = "Cosmetic object was found/extracted."
			};
			AddTag(runEvent, "rarity", rarity);
			Record(runEvent);
			return true;
		}

		public void RecordMonsterDamaged(string monsterId, string monsterName, int damageAmount, string? levelName = null)
		{
			if (!string.IsNullOrWhiteSpace(monsterId) && damageAmount > 0 && IsActive)
			{
				RunEvent runEvent = new RunEvent(RunEventType.MonsterDamaged, _clock.UtcNow, ConfidenceLevel.High)
				{
					LevelName = levelName,
					DamageAmount = damageAmount,
					Message = "Monster took damage."
				};
				AddTag(runEvent, "monsterId", monsterId);
				AddTag(runEvent, "monsterName", string.IsNullOrWhiteSpace(monsterName) ? monsterId : monsterName);
				Record(runEvent);
			}
		}

		public bool TryRecordMonsterKilled(string monsterId, string monsterName, string? levelName = null)
		{
			if (string.IsNullOrWhiteSpace(monsterId) || !IsActive || !_killedMonsters.Add(monsterId))
			{
				return false;
			}
			RunEvent runEvent = new RunEvent(RunEventType.MonsterKilled, _clock.UtcNow, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Message = "Monster killed."
			};
			AddTag(runEvent, "monsterId", monsterId);
			AddTag(runEvent, "monsterName", string.IsNullOrWhiteSpace(monsterName) ? monsterId : monsterName);
			Record(runEvent);
			return true;
		}

		public void RecordPlayerDamaged(PlayerRef player, int damageAmount, string? levelName = null)
		{
			if (player == null)
			{
				throw new ArgumentNullException("player");
			}
			if (damageAmount > 0 && IsActive)
			{
				Record(new RunEvent(RunEventType.PlayerDamaged, _clock.UtcNow, ConfidenceLevel.High)
				{
					LevelName = levelName,
					Player = player,
					DamageAmount = damageAmount,
					Message = "Player took damage."
				});
			}
		}

		public void Record(RunEvent runEvent)
		{
			if (runEvent == null)
			{
				throw new ArgumentNullException("runEvent");
			}
			_events.Add(runEvent);
			EventVersion++;
			this.EventRecorded?.Invoke(runEvent);
		}

		public RunReport EndRun(string? levelName = null)
		{
			if (_activeRunId == null)
			{
				throw new InvalidOperationException("Cannot end a run before StartRun has been called.");
			}
			_endedAt = _clock.UtcNow;
			Record(new RunEvent(RunEventType.LevelEnded, _endedAt.Value, ConfidenceLevel.High)
			{
				LevelName = levelName,
				Message = "Level ended."
			});
			RunReport result = Snapshot();
			_activeRunId = null;
			return result;
		}

		public RunReport Snapshot()
		{
			if (_activeRunId == null)
			{
				throw new InvalidOperationException("No active run is available.");
			}
			return new RunReport(_activeRunId, _startedAt, _endedAt, _events.ToArray());
		}

		private void LockAttributionIfAvailable(string runtimeId, PlayerRef? player)
		{
			if (player != null && !_lockedValuableAttribution.ContainsKey(runtimeId))
			{
				_lockedValuableAttribution.Add(runtimeId, player);
			}
		}

		private PlayerRef? GetLockedAttribution(string runtimeId)
		{
			if (!_lockedValuableAttribution.TryGetValue(runtimeId, out PlayerRef value))
			{
				return null;
			}
			return value;
		}

		private static ValuableRef Merge(ValuableRef previous, ValuableRef next)
		{
			return new ValuableRef(previous.RuntimeId, string.IsNullOrWhiteSpace(next.Name) ? previous.Name : next.Name, next.ValueAtEvent ?? previous.ValueAtEvent, next.WeightAtEvent ?? previous.WeightAtEvent);
		}

		private static void AddTag(RunEvent runEvent, string key, string? value)
		{
			if (!string.IsNullOrWhiteSpace(value))
			{
				runEvent.Tags[key] = value;
			}
		}
	}
	public sealed class SystemClock : IClock
	{
		public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
	}
}
namespace NuageReport.Core.Model
{
	public enum ConfidenceLevel
	{
		Low,
		Medium,
		High
	}
	public sealed class GlobalReportSummary
	{
		public int ObjectsAtStart { get; set; }

		public int ObjectsAtStartValue { get; set; }

		public int ObjectsExtracted { get; set; }

		public int ObjectsExtractedValue { get; set; }

		public int ObjectsDetected { get; set; }

		public double ObjectsDetectedPercent { get; set; }

		public int ObjectsMissed { get; set; }

		public int ObjectsDamaged { get; set; }

		public int ObjectsDestroyed { get; set; }

		public int ObjectsValueLost { get; set; }

		public int OrbsCollected { get; set; }

		public int OrbsDestroyed { get; set; }

		public int OrbsExtractedValue { get; set; }

		public int OrbsLostValue { get; set; }

		public int CosmeticObjectsExisting { get; set; }

		public int CosmeticObjectsFound { get; set; }

		public int MonsterDamageTaken { get; set; }

		public int MonstersKilled { get; set; }

		public int PlayerDamageTaken { get; set; }
	}
	public sealed class PlayerRef
	{
		public string Id { get; }

		public string DisplayName { get; }

		public PlayerRef(string id, string displayName)
		{
			if (string.IsNullOrWhiteSpace(id))
			{
				throw new ArgumentException("Player id is required.", "id");
			}
			Id = id;
			DisplayName = (string.IsNullOrWhiteSpace(displayName) ? id : displayName);
		}

		public override string ToString()
		{
			return DisplayName;
		}
	}
	public sealed class PlayerReportSummary
	{
		public string PlayerId { get; set; } = string.Empty;


		public string DisplayName { get; set; } = string.Empty;


		public int ObjectsPutInCartOrCashout { get; set; }

		public int ObjectsPutInCartOrCashoutValue { get; set; }

		public float ExtractedObjectWeight { get; set; }

		public float AverageExtractedObjectWeight { get; set; }

		public int AverageExtractedObjectValue { get; set; }

		public int ObjectsDetected { get; set; }

		public int ObjectsDetectedValue { get; set; }

		public int DamageDealt { get; set; }

		public int DamageTaken { get; set; }
	}
	public sealed class RunEvent
	{
		public RunEventType Type { get; }

		public DateTimeOffset Timestamp { get; }

		public ConfidenceLevel Confidence { get; }

		public string? LevelName { get; set; }

		public string? Message { get; set; }

		public ValuableRef? Valuable { get; set; }

		public PlayerRef? Player { get; set; }

		public int? DeltaValue { get; set; }

		public int? DamageAmount { get; set; }

		public IDictionary<string, string> Tags { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);


		public RunEvent(RunEventType type, DateTimeOffset timestamp, ConfidenceLevel confidence)
		{
			Type = type;
			Timestamp = timestamp;
			Confidence = confidence;
		}
	}
	public enum RunEventType
	{
		Unknown,
		LevelStarted,
		LevelEnded,
		ValuableSpawned,
		ValuableValueUpdated,
		ValuableDiscovered,
		ValuableFirstCarted,
		ValuableExtracted,
		ValuableDamaged,
		ValuableDestroyed,
		OrbSpawned,
		OrbExtracted,
		OrbDestroyed,
		CosmeticObjectExisting,
		CosmeticObjectFound,
		MonsterDamaged,
		MonsterKilled,
		PlayerDamaged,
		PlayerDied,
		PlayerDisconnected,
		ShopEntered,
		QuotaEvaluated
	}
	public sealed class RunReport
	{
		public string RunId { get; }

		public DateTimeOffset StartedAt { get; }

		public DateTimeOffset? EndedAt { get; }

		public IReadOnlyList<RunEvent> Events { get; }

		public RunReportSummary Summary => RunReportSummary.Build(this);

		public int DiscoveredCount => Events.Count((RunEvent e) => e.Type == RunEventType.ValuableDiscovered);

		public int FirstCartedCount => Events.Count((RunEvent e) => e.Type == RunEventType.ValuableFirstCarted);

		public int ExtractedValue => Events.Where((RunEvent e) => e.Type == RunEventType.ValuableExtracted).Sum((RunEvent e) => e.Valuable?.ValueAtEvent ?? e.DeltaValue.GetValueOrDefault());

		public int LostOrDestroyedValue => (from e in Events
			where e.Type == RunEventType.ValuableDestroyed || e.Type == RunEventType.ValuableDamaged
			where e.DeltaValue.HasValue && e.DeltaValue.Value < 0
			select e).Sum((RunEvent e) => -e.DeltaValue.Value);

		public RunReport(string runId, DateTimeOffset startedAt, DateTimeOffset? endedAt, IReadOnlyList<RunEvent> events)
		{
			if (string.IsNullOrWhiteSpace(runId))
			{
				throw new ArgumentException("Run id is required.", "runId");
			}
			RunId = runId;
			StartedAt = startedAt;
			EndedAt = endedAt;
			Events = events ?? throw new ArgumentNullException("events");
		}
	}
	public sealed class RunReportSummary
	{
		private sealed class PlayerAccumulator
		{
			public PlayerRef Player { get; }

			public HashSet<string> CartOrCashoutValuables { get; } = new HashSet<string>(StringComparer.Ordinal);


			public HashSet<string> ExtractedValuables { get; } = new HashSet<string>(StringComparer.Ordinal);


			public Dictionary<string, ValuableRef> DetectedValuables { get; } = new Dictionary<string, ValuableRef>(StringComparer.Ordinal);


			public int DamageTaken { get; set; }

			public PlayerAccumulator(PlayerRef player)
			{
				Player = player;
			}

			public PlayerReportSummary ToSummary(Dictionary<string, ValuableRef> latestValuables)
			{
				Dictionary<string, ValuableRef> latestValuables2 = latestValuables;
				int objectsPutInCartOrCashoutValue = CartOrCashoutValuables.Sum((string id) => ValueFor(latestValuables2, id));
				int num = ExtractedValuables.Sum((string id) => ValueFor(latestValuables2, id));
				float num2 = ExtractedValuables.Sum((string id) => WeightFor(latestValuables2, id));
				return new PlayerReportSummary
				{
					PlayerId = Player.Id,
					DisplayName = Player.DisplayName,
					ObjectsPutInCartOrCashout = CartOrCashoutValuables.Count,
					ObjectsPutInCartOrCashoutValue = objectsPutInCartOrCashoutValue,
					ExtractedObjectWeight = num2,
					AverageExtractedObjectWeight = ((ExtractedValuables.Count == 0) ? 0f : (num2 / (float)ExtractedValuables.Count)),
					AverageExtractedObjectValue = ((ExtractedValuables.Count != 0) ? (num / ExtractedValuables.Count) : 0),
					ObjectsDetected = DetectedValuables.Count,
					ObjectsDetectedValue = DetectedValuables.Values.Sum((ValuableRef v) => v.ValueAtEvent ?? ValueFor(latestValuables2, v.RuntimeId)),
					DamageDealt = 0,
					DamageTaken = DamageTaken
				};
			}
		}

		public GlobalReportSummary Global { get; }

		public IReadOnlyList<PlayerReportSummary> Players { get; }

		public IReadOnlyList<ValuableRef> CosmeticObjectsExisting { get; }

		public IReadOnlyList<ValuableRef> CosmeticObjectsFound { get; }

		private RunReportSummary(GlobalReportSummary global, IReadOnlyList<PlayerReportSummary> players, IReadOnlyList<ValuableRef> cosmeticObjectsExisting, IReadOnlyList<ValuableRef> cosmeticObjectsFound)
		{
			Global = global;
			Players = players;
			CosmeticObjectsExisting = cosmeticObjectsExisting;
			CosmeticObjectsFound = cosmeticObjectsFound;
		}

		public static RunReportSummary Build(RunReport report)
		{
			if (report == null)
			{
				throw new ArgumentNullException("report");
			}
			Dictionary<string, ValuableRef> initialValuables = new Dictionary<string, ValuableRef>(StringComparer.Ordinal);
			Dictionary<string, ValuableRef> latestValuables = new Dictionary<string, ValuableRef>(StringComparer.Ordinal);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
			HashSet<string> hashSet3 = new HashSet<string>(StringComparer.Ordinal);
			HashSet<string> hashSet4 = new HashSet<string>(StringComparer.Ordinal);
			HashSet<string> hashSet5 = new HashSet<string>(StringComparer.Ordinal);
			HashSet<string> hashSet6 = new HashSet<string>(StringComparer.Ordinal);
			Dictionary<string, ValuableRef> dictionary = new Dictionary<string, ValuableRef>(StringComparer.Ordinal);
			Dictionary<string, ValuableRef> dictionary2 = new Dictionary<string, ValuableRef>(StringComparer.Ordinal);
			HashSet<string> hashSet7 = new HashSet<string>(StringComparer.Ordinal);
			Dictionary<string, PlayerAccumulator> dictionary3 = new Dictionary<string, PlayerAccumulator>(StringComparer.Ordinal);
			Dictionary<string, PlayerRef> dictionary4 = new Dictionary<string, PlayerRef>(StringComparer.Ordinal);
			Dictionary<string, PlayerRef> dictionary5 = new Dictionary<string, PlayerRef>(StringComparer.Ordinal);
			Dictionary<string, int> dictionary6 = new Dictionary<string, int>(StringComparer.Ordinal);
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			foreach (RunEvent @event in report.Events)
			{
				if (@event.Valuable != null)
				{
					latestValuables[@event.Valuable.RuntimeId] = Merge(latestValuables, @event.Valuable);
				}
				switch (@event.Type)
				{
				case RunEventType.ValuableSpawned:
					if (@event.Valuable != null)
					{
						initialValuables[@event.Valuable.RuntimeId] = Merge(initialValuables, @event.Valuable);
					}
					break;
				case RunEventType.ValuableValueUpdated:
					if (@event.Valuable != null && initialValuables.ContainsKey(@event.Valuable.RuntimeId))
					{
						initialValuables[@event.Valuable.RuntimeId] = Merge(initialValuables, @event.Valuable);
					}
					break;
				case RunEventType.ValuableDiscovered:
					if (@event.Valuable != null)
					{
						hashSet2.Add(@event.Valuable.RuntimeId);
						if (@event.Player != null)
						{
							GetPlayer(dictionary3, @event.Player).DetectedValuables[@event.Valuable.RuntimeId] = @event.Valuable;
						}
					}
					break;
				case RunEventType.ValuableFirstCarted:
					if (@event.Valuable != null && @event.Player != null)
					{
						dictionary4[@event.Valuable.RuntimeId] = @event.Player;
					}
					break;
				case RunEventType.ValuableExtracted:
					if (@event.Valuable != null)
					{
						hashSet.Add(@event.Valuable.RuntimeId);
						if (@event.Player != null)
						{
							dictionary5[@event.Valuable.RuntimeId] = @event.Player;
						}
					}
					break;
				case RunEventType.ValuableDamaged:
					if (@event.Valuable != null)
					{
						hashSet3.Add(@event.Valuable.RuntimeId);
					}
					if (@event.DeltaValue.HasValue && @event.DeltaValue.Value < 0)
					{
						AddValueLost(dictionary6, @event.Valuable?.RuntimeId, -@event.DeltaValue.Value);
					}
					break;
				case RunEventType.ValuableDestroyed:
					if (@event.Valuable != null)
					{
						hashSet4.Add(@event.Valuable.RuntimeId);
						AddValueLost(dictionary6, @event.Valuable.RuntimeId, ValueFor(latestValuables, @event.Valuable.RuntimeId));
					}
					break;
				case RunEventType.OrbExtracted:
					if (@event.Valuable != null && hashSet5.Add(@event.Valuable.RuntimeId))
					{
						num += @event.Valuable.ValueAtEvent.GetValueOrDefault();
					}
					break;
				case RunEventType.OrbDestroyed:
					if (@event.Valuable != null && hashSet6.Add(@event.Valuable.RuntimeId))
					{
						num2 += @event.Valuable.ValueAtEvent.GetValueOrDefault();
					}
					break;
				case RunEventType.CosmeticObjectExisting:
					if (@event.Valuable != null)
					{
						dictionary[@event.Valuable.RuntimeId] = Merge(dictionary, @event.Valuable);
					}
					break;
				case RunEventType.CosmeticObjectFound:
					if (@event.Valuable != null)
					{
						dictionary2[@event.Valuable.RuntimeId] = Merge(dictionary2, @event.Valuable);
					}
					break;
				case RunEventType.MonsterDamaged:
					num3 += @event.DamageAmount.GetValueOrDefault();
					break;
				case RunEventType.MonsterKilled:
					hashSet7.Add(TagValue(@event, "monsterId", @event.Message ?? @event.Timestamp.ToUnixTimeMilliseconds().ToString()));
					break;
				case RunEventType.PlayerDamaged:
					num4 += @event.DamageAmount.GetValueOrDefault();
					if (@event.Player != null)
					{
						GetPlayer(dictionary3, @event.Player).DamageTaken += @event.DamageAmount.GetValueOrDefault();
					}
					break;
				}
			}
			foreach (KeyValuePair<string, PlayerRef> item in dictionary4)
			{
				GetPlayer(dictionary3, item.Value).CartOrCashoutValuables.Add(item.Key);
			}
			foreach (KeyValuePair<string, PlayerRef> item2 in dictionary5)
			{
				PlayerRef value;
				PlayerRef playerRef = (dictionary4.TryGetValue(item2.Key, out value) ? value : item2.Value);
				PlayerAccumulator player = GetPlayer(dictionary3, playerRef);
				player.ExtractedValuables.Add(item2.Key);
				if (!dictionary4.ContainsKey(item2.Key))
				{
					player.CartOrCashoutValuables.Add(item2.Key);
				}
			}
			GlobalReportSummary global = new GlobalReportSummary
			{
				ObjectsAtStart = initialValuables.Count,
				ObjectsAtStartValue = initialValuables.Values.Sum((ValuableRef v) => v.ValueAtEvent.GetValueOrDefault()),
				ObjectsExtracted = hashSet.Count,
				ObjectsExtractedValue = hashSet.Sum((string id) => ValueFor(latestValuables, id)),
				ObjectsDetected = hashSet2.Count,
				ObjectsDetectedPercent = ((initialValuables.Count == 0) ? 0.0 : Math.Round((double)hashSet2.Count / (double)initialValuables.Count * 100.0, 2)),
				ObjectsMissed = Math.Max(0, initialValuables.Count - hashSet2.Count),
				ObjectsDamaged = hashSet3.Count,
				ObjectsDestroyed = hashSet4.Count,
				ObjectsValueLost = dictionary6.Sum((KeyValuePair<string, int> pair) => Math.Min(pair.Value, (ValueFor(initialValuables, pair.Key) == 0) ? pair.Value : ValueFor(initialValuables, pair.Key))),
				OrbsCollected = hashSet5.Count,
				OrbsDestroyed = hashSet6.Count,
				OrbsExtractedValue = num,
				OrbsLostValue = num2,
				CosmeticObjectsExisting = dictionary.Count,
				CosmeticObjectsFound = dictionary2.Count,
				MonsterDamageTaken = num3,
				MonstersKilled = hashSet7.Count,
				PlayerDamageTaken = num4
			};
			List<PlayerReportSummary> players = (from p in dictionary3.Values.OrderBy<PlayerAccumulator, string>((PlayerAccumulator p) => p.Player.DisplayName, StringComparer.OrdinalIgnoreCase)
				select p.ToSummary(latestValuables)).ToList();
			return new RunReportSummary(global, players, dictionary.Values.OrderBy<ValuableRef, string>((ValuableRef v) => v.Name, StringComparer.OrdinalIgnoreCase).ToList(), dictionary2.Values.OrderBy<ValuableRef, string>((ValuableRef v) => v.Name, StringComparer.OrdinalIgnoreCase).ToList());
		}

		private static void AddValueLost(Dictionary<string, int> values, string? runtimeId, int valueLost)
		{
			if (!string.IsNullOrWhiteSpace(runtimeId) && valueLost > 0)
			{
				values[runtimeId] = (values.TryGetValue(runtimeId, out var value) ? (value + valueLost) : valueLost);
			}
		}

		private static PlayerAccumulator GetPlayer(Dictionary<string, PlayerAccumulator> players, PlayerRef playerRef)
		{
			if (!players.TryGetValue(playerRef.Id, out PlayerAccumulator value))
			{
				value = new PlayerAccumulator(playerRef);
				players.Add(playerRef.Id, value);
			}
			return value;
		}

		private static ValuableRef Merge(Dictionary<string, ValuableRef> refs, ValuableRef next)
		{
			if (!refs.TryGetValue(next.RuntimeId, out ValuableRef value))
			{
				return next;
			}
			return new ValuableRef(next.RuntimeId, string.IsNullOrWhiteSpace(next.Name) ? value.Name : next.Name, next.ValueAtEvent ?? value.ValueAtEvent, next.WeightAtEvent ?? value.WeightAtEvent);
		}

		private static int ValueFor(Dictionary<string, ValuableRef> refs, string runtimeId)
		{
			if (!refs.TryGetValue(runtimeId, out ValuableRef value))
			{
				return 0;
			}
			return value.ValueAtEvent.GetValueOrDefault();
		}

		private static float WeightFor(Dictionary<string, ValuableRef> refs, string runtimeId)
		{
			if (!refs.TryGetValue(runtimeId, out ValuableRef value))
			{
				return 0f;
			}
			return value.WeightAtEvent.GetValueOrDefault();
		}

		private static string TagValue(RunEvent runEvent, string key, string fallback)
		{
			if (!runEvent.Tags.TryGetValue(key, out string value) || string.IsNullOrWhiteSpace(value))
			{
				return fallback;
			}
			return value;
		}
	}
	public sealed class ValuableRef
	{
		public string RuntimeId { get; }

		public string Name { get; }

		public int? ValueAtEvent { get; }

		public float? WeightAtEvent { get; }

		public ValuableRef(string runtimeId, string name, int? valueAtEvent = null, float? weightAtEvent = null)
		{
			if (string.IsNullOrWhiteSpace(runtimeId))
			{
				throw new ArgumentException("Runtime id is required.", "runtimeId");
			}
			RuntimeId = runtimeId;
			Name = (string.IsNullOrWhiteSpace(name) ? runtimeId : name);
			ValueAtEvent = valueAtEvent;
			WeightAtEvent = weightAtEvent;
		}

		public override string ToString()
		{
			if (!ValueAtEvent.HasValue)
			{
				return Name;
			}
			return $"{Name} (${ValueAtEvent.Value})";
		}
	}
}

NuageReport.Plugin.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NuageReport.Core.Model;
using NuageReport.Core.Recording;
using NuageReport.Core.Reporting;
using NuageReport.Plugin.Config;
using NuageReport.Plugin.Infrastructure;
using NuageReport.Plugin.Networking;
using NuageReport.Plugin.Patches;
using NuageReport.Plugin.Reports;
using NuageReport.Plugin.UI;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[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("TheoHay, GangDesNuages")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f7dcda716f6ccd2361b1be02417714f4cb5ee1f9")]
[assembly: AssemblyProduct("NuageReport.Plugin")]
[assembly: AssemblyTitle("NuageReport.Plugin")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TheoHay/NuageREPO")]
[assembly: AssemblyVersion("1.0.0.0")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 NuageReport.Plugin
{
	[BepInPlugin("com.theohay.nuagereport", "NuageReport", "1.0.0")]
	public sealed class NuageReportPlugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <PollingLoop>d__52 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private float <resumeAt>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <PollingLoop>d__52(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_0056;
				}
				<>1__state = -1;
				LogVerbose("NuageReport polling coroutine started.");
				goto IL_0021;
				IL_0056:
				if (Time.unscaledTime < <resumeAt>5__2)
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				goto IL_0021;
				IL_0021:
				TickSurvivingSystems("Plugin.PollingLoop", forcePoll: true);
				<resumeAt>5__2 = Time.unscaledTime + 0.5f;
				goto IL_0056;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public const string PluginGuid = "com.theohay.nuagereport";

		public const string PluginName = "NuageReport";

		public const string PluginVersion = "1.0.0";

		private const float InitialReportCatchupDelaySeconds = 3f;

		private const float ReportCatchupIntervalSeconds = 2f;

		private Harmony? _harmony;

		private static bool _initialObjectBackfillDone;

		private static readonly PollingRecorder SharedPollingRecorder = new PollingRecorder();

		private static readonly ReportSnapshotFactory SnapshotFactory = new ReportSnapshotFactory();

		private static GameObject? _runnerObject;

		private static float _nextLiveReportSyncAt;

		private static float _nextReportCatchupRequestAt;

		private static string? _lastLiveReportRunId;

		private static int _lastLiveReportVersionPublished = -1;

		private bool _isQuitting;

		internal static ManualLogSource Log { get; private set; } = null;


		internal static RunRecorder Recorder { get; private set; } = null;


		internal static ReportExporter Exporter { get; private set; } = null;


		internal static ReportSyncService? ReportSync { get; private set; }

		internal static ReportUiController? ReportUi { get; private set; }

		internal static bool CanRecord => PatchSafety.Try("NuageReport Plugin.CanRecord", () => PluginConfig.Instance.EnableRecording != null && PluginConfig.Instance.IsModActive() && HostAuthority.IsAuthoritativeHost(), fallback: false);

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			PluginConfig.Instance.Bind(((BaseUnityPlugin)this).Config);
			PluginPaths.Initialize("NuageReport");
			Recorder = new RunRecorder((IClock)new SystemClock());
			Recorder.EventRecorded += LogRecordedEvent;
			Exporter = new ReportExporter();
			ReportSync = new ReportSyncService();
			ReportUi = new ReportUiController(ReportSync, CreateLiveReportSnapshot);
			ReportSync.FinalReportReceived += HandleFinalReportReceived;
			ReportSync.LiveReportReceived += HandleLiveReportReceived;
			ReportUi.Initialize();
			_harmony = new Harmony("com.theohay.nuagereport");
			WriteStartupMarker();
			InstallPatchFamilies();
			Log.LogInfo((object)("NuageReport 1.0.0 loaded. " + $"Recording={PluginConfig.Instance.EnableRecording.Value}, " + $"JsonOutput={PluginConfig.Instance.EnableJsonOutput.Value}, " + "RecordingAuthority=Host, " + $"LiveReport={PluginConfig.Instance.IsLiveReportEnabled()}, " + $"ShareReports={PluginConfig.Instance.ShouldShareReports()}, " + $"VerboseLogging={PluginConfig.Instance.ShouldLogVerbose()}, " + $"LogHookHits={PluginConfig.Instance.LogHookHits.Value}, " + $"LogRecordedEvents={PluginConfig.Instance.LogRecordedEvents.Value}, " + $"CartConfirmation={PluginConfig.Instance.CartConfirmationSeconds.Value:0.###}s, " + $"LiveReportSyncInterval={EffectiveLiveReportSyncIntervalSeconds():0.###}s, " + $"ReportMenuKeybind={PluginConfig.Instance.EffectiveReportMenuShortcut()}, " + "Reports path: " + PluginPaths.ReportsDirectory));
			CreatePollingRunner();
			((MonoBehaviour)this).StartCoroutine(PollingLoop());
			LogVerbose("Polling coroutine requested from Awake.");
		}

		private void CreatePollingRunner()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			try
			{
				if ((Object)(object)_runnerObject != (Object)null)
				{
					LogVerbose("NuageReport polling runner already exists.");
					return;
				}
				_runnerObject = new GameObject("NuageReport Polling Runner");
				_runnerObject.AddComponent<PollingRunner>();
				LogVerbose("NuageReport polling runner GameObject created.");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to create NuageReport polling runner: {arg}");
			}
		}

		private void OnEnable()
		{
			LogVerbose("NuageReport OnEnable reached.");
		}

		private void Start()
		{
			LogVerbose("NuageReport Start reached.");
		}

		private void InstallPatchFamilies()
		{
			if (_harmony == null)
			{
				return;
			}
			Type[] array = new Type[6]
			{
				typeof(RunLifecyclePatches),
				typeof(ValuableLifecyclePatches),
				typeof(CosmeticObjectLifecyclePatches),
				typeof(CartDetectionPatches),
				typeof(DamagePatches),
				typeof(InputManagerPatches)
			};
			int num = 0;
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				string name = type.Name;
				try
				{
					int num2 = CountInstalledPatches();
					_harmony.PatchAll(type);
					int num3 = CountInstalledPatches();
					num++;
					Log.LogInfo((object)$"Installed patch family: {name} ({num3 - num2} patch methods, {num3} total owned patch methods).");
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"Failed to install patch family '{name}'. Other patch families will still be attempted. {arg}");
				}
			}
			Log.LogInfo((object)$"Installed {num}/{array.Length} NuageReport patch families.");
		}

		private static void WriteStartupMarker()
		{
			try
			{
				Directory.CreateDirectory(PluginPaths.RootDirectory);
				File.WriteAllText(PluginPaths.StartupMarkerPath, string.Format("{0} {1} reached Awake at {2:O}.{3}", "NuageReport", "1.0.0", DateTimeOffset.UtcNow, Environment.NewLine) + $"JSON output enabled: {PluginConfig.Instance.EnableJsonOutput.Value}{Environment.NewLine}" + "Reports path: " + PluginPaths.ReportsDirectory + Environment.NewLine);
			}
			catch (Exception arg)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)$"Failed to write startup marker: {arg}");
				}
			}
		}

		private void OnDestroy()
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)$"NuageReport OnDestroy reached. isQuitting={_isQuitting}. Keeping patches/runner alive unless the app is quitting.");
			}
			if (!_isQuitting)
			{
				CreatePollingRunner();
				return;
			}
			try
			{
				RunRecorder recorder = Recorder;
				if (recorder != null && recorder.IsActive)
				{
					EndRunIfActive(null);
				}
				if (recorder != null)
				{
					recorder.EventRecorded -= LogRecordedEvent;
				}
				if (ReportSync != null)
				{
					ReportSync.FinalReportReceived -= HandleFinalReportReceived;
					ReportSync.LiveReportReceived -= HandleLiveReportReceived;
					ReportSync.Dispose();
				}
				if ((Object)(object)_runnerObject != (Object)null)
				{
					Object.Destroy((Object)(object)_runnerObject);
					_runnerObject = null;
				}
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)$"Failed to unpatch Harmony cleanly: {arg}");
				}
			}
		}

		private void OnApplicationQuit()
		{
			_isQuitting = true;
			LogVerbose("NuageReport OnApplicationQuit reached.");
		}

		private void Update()
		{
			TickSurvivingSystems("Plugin.Update");
		}

		private void OnGUI()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Event current = Event.current;
			if (PluginConfig.Instance.IsModActive() && current != null && (int)current.type == 4 && ReportUi != null && ReportUi.HandleKeyEvent(current.keyCode, "Plugin.OnGUI"))
			{
				current.Use();
			}
		}

		internal static void TickSurvivingSystems(string source, bool forcePoll = false)
		{
			PatchSafety.LogHookHit("NuageReport TickSurvivingSystems", source, 3);
			if (!PluginConfig.Instance.IsModActive())
			{
				ReportUi?.Deactivate();
				ReportSync?.Dispose();
				return;
			}
			ReportUi?.Tick(source);
			EnsureReportSyncSubscribedIfSafe();
			RequestReportCatchupIfDue();
			PublishLiveReportIfDue();
			SharedPollingRecorder.Tick(forcePoll);
		}

		private static void EnsureReportSyncSubscribedIfSafe()
		{
			if (ReportSync != null && PluginConfig.Instance.ShouldShareReports())
			{
				string levelName = RunLifecyclePatches.CurrentLevelName();
				if (RunLifecyclePatches.IsRecordableLevel(levelName))
				{
					ReportSync.EnsureSubscribed();
				}
			}
		}

		private static void PublishLiveReportIfDue()
		{
			if (ReportSync == null || !PluginConfig.Instance.ShouldShareLiveReport() || !Recorder.IsActive || !ReportSync.IsRoomReady || !CanRecord || !HostAuthority.IsAuthoritativeHost())
			{
				return;
			}
			float num = EffectiveLiveReportSyncIntervalSeconds();
			if (Time.unscaledTime < _nextLiveReportSyncAt)
			{
				return;
			}
			string activeRunId = Recorder.ActiveRunId;
			int eventVersion = Recorder.EventVersion;
			if (_lastLiveReportRunId == activeRunId && _lastLiveReportVersionPublished == eventVersion)
			{
				_nextLiveReportSyncAt = Time.unscaledTime + num;
				return;
			}
			_nextLiveReportSyncAt = Time.unscaledTime + num;
			ReportSnapshot reportSnapshot = CreateLiveReportSnapshot();
			if (reportSnapshot != null)
			{
				ReportSync.PublishLiveReport(reportSnapshot);
				_lastLiveReportRunId = activeRunId;
				_lastLiveReportVersionPublished = eventVersion;
			}
		}

		private static void RequestReportCatchupIfDue()
		{
			if (ReportSync == null || !PluginConfig.Instance.ShouldShareReports())
			{
				return;
			}
			string levelName = RunLifecyclePatches.CurrentLevelName();
			if (!RunLifecyclePatches.IsRecordableLevel(levelName) || !ReportSync.IsRoomReady || HostAuthority.IsAuthoritativeHost())
			{
				return;
			}
			bool flag = ReportSync.LatestFinalReport == null;
			bool flag2 = PluginConfig.Instance.IsLiveReportEnabled() && ReportSync.LatestLiveReport == null;
			if (flag || flag2)
			{
				if (_nextReportCatchupRequestAt <= 0f)
				{
					_nextReportCatchupRequestAt = Time.unscaledTime + 3f;
				}
				else if (!(Time.unscaledTime < _nextReportCatchupRequestAt))
				{
					_nextReportCatchupRequestAt = Time.unscaledTime + 2f;
					ReportSync.RequestLatestReport(flag, flag2);
				}
			}
		}

		private static float EffectiveLiveReportSyncIntervalSeconds()
		{
			return Mathf.Clamp(PluginConfig.Instance.LiveReportSyncIntervalSeconds.Value, 0.5f, 10f);
		}

		[IteratorStateMachine(typeof(<PollingLoop>d__52))]
		private IEnumerator PollingLoop()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PollingLoop>d__52(0);
		}

		private static void HandleFinalReportReceived(ReportSnapshot snapshot)
		{
			ReportUi?.SetLatestFinalReport(snapshot);
			Log.LogInfo((object)("Received NuageReport final report sync. runId=" + snapshot.RunId + " level=" + Quote(snapshot.LevelName ?? "unknown") + " modVersion=" + snapshot.ModVersion));
		}

		private static void HandleLiveReportReceived(ReportSnapshot snapshot)
		{
			ReportUi?.SetLatestLiveReport(snapshot);
		}

		private static ReportSnapshot? CreateLiveReportSnapshot()
		{
			return PatchSafety.Try("NuageReport Plugin.CreateLiveReportSnapshot", () => (!Recorder.IsActive) ? null : SnapshotFactory.CreateLiveReport(Recorder.Snapshot(), "1.0.0"), null);
		}

		internal static void StartRunIfNeeded(string? levelName)
		{
			if (CanRecord && !Recorder.IsActive)
			{
				if (!RunLifecyclePatches.IsRecordableLevel(levelName))
				{
					PatchSafety.LogHookHit("NuageReportPlugin.StartRunIfNeeded", "ignored non-run level=" + (levelName ?? "unknown"));
					return;
				}
				string text = DateTimeOffset.UtcNow.ToString("yyyyMMdd-HHmmssfff");
				_initialObjectBackfillDone = false;
				Recorder.StartRun(text, levelName);
				Log.LogInfo((object)("Run recording started. runId=" + text + " level=" + Quote(levelName ?? "unknown")));
			}
		}

		internal static bool EnsureRunActiveFromHook(string hookName)
		{
			if (!CanRecord)
			{
				PatchSafety.LogHookHit(hookName, "recording gate is closed");
				return false;
			}
			string text = RunLifecyclePatches.CurrentLevelName();
			if (!RunLifecyclePatches.IsRecordableLevel(text))
			{
				PatchSafety.LogHookHit(hookName, "ignored non-run level=" + (text ?? "unknown"));
				return false;
			}
			if (!Recorder.IsActive)
			{
				LogVerbose("No active run when " + hookName + " fired; starting a fallback run.");
				StartRunIfNeeded(text);
				BackfillInitialObjectsIfAvailable(hookName);
			}
			return Recorder.IsActive;
		}

		internal static void BackfillInitialObjectsIfAvailable(string source)
		{
			if (_initialObjectBackfillDone || !Recorder.IsActive)
			{
				return;
			}
			RoundDirector instance = RoundDirector.instance;
			if ((Object)(object)instance == (Object)null)
			{
				LogVerbose("Initial object backfill skipped from " + source + ": RoundDirector.instance is null.");
				return;
			}
			int num = 0;
			int num2 = 0;
			List<PhysGrabObject> list = GameAccess.Field<List<PhysGrabObject>>(instance, "physGrabObjects");
			if (list != null)
			{
				foreach (PhysGrabObject item in list)
				{
					if (ValuableLifecyclePatches.RecordPhysGrabObject(item))
					{
						num++;
					}
				}
			}
			List<CosmeticWorldObject> list2 = GameAccess.Field<List<CosmeticWorldObject>>(instance, "cosmeticWorldObjects");
			if (list2 != null)
			{
				foreach (CosmeticWorldObject item2 in list2)
				{
					if (CosmeticObjectLifecyclePatches.RecordCosmeticObjectExisting(item2))
					{
						num2++;
					}
				}
			}
			_initialObjectBackfillDone = true;
			LogVerbose($"Initial object backfill from {source}: valuables={num}, cosmeticObjects={num2}.");
		}

		internal static void EndRunIfActive(string? levelName)
		{
			if (!Recorder.IsActive)
			{
				return;
			}
			try
			{
				RunReport val = Recorder.EndRun(levelName);
				if (!ReportContainsRecordableLevel(val, levelName))
				{
					_initialObjectBackfillDone = false;
					Log.LogInfo((object)("Discarded non-run report. runId=" + val.RunId + " level=" + Quote(ReportLevelName(val, levelName) ?? "unknown") + "."));
				}
				else
				{
					string text = ExportJsonReportStatus(val);
					ReportSnapshot reportSnapshot = SnapshotFactory.CreateFinalReport(val, "1.0.0");
					ReportUi?.SetLatestFinalReport(reportSnapshot);
					ReportSync?.PublishFinalReport(reportSnapshot);
					_initialObjectBackfillDone = false;
					GlobalReportSummary global = val.Summary.Global;
					Log.LogInfo((object)(text + " " + $"Objects {global.ObjectsExtracted}/{global.ObjectsAtStart}, " + $"value ${global.ObjectsExtractedValue}/${global.ObjectsAtStartValue}, " + $"lost ${global.ObjectsValueLost}."));
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to end/export run report: {arg}");
			}
		}

		private static string ExportJsonReportStatus(RunReport report)
		{
			if (!PluginConfig.Instance.ShouldWriteJsonOutput())
			{
				return "Run report completed with JSON output disabled.";
			}
			try
			{
				string text = Exporter.Export(report);
				return "Run report exported: " + text + ".";
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to export JSON run report: {arg}");
				return "Run report completed, but JSON export failed.";
			}
		}

		private static bool ReportContainsRecordableLevel(RunReport report, string? fallbackLevelName)
		{
			if (RunLifecyclePatches.IsRecordableLevel(fallbackLevelName))
			{
				return true;
			}
			foreach (RunEvent @event in report.Events)
			{
				if (RunLifecyclePatches.IsRecordableLevel(@event.LevelName))
				{
					return true;
				}
			}
			return false;
		}

		private static string? ReportLevelName(RunReport report, string? fallbackLevelName)
		{
			if (!string.IsNullOrWhiteSpace(fallbackLevelName))
			{
				return fallbackLevelName;
			}
			foreach (RunEvent @event in report.Events)
			{
				if (!string.IsNullOrWhiteSpace(@event.LevelName))
				{
					return @event.LevelName;
				}
			}
			return null;
		}

		private static void LogRecordedEvent(RunEvent runEvent)
		{
			RunEvent runEvent2 = runEvent;
			if (PluginConfig.Instance.LogRecordedEvents != null && !PluginConfig.Instance.LogRecordedEvents.Value)
			{
				return;
			}
			PatchSafety.Run("Recorder.EventRecordedLog", delegate
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append("Recorded ").Append(runEvent2.Type).Append(" [")
					.Append(runEvent2.Confidence)
					.Append(']');
				if (!string.IsNullOrWhiteSpace(runEvent2.LevelName))
				{
					stringBuilder.Append(" level=").Append(Quote(runEvent2.LevelName));
				}
				if (runEvent2.Valuable != null)
				{
					stringBuilder.Append(" valuable=").Append(Quote(runEvent2.Valuable.Name)).Append(" valuableId=")
						.Append(runEvent2.Valuable.RuntimeId);
					if (runEvent2.Valuable.ValueAtEvent.HasValue)
					{
						stringBuilder.Append(" value=$").Append(runEvent2.Valuable.ValueAtEvent.Value);
					}
					if (runEvent2.Valuable.WeightAtEvent.HasValue)
					{
						stringBuilder.Append(" weight=").Append(runEvent2.Valuable.WeightAtEvent.Value.ToString("0.###"));
					}
				}
				if (runEvent2.Player != null)
				{
					stringBuilder.Append(" player=").Append(Quote(runEvent2.Player.DisplayName)).Append(" playerId=")
						.Append(runEvent2.Player.Id);
				}
				if (runEvent2.DamageAmount.HasValue)
				{
					stringBuilder.Append(" damage=").Append(runEvent2.DamageAmount.Value);
				}
				if (runEvent2.DeltaValue.HasValue)
				{
					stringBuilder.Append(" deltaValue=").Append(runEvent2.DeltaValue.Value);
				}
				foreach (KeyValuePair<string, string> tag in runEvent2.Tags)
				{
					stringBuilder.Append(' ').Append(tag.Key).Append('=')
						.Append(Quote(tag.Value));
				}
				if (!string.IsNullOrWhiteSpace(runEvent2.Message))
				{
					stringBuilder.Append(" note=").Append(Quote(runEvent2.Message));
				}
				Log.LogInfo((object)stringBuilder.ToString());
			});
		}

		private static string Quote(string value)
		{
			return "\"" + value.Replace("\"", "'") + "\"";
		}

		internal static void LogVerbose(string message)
		{
			if (PluginConfig.Instance.ShouldLogVerbose())
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}

		private static int CountInstalledPatches()
		{
			int num = 0;
			foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods())
			{
				Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod);
				if (patchInfo != null)
				{
					num += CountOwnedPatches(patchInfo.Prefixes);
					num += CountOwnedPatches(patchInfo.Postfixes);
					num += CountOwnedPatches(patchInfo.Transpilers);
					num += CountOwnedPatches(patchInfo.Finalizers);
				}
			}
			return num;
		}

		private static int CountOwnedPatches(IEnumerable<Patch> patches)
		{
			int num = 0;
			foreach (Patch patch in patches)
			{
				if (patch.owner == "com.theohay.nuagereport")
				{
					num++;
				}
			}
			return num;
		}
	}
}
namespace NuageReport.Plugin.UI
{
	internal sealed class ReportOverlayPage : MonoBehaviour
	{
		private sealed class Metric
		{
			public string Label { get; }

			public string Value { get; }

			public string Detail { get; }

			public Metric(string label, string value, string detail)
			{
				Label = label;
				Value = value;
				Detail = detail;
			}
		}

		private sealed class ButtonVisual
		{
			public Button Button { get; }

			public Image Background { get; }

			public TextMeshProUGUI Label { get; }

			public ButtonVisual(Button button, Image background, TextMeshProUGUI label)
			{
				Button = button;
				Background = background;
				Label = label;
			}
		}

		private const float FallbackFontSize = 18f;

		private const float HeaderHeight = 64f;

		private const float FooterHeight = 42f;

		private const float StackSpacing = 12f;

		private RectTransform? _contentRect;

		private RectTransform? _viewportRect;

		private RectTransform? _scrollTrackRect;

		private RectTransform? _scrollThumbRect;

		private Image? _scrollTrackImage;

		private Image? _scrollThumbImage;

		private TextMeshProUGUI? _titleText;

		private TextMeshProUGUI? _footerText;

		private TextMeshProUGUI? _fallbackText;

		private LayoutElement? _fallbackLayout;

		private ButtonVisual? _summaryTab;

		private ButtonVisual? _liveTab;

		private Action<ReportUiTab>? _tabSelected;

		private Action? _closed;

		private bool _isClosing;

		private bool _layoutDirty;

		private float _scrollOffset;

		private float _maxScrollOffset;

		private CursorLockMode _previousCursorLockState;

		private bool _previousCursorVisible;

		private ReportUiTab _selectedTab;

		private bool _summaryEnabled;

		private bool _liveEnabled;

		public static ReportOverlayPage Create(ReportUiTab selectedTab, bool summaryEnabled, bool liveEnabled, ReportSnapshot? snapshot, string fallbackText, string footer, Action<ReportUiTab> tabSelected, Action closed)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("NuageReport Report Overlay");
			try
			{
				Object.DontDestroyOnLoad((Object)(object)val);
				Canvas val2 = val.AddComponent<Canvas>();
				val2.renderMode = (RenderMode)0;
				val2.sortingOrder = 6000;
				CanvasScaler val3 = val.AddComponent<CanvasScaler>();
				val3.uiScaleMode = (ScaleMode)1;
				val3.referenceResolution = new Vector2(1920f, 1080f);
				val3.matchWidthOrHeight = 0.5f;
				val.AddComponent<GraphicRaycaster>();
				CanvasGroup val4 = val.AddComponent<CanvasGroup>();
				val4.alpha = 1f;
				val4.interactable = true;
				val4.blocksRaycasts = true;
				ReportOverlayPage reportOverlayPage = val.AddComponent<ReportOverlayPage>();
				reportOverlayPage._tabSelected = tabSelected;
				reportOverlayPage._closed = closed;
				reportOverlayPage.CaptureCursorState();
				reportOverlayPage.Build(val.transform);
				reportOverlayPage.SetContent(selectedTab, summaryEnabled, liveEnabled, snapshot, fallbackText, footer);
				return reportOverlayPage;
			}
			catch
			{
				Object.Destroy((Object)(object)val);
				throw;
			}
		}

		public void SetContent(ReportUiTab selectedTab, bool summaryEnabled, bool liveEnabled, ReportSnapshot? snapshot, string fallbackText, string footer)
		{
			bool flag = _selectedTab != selectedTab;
			_selectedTab = selectedTab;
			_summaryEnabled = summaryEnabled;
			_liveEnabled = liveEnabled;
			_fallbackText = null;
			_fallbackLayout = null;
			if ((Object)(object)_titleText != (Object)null)
			{
				((TMP_Text)_titleText).text = "NuageReport";
			}
			if ((Object)(object)_footerText != (Object)null)
			{
				((TMP_Text)_footerText).text = footer;
			}
			ClearContent();
			if (snapshot != null && ReportUiData.TryParse(snapshot.JsonPayload, out ReportUiData data) && data != null)
			{
				BuildStructuredContent(snapshot, data);
			}
			else
			{
				BuildFallbackContent(string.IsNullOrWhiteSpace(fallbackText) ? "No report data." : fallbackText);
			}
			if (flag)
			{
				_scrollOffset = 0f;
			}
			UpdateTabVisuals();
			_layoutDirty = true;
		}

		public void Close()
		{
			if (!_isClosing)
			{
				_isClosing = true;
				_closed?.Invoke();
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}

		private void Build(Transform root)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_006d: 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)
			//IL_00ba: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Expected O, but got Unknown
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Expected O, but got Unknown
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0375: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Expected O, but got Unknown
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_0399: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Expected O, but got Unknown
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			//IL_042b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0445: Unknown result type (might be due to invalid IL or missing references)
			//IL_045f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_047d: Expected O, but got Unknown
			//IL_04ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_050c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0559: Unknown result type (might be due to invalid IL or missing references)
			//IL_0563: Expected O, but got Unknown
			//IL_0579: Unknown result type (might be due to invalid IL or missing references)
			//IL_057e: Unknown result type (might be due to invalid IL or missing references)
			//IL_058d: Unknown result type (might be due to invalid IL or missing references)
			//IL_059c: Unknown result type (might be due to invalid IL or missing references)
			//IL_05dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0601: Unknown result type (might be due to invalid IL or missing references)
			//IL_0606: Unknown result type (might be due to invalid IL or missing references)
			//IL_060b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0634: Unknown result type (might be due to invalid IL or missing references)
			//IL_064e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0668: Unknown result type (might be due to invalid IL or missing references)
			//IL_0682: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0712: Unknown result type (might be due to invalid IL or missing references)
			//IL_074c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0758: Unknown result type (might be due to invalid IL or missing references)
			//IL_076e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0784: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a8: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreatePanel(root, "Background", Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero);
			((Graphic)val.GetComponent<Image>()).color = new Color(0.01f, 0.012f, 0.014f, 0.88f);
			GameObject val2 = CreatePanel(root, "Window", Vector2.zero, Vector2.one, new Vector2(64f, 54f), new Vector2(-64f, -54f));
			Image component = val2.GetComponent<Image>();
			((Graphic)component).color = new Color(0.035f, 0.043f, 0.048f, 0.96f);
			Outline val3 = val2.AddComponent<Outline>();
			((Shadow)val3).effectColor = new Color(0.23f, 0.34f, 0.37f, 0.62f);
			((Shadow)val3).effectDistance = new Vector2(1f, -1f);
			GameObject val4 = CreatePanel(val2.transform, "Top Bar", new Vector2(0f, 1f), Vector2.one, new Vector2(0f, -64f), Vector2.zero);
			((Graphic)val4.GetComponent<Image>()).color = new Color(0.055f, 0.067f, 0.072f, 0.98f);
			_titleText = CreateText(val4.transform, "Title", 22f, (TextAlignmentOptions)4097);
			RectTransform rectTransform = ((TMP_Text)_titleText).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0f);
			rectTransform.anchorMax = new Vector2(0f, 1f);
			rectTransform.pivot = new Vector2(0f, 0.5f);
			rectTransform.anchoredPosition = new Vector2(24f, 0f);
			rectTransform.sizeDelta = new Vector2(250f, 0f);
			((TMP_Text)_titleText).fontStyle = (FontStyles)1;
			((Graphic)_titleText).color = new Color(0.93f, 0.98f, 1f, 1f);
			((TMP_Text)_titleText).enableWordWrapping = false;
			((TMP_Text)_titleText).overflowMode = (TextOverflowModes)1;
			_summaryTab = CreateButton(val4.transform, "Summary", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(318f, 0f), new Vector2(126f, 38f), 16f);
			((UnityEvent)_summaryTab.Button.onClick).AddListener((UnityAction)delegate
			{
				_tabSelected?.Invoke(ReportUiTab.Summary);
			});
			_liveTab = CreateButton(val4.transform, "Live", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(450f, 0f), new Vector2(104f, 38f), 16f);
			((UnityEvent)_liveTab.Button.onClick).AddListener((UnityAction)delegate
			{
				_tabSelected?.Invoke(ReportUiTab.Live);
			});
			ButtonVisual buttonVisual = CreateButton(val4.transform, "X", new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-38f, 0f), new Vector2(44f, 38f), 18f);
			((UnityEvent)buttonVisual.Button.onClick).AddListener(new UnityAction(Close));
			ApplyButtonStyle(buttonVisual, enabled: true, selected: false, compact: true);
			GameObject val5 = CreatePanel(val2.transform, "Content", Vector2.zero, Vector2.one, new Vector2(24f, 60f), new Vector2(-24f, -82f));
			((Graphic)val5.GetComponent<Image>()).color = new Color(0.018f, 0.023f, 0.026f, 0.92f);
			GameObject val6 = new GameObject("Viewport");
			val6.transform.SetParent(val5.transform, false);
			_viewportRect = val6.AddComponent<RectTransform>();
			_viewportRect.anchorMin = Vector2.zero;
			_viewportRect.anchorMax = Vector2.one;
			_viewportRect.offsetMin = new Vector2(22f, 16f);
			_viewportRect.offsetMax = new Vector2(-38f, -16f);
			val6.AddComponent<RectMask2D>();
			GameObject val7 = new GameObject("Content Stack");
			val7.transform.SetParent(val6.transform, false);
			_contentRect = val7.AddComponent<RectTransform>();
			_contentRect.anchorMin = new Vector2(0f, 1f);
			_contentRect.anchorMax = new Vector2(1f, 1f);
			_contentRect.pivot = new Vector2(0f, 1f);
			_contentRect.anchoredPosition = Vector2.zero;
			_contentRect.sizeDelta = new Vector2(0f, 800f);
			VerticalLayoutGroup val8 = val7.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)val8).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)val8).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)val8).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)val8).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)val8).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)val8).spacing = 12f;
			((LayoutGroup)val8).padding = new RectOffset(0, 0, 0, 0);
			GameObject val9 = CreatePanel(val5.transform, "Scroll Track", new Vector2(1f, 0f), Vector2.one, new Vector2(-17f, 18f), new Vector2(-12f, -18f));
			_scrollTrackRect = val9.GetComponent<RectTransform>();
			_scrollTrackImage = val9.GetComponent<Image>();
			((Graphic)_scrollTrackImage).color = new Color(0.16f, 0.21f, 0.23f, 0.55f);
			GameObject val10 = CreatePanel(val9.transform, "Scroll Thumb", new Vector2(0f, 1f), Vector2.one, Vector2.zero, Vector2.zero);
			_scrollThumbRect = val10.GetComponent<RectTransform>();
			_scrollThumbRect.anchorMin = new Vector2(0f, 1f);
			_scrollThumbRect.anchorMax = new Vector2(1f, 1f);
			_scrollThumbRect.pivot = new Vector2(0.5f, 1f);
			_scrollThumbRect.sizeDelta = new Vector2(0f, 80f);
			_scrollThumbImage = val10.GetComponent<Image>();
			((Graphic)_scrollThumbImage).color = new Color(0.45f, 0.72f, 0.74f, 0.9f);
			GameObject val11 = CreatePanel(val2.transform, "Footer", Vector2.zero, new Vector2(1f, 0f), Vector2.zero, new Vector2(0f, 42f));
			((Graphic)val11.GetComponent<Image>()).color = new Color(0.055f, 0.067f, 0.072f, 0.98f);
			_footerText = CreateText(val11.transform, "Footer Text", 13f, (TextAlignmentOptions)514);
			RectTransform rectTransform2 = ((TMP_Text)_footerText).rectTransform;
			rectTransform2.anchorMin = Vector2.zero;
			rectTransform2.anchorMax = Vector2.one;
			rectTransform2.offsetMin = new Vector2(24f, 0f);
			rectTransform2.offsetMax = new Vector2(-24f, 0f);
			((Graphic)_footerText).color = new Color(0.66f, 0.78f, 0.8f, 1f);
			((TMP_Text)_footerText).enableWordWrapping = false;
			((TMP_Text)_footerText).overflowMode = (TextOverflowModes)1;
		}

		private void BuildStructuredContent(ReportSnapshot snapshot, ReportUiData data)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_contentRect == (Object)null || data.global == null)
			{
				return;
			}
			ReportUiGlobal global = data.global;
			ReportUiPlayer[] array = data.players ?? Array.Empty<ReportUiPlayer>();
			ReportUiCosmeticObjects cosmeticObjects = data.cosmeticObjects;
			string title = ((snapshot.ReportKind == ReportKind.Live) ? "Live Report" : "Post Game Summary");
			string state = ((snapshot.ReportKind == ReportKind.Live) ? "In progress" : "Complete");
			CreateHero(title, state, snapshot.LevelName);
			GameObject val = CreateHorizontalGroup("KPI Row", 94f, 10f);
			AddStatTile(val.transform, "Extracted value", Money(global.objectsExtractedValue), CountDetail(global.objectsExtracted, "object"), new Color(0.22f, 0.64f, 0.42f, 1f));
			AddStatTile(val.transform, "Value lost", Money(global.objectsValueLost), CountDetail(global.objectsDestroyed, "destroyed"), new Color(0.74f, 0.28f, 0.25f, 1f));
			AddStatTile(val.transform, "Detected", Percent(global.objectsDetectedPercent), global.objectsDetected + " / " + global.objectsAtStart, new Color(0.33f, 0.56f, 0.86f, 1f));
			AddStatTile(val.transform, "Players hurt", Number(global.playerDamageTaken), "total damage", new Color(0.77f, 0.56f, 0.26f, 1f));
			CreateSectionTitle("Valuables");
			CreateMetricRows(new Metric("Map value", Money(global.objectsAtStartValue), CountDetail(global.objectsAtStart, "object")), new Metric("Extracted", Money(global.objectsExtractedValue), CountDetail(global.objectsExtracted, "object")), new Metric("Missed", Number(global.objectsMissed), "not detected"), new Metric("Damaged", Number(global.objectsDamaged), "valuable objects"), new Metric("Destroyed", Number(global.objectsDestroyed), Money(global.objectsValueLost) + " lost"), new Metric("Detection", Percent(global.objectsDetectedPercent), global.objectsDetected + " found"));
			CreateSectionTitle("Extras and Combat");
			CreateMetricRows(new Metric("Monster loot", Money(global.orbsExtractedValue), CountDetail(global.orbsCollected, "orb")), new Metric("Loot lost", Money(global.orbsLostValue), CountDetail(global.orbsDestroyed, "orb")), new Metric("Cosmetics found", Number(global.cosmeticObjectsFound), CountDetail(global.cosmeticObjectsExisting, "known")), new Metric("Monsters killed", Number(global.monstersKilled), Number(global.monsterDamageTaken) + " damage"), new Metric("Player damage", Number(global.playerDamageTaken), "taken by crew"));
			CreateSectionTitle("Players");
			if (array.Length == 0)
			{
				CreateEmptyState("No player-specific stats recorded yet.");
			}
			else
			{
				ReportUiPlayer[] array2 = array;
				foreach (ReportUiPlayer player in array2)
				{
					CreatePlayerRow(player);
				}
			}
			int num = cosmeticObjects?.existing?.Length ?? global.cosmeticObjectsExisting;
			int num2 = cosmeticObjects?.found?.Length ?? global.cosmeticObjectsFound;
			if (num > 0 || num2 > 0)
			{
				CreateSectionTitle("Cosmetic Objects");
				CreateMetricRows(new Metric("Existing", Number(num), "seen by recorder"), new Metric("Found", Number(num2), "extracted or found"));
			}
		}

		private void BuildFallbackContent(string text)
		{
			if ((Object)(object)_contentRect == (Object)null)
			{
				return;
			}
			string text2 = text.Replace("\r\n", "\n");
			CreateHero("Text Report", "Compatibility fallback", null);
			string[] array = text2.Split('\n');
			if (array.Length <= 1)
			{
				CreateEmptyState(text2);
				return;
			}
			string title = "Report";
			List<string> list = new List<string>();
			for (int i = 0; i < array.Length; i++)
			{
				string text3 = array[i].TrimEnd();
				if (IsLegacySectionHeader(array, i))
				{
					FlushFallbackSection(title, list);
					list.Clear();
					title = TitleCase(text3);
					i++;
				}
				else if (!string.IsNullOrWhiteSpace(text3))
				{
					list.Add(text3);
				}
			}
			FlushFallbackSection(title, list);
		}

		private void FlushFallbackSection(string title, List<string> lines)
		{
			//IL_004d: 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)
			//IL_009e: Expected O, but got Unknown
			if (lines.Count == 0)
			{
				return;
			}
			CreateSectionTitle(title);
			GameObject val = CreateContentPanel(title + " Fallback Rows", Math.Max(54f, 16f + (float)lines.Count * 34f), new Color(0.03f, 0.04f, 0.045f, 0.95f));
			VerticalLayoutGroup val2 = val.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)val2).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = false;
			((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)val2).spacing = 0f;
			((LayoutGroup)val2).padding = new RectOffset(14, 14, 8, 8);
			foreach (string line in lines)
			{
				AddFallbackLine(val.transform, line);
			}
		}

		private void AddFallbackLine(Transform parent, string line)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Fallback Row");
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			LayoutElement val3 = val.AddComponent<LayoutElement>();
			val3.preferredHeight = 34f;
			val3.minHeight = 34f;
			int num = line.IndexOf(':');
			if (num <= 0)
			{
				TextMeshProUGUI val4 = CreateText(val.transform, "Text", 15f, (TextAlignmentOptions)4097);
				((TMP_Text)val4).rectTransform.offsetMin = Vector2.zero;
				((TMP_Text)val4).rectTransform.offsetMax = Vector2.zero;
				((TMP_Text)val4).text = line.Trim();
				((TMP_Text)val4).fontStyle = (FontStyles)((!line.StartsWith("  ", StringComparison.Ordinal)) ? 1 : 0);
				((Graphic)val4).color = new Color(0.86f, 0.94f, 0.95f, 1f);
				((TMP_Text)val4).enableWordWrapping = false;
				((TMP_Text)val4).overflowMode = (TextOverflowModes)1;
			}
			else
			{
				string text = line.Substring(0, num).Trim();
				string text2 = line.Substring(num + 1).Trim();
				TextMeshProUGUI val5 = CreateText(val.transform, "Label", 13f, (TextAlignmentOptions)4097);
				SetAnchors(((TMP_Text)val5).rectTransform, Vector2.zero, new Vector2(0.48f, 1f), Vector2.zero, new Vector2(-8f, 0f));
				((TMP_Text)val5).text = text;
				((Graphic)val5).color = new Color(0.62f, 0.72f, 0.74f, 1f);
				((TMP_Text)val5).enableWordWrapping = false;
				((TMP_Text)val5).overflowMode = (TextOverflowModes)1;
				TextMeshProUGUI val6 = CreateText(val.transform, "Value", 15f, (TextAlignmentOptions)4100);
				SetAnchors(((TMP_Text)val6).rectTransform, new Vector2(0.48f, 0f), Vector2.one, new Vector2(8f, 0f), Vector2.zero);
				((TMP_Text)val6).text = text2;
				((TMP_Text)val6).fontStyle = (FontStyles)1;
				((Graphic)val6).color = new Color(0.92f, 0.97f, 0.98f, 1f);
				((TMP_Text)val6).enableWordWrapping = false;
				((TMP_Text)val6).overflowMode = (TextOverflowModes)1;
			}
		}

		private static bool IsLegacySectionHeader(string[] lines, int index)
		{
			if (index + 1 >= lines.Length)
			{
				return false;
			}
			string text = lines[index].Trim();
			string text2 = lines[index + 1].Trim();
			if (text.Length > 0 && text.ToUpperInvariant() == text && text2.Length >= 8)
			{
				return text2.Trim('-').Length == 0;
			}
			return false;
		}

		private static string TitleCase(string value)
		{
			string str = value.ToLowerInvariant();
			return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(str);
		}

		private void CreateHero(string title, string state, string? levelName)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateContentPanel("Report Header", 64f, new Color(0.045f, 0.058f, 0.064f, 0.98f));
			TextMeshProUGUI val2 = CreateText(val.transform, "Report Title", 22f, (TextAlignmentOptions)4097);
			RectTransform rectTransform = ((TMP_Text)val2).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0f);
			rectTransform.anchorMax = new Vector2(0.55f, 1f);
			rectTransform.offsetMin = new Vector2(18f, 0f);
			rectTransform.offsetMax = new Vector2(-8f, 0f);
			((TMP_Text)val2).text = title;
			((TMP_Text)val2).fontStyle = (FontStyles)1;
			((Graphic)val2).color = new Color(0.95f, 0.99f, 1f, 1f);
			((TMP_Text)val2).enableWordWrapping = false;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)1;
			TextMeshProUGUI val3 = CreateText(val.transform, "Report Meta", 15f, (TextAlignmentOptions)4100);
			RectTransform rectTransform2 = ((TMP_Text)val3).rectTransform;
			rectTransform2.anchorMin = new Vector2(0.55f, 0f);
			rectTransform2.anchorMax = Vector2.one;
			rectTransform2.offsetMin = new Vector2(8f, 0f);
			rectTransform2.offsetMax = new Vector2(-18f, 0f);
			((TMP_Text)val3).text = (string.IsNullOrWhiteSpace(levelName) ? state : (state + " | " + levelName));
			((Graphic)val3).color = new Color(0.62f, 0.78f, 0.8f, 1f);
			((TMP_Text)val3).enableWordWrapping = false;
			((TMP_Text)val3).overflowMode = (TextOverflowModes)1;
		}

		private void CreateSectionTitle(string title)
		{
			//IL_0025: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateContentPanel(title + " Title", 30f, new Color(0f, 0f, 0f, 0f));
			Image component = val.GetComponent<Image>();
			((Behaviour)component).enabled = false;
			TextMeshProUGUI val2 = CreateText(val.transform, "Title", 16f, (TextAlignmentOptions)4097);
			((TMP_Text)val2).rectTransform.offsetMin = new Vector2(2f, 0f);
			((TMP_Text)val2).rectTransform.offsetMax = Vector2.zero;
			((TMP_Text)val2).text = title.ToUpperInvariant();
			((TMP_Text)val2).fontStyle = (FontStyles)1;
			((Graphic)val2).color = new Color(0.5f, 0.72f, 0.74f, 1f);
			((TMP_Text)val2).characterSpacing = 0f;
			((TMP_Text)val2).enableWordWrapping = false;
		}

		private GameObject CreateHorizontalGroup(string name, float height, float spacing)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateContentPanel(name, height, new Color(0f, 0f, 0f, 0f));
			((Behaviour)val.GetComponent<Image>()).enabled = false;
			HorizontalLayoutGroup val2 = val.AddComponent<HorizontalLayoutGroup>();
			((LayoutGroup)val2).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = true;
			((HorizontalOrVerticalLayoutGroup)val2).spacing = spacing;
			return val;
		}

		private void AddStatTile(Transform parent, string label, string value, string detail, Color accent)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: 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_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateChildPanel(parent, label + " Tile", new Color(0.03f, 0.04f, 0.045f, 0.98f));
			LayoutElement val2 = val.AddComponent<LayoutElement>();
			val2.flexibleWidth = 1f;
			val2.preferredHeight = 94f;
			GameObject val3 = CreatePanel(val.transform, "Accent", new Vector2(0f, 0f), new Vector2(0f, 1f), Vector2.zero, new Vector2(4f, 0f));
			((Graphic)val3.GetComponent<Image>()).color = accent;
			TextMeshProUGUI val4 = CreateText(val.transform, "Label", 13f, (TextAlignmentOptions)257);
			SetOffsets(((TMP_Text)val4).rectTransform, new Vector2(16f, 52f), new Vector2(-12f, -10f));
			((TMP_Text)val4).text = label;
			((Graphic)val4).color = new Color(0.62f, 0.72f, 0.74f, 1f);
			((TMP_Text)val4).enableWordWrapping = false;
			((TMP_Text)val4).overflowMode = (TextOverflowModes)1;
			TextMeshProUGUI val5 = CreateText(val.transform, "Value", 28f, (TextAlignmentOptions)4097);
			SetOffsets(((TMP_Text)val5).rectTransform, new Vector2(16f, 18f), new Vector2(-12f, -24f));
			((TMP_Text)val5).text = value;
			((TMP_Text)val5).fontStyle = (FontStyles)1;
			((Graphic)val5).color = new Color(0.95f, 0.99f, 1f, 1f);
			((TMP_Text)val5).enableWordWrapping = false;
			((TMP_Text)val5).overflowMode = (TextOverflowModes)1;
			TextMeshProUGUI val6 = CreateText(val.transform, "Detail", 12f, (TextAlignmentOptions)1025);
			SetOffsets(((TMP_Text)val6).rectTransform, new Vector2(16f, 6f), new Vector2(-12f, -62f));
			((TMP_Text)val6).text = detail;
			((Graphic)val6).color = new Color(0.55f, 0.64f, 0.66f, 1f);
			((TMP_Text)val6).enableWordWrapping = false;
			((TMP_Text)val6).overflowMode = (TextOverflowModes)1;
		}

		private void CreateMetricRows(params Metric[] metrics)
		{
			for (int i = 0; i < metrics.Length; i += 2)
			{
				GameObject val = CreateHorizontalGroup("Metric Row", 54f, 10f);
				AddMetricCell(val.transform, metrics[i]);
				if (i + 1 < metrics.Length)
				{
					AddMetricCell(val.transform, metrics[i + 1]);
				}
				else
				{
					AddMetricCell(val.transform, new Metric(string.Empty, string.Empty, string.Empty), visible: false);
				}
			}
		}

		private void AddMetricCell(Transform parent, Metric metric, bool visible = true)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: 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_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateChildPanel(parent, "Metric", visible ? new Color(0.028f, 0.035f, 0.039f, 0.96f) : new Color(0f, 0f, 0f, 0f));
			((Behaviour)val.GetComponent<Image>()).enabled = visible;
			LayoutElement val2 = val.AddComponent<LayoutElement>();
			val2.flexibleWidth = 1f;
			val2.preferredHeight = 54f;
			if (visible)
			{
				TextMeshProUGUI val3 = CreateText(val.transform, "Label", 13f, (TextAlignmentOptions)4097);
				SetOffsets(((TMP_Text)val3).rectTransform, new Vector2(14f, 0f), new Vector2(-260f, 0f));
				((TMP_Text)val3).text = metric.Label;
				((Graphic)val3).color = new Color(0.64f, 0.74f, 0.76f, 1f);
				((TMP_Text)val3).enableWordWrapping = false;
				((TMP_Text)val3).overflowMode = (TextOverflowModes)1;
				TextMeshProUGUI val4 = CreateText(val.transform, "Value", 18f, (TextAlignmentOptions)4100);
				SetOffsets(((TMP_Text)val4).rectTransform, new Vector2(145f, 0f), new Vector2(-130f, 0f));
				((TMP_Text)val4).text = metric.Value;
				((TMP_Text)val4).fontStyle = (FontStyles)1;
				((Graphic)val4).color = new Color(0.93f, 0.97f, 0.98f, 1f);
				((TMP_Text)val4).enableWordWrapping = false;
				((TMP_Text)val4).overflowMode = (TextOverflowModes)1;
				TextMeshProUGUI val5 = CreateText(val.transform, "Detail", 12f, (TextAlignmentOptions)4100);
				SetOffsets(((TMP_Text)val5).rectTransform, new Vector2(270f, 0f), new Vector2(-14f, 0f));
				((TMP_Text)val5).text = metric.Detail;
				((Graphic)val5).color = new Color(0.5f, 0.6f, 0.62f, 1f);
				((TMP_Text)val5).enableWordWrapping = false;
				((TMP_Text)val5).overflowMode = (TextOverflowModes)1;
			}
		}

		private void CreatePlayerRow(ReportUiPlayer player)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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_0143: 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_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			string text = (string.IsNullOrWhiteSpace(player.displayName) ? player.playerId : player.displayName);
			GameObject val = CreateContentPanel("Player Row", 82f, new Color(0.03f, 0.039f, 0.043f, 0.98f));
			TextMeshProUGUI val2 = CreateText(val.transform, "Name", 18f, (TextAlignmentOptions)4097);
			SetAnchors(((TMP_Text)val2).rectTransform, new Vector2(0f, 0f), new Vector2(0.28f, 1f), new Vector2(16f, 0f), new Vector2(-8f, 0f));
			((TMP_Text)val2).text = text;
			((TMP_Text)val2).fontStyle = (FontStyles)1;
			((Graphic)val2).color = new Color(0.95f, 0.99f, 1f, 1f);
			((TMP_Text)val2).enableWordWrapping = false;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)1;
			AddPlayerStat(val.transform, new Vector2(0.3f, 0f), new Vector2(0.48f, 1f), "Cart/cashout", CountAndMoney(player.objectsPutInCartOrCashout, player.objectsPutInCartOrCashoutValue));
			AddPlayerStat(val.transform, new Vector2(0.5f, 0f), new Vector2(0.66f, 1f), "Extracted weight", Weight(player.extractedObjectWeight));
			AddPlayerStat(val.transform, new Vector2(0.68f, 0f), new Vector2(0.84f, 1f), "Detected", CountAndMoney(player.objectsDetected, player.objectsDetectedValue));
			AddPlayerStat(val.transform, new Vector2(0.86f, 0f), new Vector2(1f, 1f), "Damage taken", Number(player.damageTaken));
		}

		private void AddPlayerStat(Transform parent, Vector2 anchorMin, Vector2 anchorMax, string label, string value)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_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_006d: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(label);
			val.transform.SetParent(parent, false);
			RectTransform rect = val.AddComponent<RectTransform>();
			SetAnchors(rect, anchorMin, anchorMax, Vector2.zero, Vector2.zero);
			TextMeshProUGUI val2 = CreateText(val.transform, "Label", 11f, (TextAlignmentOptions)1025);
			SetAnchors(((TMP_Text)val2).rectTransform, new Vector2(0f, 0.5f), Vector2.one, new Vector2(0f, 0f), new Vector2(-8f, -8f));
			((TMP_Text)val2).text = label;
			((Graphic)val2).color = new Color(0.54f, 0.64f, 0.66f, 1f);
			((TMP_Text)val2).enableWordWrapping = false;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)1;
			TextMeshProUGUI val3 = CreateText(val.transform, "Value", 17f, (TextAlignmentOptions)257);
			SetAnchors(((TMP_Text)val3).rectTransform, Vector2.zero, new Vector2(1f, 0.55f), new Vector2(0f, 8f), new Vector2(-8f, 0f));
			((TMP_Text)val3).text = value;
			((TMP_Text)val3).fontStyle = (FontStyles)1;
			((Graphic)val3).color = new Color(0.87f, 0.94f, 0.95f, 1f);
			((TMP_Text)val3).enableWordWrapping = false;
			((TMP_Text)val3).overflowMode = (TextOverflowModes)1;
		}

		private void CreateEmptyState(string text)
		{
			//IL_001f: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateContentPanel("Empty State", 58f, new Color(0.028f, 0.035f, 0.039f, 0.96f));
			TextMeshProUGUI val2 = CreateText(val.transform, "Text", 15f, (TextAlignmentOptions)4097);
			((TMP_Text)val2).rectTransform.offsetMin = new Vector2(16f, 0f);
			((TMP_Text)val2).rectTransform.offsetMax = new Vector2(-16f, 0f);
			((TMP_Text)val2).text = text;
			((Graphic)val2).color = new Color(0.62f, 0.72f, 0.74f, 1f);
			((TMP_Text)val2).enableWordWrapping = false;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)1;
		}

		private void LateUpdate()
		{
			KeepModalInputState();
			if (_layoutDirty)
			{
				RefreshContentHeight();
				_layoutDirty = false;
			}
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			KeepModalInputState();
			float y = Input.mouseScrollDelta.y;
			if (Mathf.Abs(y) > 0.01f)
			{
				_scrollOffset = Mathf.Clamp(_scrollOffset - y * 58f, 0f, _maxScrollOffset);
				ApplyScrollOffset();
			}
		}

		private void RefreshContentHeight()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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)
			if (!((Object)(object)_contentRect == (Object)null) && !((Object)(object)_viewportRect == (Object)null))
			{
				Canvas.ForceUpdateCanvases();
				Rect rect = _viewportRect.rect;
				float num = Mathf.Max(300f, ((Rect)(ref rect)).height);
				if ((Object)(object)_fallbackText != (Object)null && (Object)(object)_fallbackLayout != (Object)null)
				{
					rect = _viewportRect.rect;
					float num2 = Mathf.Max(300f, ((Rect)(ref rect)).width - 36f);
					Vector2 preferredValues = ((TMP_Text)_fallbackText).GetPreferredValues(((TMP_Text)_fallbackText).text, num2, 0f);
					_fallbackLayout.preferredHeight = Mathf.Max(num, preferredValues.y + 36f);
				}
				LayoutRebuilder.ForceRebuildLayoutImmediate(_contentRect);
				float num3 = Mathf.Max(num, LayoutUtility.GetPreferredHeight(_contentRect));
				_contentRect.sizeDelta = new Vector2(0f, num3);
				_maxScrollOffset = Mathf.Max(0f, num3 - num);
				_scrollOffset = Mathf.Clamp(_scrollOffset, 0f, _maxScrollOffset);
				ApplyScrollOffset();
			}
		}

		private void ApplyScrollOffset()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_contentRect == (Object)null))
			{
				_contentRect.anchoredPosition = new Vector2(0f, _scrollOffset);
				UpdateScrollThumb();
			}
		}

		private void UpdateScrollThumb()
		{
			//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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_viewportRect == (Object)null) && !((Object)(object)_scrollTrackRect == (Object)null) && !((Object)(object)_scrollThumbRect == (Object)null) && !((Object)(object)_scrollTrackImage == (Object)null) && !((Object)(object)_scrollThumbImage == (Object)null))
			{
				bool flag = _maxScrollOffset > 1f;
				((Behaviour)_scrollTrackImage).enabled = flag;
				((Behaviour)_scrollThumbImage).enabled = flag;
				if (flag)
				{
					Rect rect = _scrollTrackRect.rect;
					float num = Mathf.Max(1f, ((Rect)(ref rect)).height);
					rect = _viewportRect.rect;
					float num2 = Mathf.Max(1f, ((Rect)(ref rect)).height);
					float num3 = num2 + _maxScrollOffset;
					float num4 = Mathf.Clamp(num * (num2 / num3), 42f, num);
					float num5 = Mathf.Max(0f, num - num4);
					float num6 = _scrollOffset / _maxScrollOffset;
					_scrollThumbRect.sizeDelta = new Vector2(0f, num4);
					_scrollThumbRect.anchoredPosition = new Vector2(0f, (0f - num6) * num5);
				}
			}
		}

		private void UpdateTabVisuals()
		{
			if (_summaryTab != null)
			{
				ApplyButtonStyle(_summaryTab, _summaryEnabled, _selectedTab == ReportUiTab.Summary, compact: false);
			}
			if (_liveTab != null)
			{
				ApplyButtonStyle(_liveTab, _liveEnabled, _selectedTab == ReportUiTab.Live, compact: false);
			}
		}

		private void ClearContent()
		{
			if (!((Object)(object)_contentRect == (Object)null))
			{
				for (int num = ((Transform)_contentRect).childCount - 1; num >= 0; num--)
				{
					Object.Destroy((Object)(object)((Component)((Transform)_contentRect).GetChild(num)).gameObject);
				}
			}
		}

		private void OnDestroy()
		{
			RestoreCursorState();
			if (!_isClosing)
			{
				_isClosing = true;
				_closed?.Invoke();
			}
		}

		private void CaptureCursorState()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			_previousCursorLockState = Cursor.lockState;
			_previousCursorVisible = Cursor.visible;
			KeepModalInputState();
		}

		private void KeepModalInputState()
		{
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
			InputManager instance = InputManager.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				GameAccess.SetFloatFieldAtLeast(instance, "disableAimingTimer", 0.25f);
				GameAccess.SetFloatFieldAtLeast(instance, "disableMovementTimer", 0.25f);
			}
		}

		private void RestoreCursorState()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Cursor.lockState = _previousCursorLockState;
			Cursor.visible = _previousCursorVisible;
		}

		private GameObject CreateContentPanel(string name, float height, Color color)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_contentRect == (Object)null)
			{
				throw new InvalidOperationException("Content root has not been built.");
			}
			GameObject val = CreateChildPanel((Transform)(object)_contentRect, name, color);
			RectTransform component = val.GetComponent<RectTransform>();
			component.sizeDelta = new Vector2(0f, height);
			LayoutElement val2 = val.AddComponent<LayoutElement>();
			val2.preferredHeight = height;
			val2.minHeight = height;
			val2.flexibleWidth = 1f;
			return val;
		}

		private static GameObject CreatePanel(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = anchorMin;
			val2.anchorMax = anchorMax;
			val2.offsetMin = offsetMin;
			val2.offsetMax = offsetMax;
			val.AddComponent<Image>();
			return val;
		}

		private static GameObject CreateChildPanel(Transform parent, string name, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			Image val3 = val.AddComponent<Image>();
			((Graphic)val3).color = color;
			return val;
		}

		private static TextMeshProUGUI CreateText(Transform parent, string name, float fontSize, TextAlignmentOptions alignment)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			TextMeshProUGUI val3 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val3).text = string.Empty;
			((TMP_Text)val3).fontSize = fontSize;
			((TMP_Text)val3).alignment = alignment;
			((TMP_Text)val3).richText = false;
			return val3;
		}

		private static ButtonVisual CreateButton(Transform parent, string label, Vector2 anchorMin, Vector2 anchorMax, Vector2 anchoredPosition, Vector2 size, float fontSize)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_0074: 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_00e5: 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)
			GameObject val = new GameObject(label + " Button");
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = anchorMin;
			val2.anchorMax = anchorMax;
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.anchoredPosition = anchoredPosition;
			val2.sizeDelta = size;
			Image val3 = val.AddComponent<Image>();
			((Graphic)val3).color = new Color(0.09f, 0.12f, 0.13f, 0.96f);
			Button val4 = val.AddComponent<Button>();
			((Selectable)val4).targetGraphic = (Graphic)(object)val3;
			((Selectable)val4).transition = (Transition)1;
			TextMeshProUGUI val5 = CreateText(val.transform, "Label", fontSize, (TextAlignmentOptions)514);
			((TMP_Text)val5).text = label;
			((Graphic)val5).color = Color.white;
			((TMP_Text)val5).enableWordWrapping = false;
			((TMP_Text)val5).overflowMode = (TextOverflowModes)1;
			RectTransform rectTransform = ((TMP_Text)val5).rectTransform;
			rectTransform.offsetMin = new Vector2(8f, 0f);
			rectTransform.offsetMax = new Vector2(-8f, 0f);
			return new ButtonVisual(val4, val3, val5);
		}

		private static void ApplyButtonStyle(ButtonVisual visual, bool enabled, bool selected, bool compact)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: 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_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: 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)
			((Selectable)visual.Button).interactable = enabled;
			Color val = (selected ? new Color(0.13f, 0.45f, 0.46f, 0.98f) : new Color(0.075f, 0.095f, 0.105f, compact ? 0.7f : 0.96f));
			Color val2 = (selected ? new Color(0.16f, 0.55f, 0.56f, 1f) : new Color(0.13f, 0.18f, 0.19f, 0.98f));
			Color pressedColor = (selected ? new Color(0.09f, 0.32f, 0.34f, 1f) : new Color(0.045f, 0.06f, 0.065f, 1f));
			Color val3 = default(Color);
			((Color)(ref val3))..ctor(0.06f, 0.065f, 0.07f, 0.55f);
			ColorBlock colors = ((Selectable)visual.Button).colors;
			((ColorBlock)(ref colors)).normalColor = val;
			((ColorBlock)(ref colors)).highlightedColor = val2;
			((ColorBlock)(ref colors)).pressedColor = pressedColor;
			((ColorBlock)(ref colors)).selectedColor = val2;
			((ColorBlock)(ref colors)).disabledColor = val3;
			((ColorBlock)(ref colors)).fadeDuration = 0.08f;
			((ColorBlock)(ref colors)).colorMultiplier = 1f;
			((Selectable)visual.Button).colors = colors;
			((Graphic)visual.Background).color = (enabled ? val : val3);
			((Graphic)visual.Label).color = ((!enabled) ? new Color(0.42f, 0.49f, 0.5f, 1f) : (selected ? new Color(0.98f, 1f, 1f, 1f) : new Color(0.74f, 0.84f, 0.86f, 1f)));
			((TMP_Text)visual.Label).fontStyle = (FontStyles)(selected ? 1 : 0);
		}

		private static void SetOffsets(RectTransform rect, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = offsetMin;
			rect.offsetMax = offsetMax;
		}

		private static void SetAnchors(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = anchorMin;
			rect.anchorMax = anchorMax;
			rect.offsetMin = offsetMin;
			rect.offsetMax = offsetMax;
		}

		private static string Money(int value)
		{
			return "$" + value.ToString("N0", CultureInfo.InvariantCulture);
		}

		private static string Number(int value)
		{
			return value.ToString("N0", CultureInfo.InvariantCulture);
		}

		private static string Percent(float value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture) + "%";
		}

		private static string Weight(float value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}

		private static string CountAndMoney(int count, int value)
		{
			return Number(count) + " / " + Money(value);
		}

		private static string CountDetail(int count, string singular)
		{
			return Number(count) + " " + singular + ((count == 1) ? string.Empty : "s");
		}
	}
	internal sealed class ReportUiController
	{
		private enum ReportKey
		{
			None,
			Menu,
			Escape
		}

		private sealed class PageContent
		{
			public ReportSnapshot? Snapshot { get; }

			public string FallbackText { get; }

			public string Footer { get; }

			public PageContent(ReportSnapshot? snapshot, string fallbackText, string footer)
			{
				Snapshot = snapshot;
				FallbackText = fallbackText;
				Footer = footer;
			}
		}

		private readonly ReportSyncService _syncService;

		private readonly Func<ReportSnapshot?> _liveReportProvider;

		private ReportSnapshot? _latestFinalReport;

		private ReportSnapshot? _latestLiveReport;

		private ReportOverlayPage? _overlay;

		private int _lastHandledFrame = -1;

		private ReportKey _lastHandledKey;

		private InputAction? _menuAction;

		private bool _menuActionWasHeld;

		private bool _waitingForShortcutRelease;

		private float _shortcutReleaseWaitStartedAt;

		private float _ignoreHotkeysUntil;

		private int _suppressGameMenuInputFrame = -1;

		private ReportUiTab _selectedTab;

		public ReportUiController(ReportSyncService syncService, Func<ReportSnapshot?> liveReportProvider)
		{
			_syncService = syncService ?? throw new ArgumentNullException("syncService");
			_liveReportProvider = liveReportProvider ?? throw new ArgumentNullException("liveReportProvider");
		}

		public void Initialize()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			RebuildInputActions();
			NuageReportPlugin.Log.LogInfo((object)($"Report UI initialized. MenuShortcut={PluginConfig.Instance.EffectiveReportMenuShortcut()}, " + $"SelectedTab={_selectedTab}, LiveEnabled={PluginConfig.Instance.IsLiveReportEnabled()}, " + $"MenuInputAction={_menuAction != null}"));
		}

		public void Tick(string source = "Update")
		{
			//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)
			if (!PluginConfig.Instance.IsModActive())
			{
				Deactivate();
				return;
			}
			KeepOpenMenuValid();
			if (!HotkeysAreSuppressed())
			{
				bool flag = ActionIsDown(_menuAction, ref _menuActionWasHeld);
				KeyboardShortcut val = PluginConfig.Instance.EffectiveReportMenuShortcut();
				if (((KeyboardShortcut)(ref val)).IsDown() || flag)
				{
					HandleMenuKey(flag ? (source + "/InputAction") : (source + "/KeyboardShortcut"));
				}
			}
		}

		public bool HandleKeyEvent(KeyCode keyCode, string source)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.Instance.IsModActive())
			{
				Deactivate();
				return false;
			}
			if ((int)keyCode == 0)
			{
				return false;
			}
			if ((int)keyCode == 27 && TryConsumeEscapeMenuInput(source))
			{
				return true;
			}
			if (HotkeysAreSuppressed())
			{
				return false;
			}
			if (IsMenuKey(keyCode))
			{
				return HandleMenuKey(source);
			}
			return false;
		}

		public void Deactivate()
		{
			CloseOpenPage();
			_menuActionWasHeld = false;
			_waitingForShortcutRelease = false;
		}

		public bool TryConsumeEscapeMenuInput(string source)
		{
			if (_suppressGameMenuInputFrame == Time.frameCount)
			{
				return true;
			}
			if ((Object)(object)_overlay == (Object)null)
			{
				return false;
			}
			if (!TryMarkHandled(ReportKey.Escape))
			{
				SuppressGameMenuInputThisFrame();
				return true;
			}
			NuageReportPlugin.Log.LogInfo((object)("Report overlay escape close detected via " + source + "."));
			CloseOpenPage();
			SuppressHotkeysUntilReleased();
			SuppressGameMenuInputThisFrame();
			return true;
		}

		public void SetLatestFinalReport(ReportSnapshot snapshot)
		{
			_latestFinalReport = snapshot ?? throw new ArgumentNullException("snapshot");
			RefreshOpenTab(ReportUiTab.Summary);
		}

		public void SetLatestLiveReport(ReportSnapshot snapshot)
		{
			_latestLiveReport = snapshot ?? throw new ArgumentNullException("snapshot");
			RefreshOpenTab(ReportUiTab.Live);
		}

		private bool HandleMenuKey(string source)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!TryMarkHandled(ReportKey.Menu))
			{
				return false;
			}
			NuageReportPlugin.Log.LogInfo((object)($"Report menu key detected via {source}. enabledTabs=summary:{IsTabEnabled(ReportUiTab.Summary)} live:{IsTabEnabled(ReportUiTab.Live)} " + $"shortcut={PluginConfig.Instance.EffectiveReportMenuShortcut()} hasFinal={HasFinalReport()} hasLive={HasLiveReport()}"));
			if ((Object)(object)_overlay != (Object)null)
			{
				CloseOpenPage();
				SuppressHotkeysUntilReleased();
				return true;
			}
			try
			{
				OpenReportMenu();
			}
			finally
			{
				SuppressHotkeysUntilReleased();
			}
			return true;
		}

		private void OpenReportMenu()
		{
			ReportUiTab selectedTab = _selectedTab;
			ReportUiTab? reportUiTab = ChooseAvailableTab(selectedTab);
			if (!reportUiTab.HasValue)
			{
				NuageReportPlugin.Log.LogInfo((object)"Report menu did not open because both report tabs are disabled.");
				return;
			}
			_selectedTab = reportUiTab.Value;
			PageContent pageContent = ContentFor(_selectedTab);
			CloseOpenPage();
			try
			{
				_overlay = ReportOverlayPage.Create(_selectedTab, IsTabEnabled(ReportUiTab.Summary), IsTabEnabled(ReportUiTab.Live), pageContent.Snapshot, pageContent.FallbackText, pageContent.Footer, SelectTabFromOverlay, delegate
				{
					_overlay = null;
				});
			}
			catch (Exception arg)
			{
				_overlay = null;
				NuageReportPlugin.Log.LogError((object)$"Failed to open report overlay: {arg}");
			}
		}

		private void SelectTabFromOverlay(ReportUiTab tab)
		{
			SelectTab(tab);
		}

		private void SelectTab(ReportUiTab tab)
		{
			if (!IsTabEnabled(tab))
			{
				KeepOpenMenuValid();
				return;
			}
			_selectedTab = tab;
			UpdateOpenMenuContent();
		}

		private void KeepOpenMenuValid()
		{
			if (!((Object)(object)_overlay == (Object)null) && !IsTabEnabled(_selectedTab))
			{
				ReportUiTab? reportUiTab = ChooseAvailableTab(OtherTab(_selectedTab));
				if (!reportUiTab.HasValue)
				{
					CloseOpenPage();
				}
				else
				{
					SelectTab(reportUiTab.Value);
				}
			}
		}

		private void RefreshOpenTab(ReportUiTab tab)
		{
			if ((Object)(object)_overlay != (Object)null && _selectedTab == tab && IsTabEnabled(tab))
			{
				UpdateOpenMenuContent();
			}
		}

		private void UpdateOpenMenuContent()
		{
			if (!((Object)(object)_overlay == (Object)null))
			{
				PageContent pageContent = ContentFor(_selectedTab);
				_overlay.SetContent(_selectedTab, IsTabEnabled(ReportUiTab.Summary), IsTabEnabled(ReportUiTab.Live), pageContent.Snapshot, pageContent.FallbackText, pageContent.Footer);
			}
		}

		private PageContent ContentFor(ReportUiTab tab)
		{
			if (tab != ReportUiTab.Live)
			{
				return SummaryContent();
			}
			return LiveContent();
		}

		private PageContent SummaryContent()
		{
			ReportSnapshot reportSnapshot = _latestFinalReport ?? _syncService.LatestFinalReport;
			if (reportSnapshot == null)
			{
				_syncService.RequestLatestReport(includeFinalReport: true, includeLiveReport: false);
				return new PageContent(null, "No completed level report is available yet.", "Summary | No completed report");
			}
			return new PageContent(reportSnapshot, reportSnapshot.DisplayText, FooterForSnapshot(reportSnapshot, "Summary"));
		}

		private PageContent LiveContent()
		{
			ReportSnapshot reportSnapshot = _liveReportProvider() ?? _latestLiveReport ?? _syncService.LatestLiveReport;
			if (reportSnapshot == null)
			{
				_syncService.RequestLatestReport(includeFinalReport: false, includeLiveReport: true);
				return new PageContent(null, "No live report is available.", "Live | Waiting for an active shared report");
			}
			return new PageContent(reportSnapshot, reportSnapshot.DisplayText, FooterForSnapshot(reportSnapshot, "Live"));
		}

		private bool HasFinalReport()
		{
			if (_latestFinalReport == null)
			{
				return _syncService.LatestFinalReport != null;
			}
			return true;
		}

		private bool HasLiveReport()
		{
			if (_latestLiveReport == null)
			{
				return _syncService.LatestLiveReport != null;
			}
			return true;
		}

		private static ReportUiTab? ChooseAvailableTab(ReportUiTab preferredTab)
		{
			if (IsTabEnabled(preferredTab))
			{
				return preferredTab;
			}
			ReportUiTab reportUiTab = OtherTab(preferredTab);
			if (!IsTabEnabled(reportUiTab))
			{
				return null;
			}
			return reportUiTab;
		}

		private static bool IsTabEnabled(ReportUiTab tab)
		{
			if (tab != ReportUiTab.Live)
			{
				return true;
			}
			return PluginConfig.Instance.IsLiveReportEnabled();
		}

		private static ReportUiTab OtherTab(ReportUiTab tab)
		{
			if (tab != ReportUiTab.Live)
			{
				return ReportUiTab.Live;
			}
			return ReportUiTab.Summary;
		}

		private void RebuildInputActions()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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)
			InputAction? menuAction = _menuAction;
			if (menuAction != null)
			{
				menuAction.Disable();
			}
			KeyboardShortcut val = PluginConfig.Instance.EffectiveReportMenuShortcut();
			_menuAction = CreateInputAction("NuageReport Menu", ((KeyboardShortcut)(ref val)).MainKey);
			_menuActionWasHeld = false;
		}

		private static InputAction? CreateInputAction(string name, KeyCode keyCode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			string text = BindingPathFor(keyCode);
			if (text == null)
			{
				return null;
			}
			InputAction val = new InputAction(name, (InputActionType)1, text, (string)null, (string)null, (string)null);
			val.Enable();
			return val;
		}

		private static bool ActionIsDown(InputAction? action, ref bool wasHeld)
		{
			if (action == null)
			{
				return false;
			}
			bool flag = action.ReadValue<float>() > 0.5f;
			bool result = flag && !wasHeld;
			wasHeld = flag;
			return result;
		}

		private bool TryMarkHandled(ReportKey key)
		{
			if (_lastHandledFrame == Time.frameCount && _lastHandledKey == key)
			{
				return false;
			}
			_lastHandledFrame = Time.frameCount;
			_lastHandledKey = key;
			return true;
		}

		private static bool IsMenuKey(KeyCode keyCode)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			KeyboardShortcut val = PluginConfig.Instance.EffectiveReportMenuShortcut();
			return keyCode == ((KeyboardShortcut)(ref val)).MainKey;
		}

		private bool HotkeysAreSuppressed()
		{
			if (Time.unscaledTime < _ignoreHotkeysUntil)
			{
				return true;
			}
			if (!_waitingForShortcutRelease)
			{
				return false;
			}
			if (Time.unscaledTime - _shortcutReleaseWaitStartedAt > 2f)
			{
				_waitingForShortcutRelease = false;
				return false;
			}
			if (AnyReportShortcutPressed())
			{
				return true;
			}
			_waitingForShortcutRelease = false;
			return false;
		}

		private void SuppressHotkeysUntilReleased()
		{
			_waitingForShortcutRelease = true;
			_shortcutReleaseWaitStartedAt = Time.unscaledTime;
			_ignoreHotkeysUntil = Time.unscaledTime + 0.15f;
			_menuActionWasHeld = IsActionPressed(_menuAction);
		}

		private void SuppressGameMenuInputThisFrame()
		{
			_suppressGameMenuInputFrame = Time.frameCount;
		}

		private bool AnyReportShortcutPressed()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut val = PluginConfig.Instance.EffectiveReportMenuShortcut();
			if (!((KeyboardShortcut)(ref val)).IsPressed())
			{
				return IsActionPressed(_menuAction);
			}
			return true;
		}

		private static bool IsActionPressed(InputAction? action)
		{
			if (action != null)
			{
				return action.ReadValue<float>() > 0.5f;
			}
			return false;
		}

		private static string? BindingPathFor(KeyCode keyCode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Invalid comparison between Unknown and I4
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected I4, but got Unknown
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected I4, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Invalid comparison between Unknown and I4
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Invalid comparison between Unknown and I4
			if ((int)keyCode <= 91)
			{
				if ((int)keyCode == 32)
				{
					return "<Keyboard>/space";
				}
				switch (keyCode - 45)
				{
				default:
					if ((int)keyCode != 91)
					{
						break;
					}
					return "<Keyboard>/leftBracket";
				case 3:
					return "<Keyboard>/0";
				case 4:
					return "<Keyboard>/1";
				case 5:
					return "<Keyboard>/2";
				case 6:
					return "<Keyboard>/3";
				case 7:
					return "<Keyboard>/4";
				case 8:
					return "<Keyboard>/5";
				case 9:
					return "<Keyboard>/6";
				case 10:
					return "<Keyboard>/7";
				case 11:
					return "<Keyboard>/8";
				case 12:
					return "<Keyboard>/9";
				case 0:
					return "<Keyboard>/minus";
				case 16:
					return "<Keyboard>/equals";
				case 1:
				case 2:
				case 13:
				case 14:
				case 15:
					break;
				}
			}
			else if ((int)keyCode <= 96)
			{
				if ((int)keyCode == 93)
				{
					return "<Keyboard>/rightBracket";
				}
				if ((int)keyCode == 96)
				{
					return "<Keyboard>/backquote";
				}
			}
			else
			{
				if ((int)keyCode == 111)
				{
					return "<Keyboard>/o";
				}
				switch (keyCode - 282)
				{
				case 0:
					return "<Keyboard>/f1";
				case 1:
					return "<Keyboard>/f2";
				case 2:
					return "<Keyboard>/f3";
				case 3:
					return "<Keyboard>/f4";
				case 4:
					return "<Keyboard>/f5";
				case 5:
					return "<Keyboard>/f6";
				case 6:
					return "<Keyboard>/f7";
				case 7:
					return "<Keyboard>/f8";
				case 8:
					return "<Keyboard>/f9";
				case 9:
					return "<Keyboard>/f10";
				case 10:
					return "<Keyboard>/f11";
				case 11:
					return "<Keyboard>/f12";
				}
			}
			return null;
		}

		private void CloseOpenPage()
		{
			if (!((Object)(object)_overlay == (Object)null))
			{
				ReportOverlayPage overlay = _overlay;
				_overlay = null;
				overlay.Close();
			}
		}

		private static string FooterForSnapshot(ReportSnapshot snapshot, string header)
		{
			string text = (string.IsNullOrWhiteSpace(snapshot.LevelName) ? "Unknown level" : snapshot.LevelName);
			string text2 = ((snapshot.ReportKind == ReportKind.Live) ? "Live" : "Complete");
			return string.Format(CultureInfo.InvariantCulture, "{0} | {1} | {2} | Run {3} | Generated {4:u}", header, text2, text, snapshot.RunId, snapshot.GeneratedAtUtc);
		}
	}
	[Serializable]
	internal sealed class ReportUiData
	{
		public int schemaVersion;

		public string runId = string.Empty;

		public string startedAt = string.Empty;

		public string endedAt = string.Empty;

		public ReportUiGlobal? global;

		public ReportUiPlayer[]? players;

		public ReportUiCosmeticObjects? cosmeticObjects;

		public static bool TryParse(string json, out ReportUiData? data)
		{
			data = null;
			if (string.IsNullOrWhiteSpace(json))
			{
				return false;
			}
			try
			{
				object obj = new ReportJsonReader(json).Read();
				if (!(obj is Dictionary<string, object> map))
				{
					return false;
				}
				data = FromMap(map);
				return data.global != null;
			}
			catch
			{
				data = null;
				return false;
			}
		}

		private static ReportUiData FromMap(Dictionary<string, object?> map)
		{
			return new ReportUiData
			{
				schemaVersion = IntValue(map, "schemaVersion"),
				runId = StringValue(map, "runId"),
				startedAt = StringValue(map, "startedAt"),
				endedAt = StringValue(map, "endedAt"),
				global = GlobalFromMap(ObjectValue(map, "global")),
				players = PlayersFromArray(ArrayValue(map, "players")),
				cosmeticObjects = CosmeticObjectsFromMap(ObjectValue(map, "cosmeticObjects"))
			};
		}

		private static ReportUiGlobal? GlobalFromMap(Dictionary<string, object?>? map)
		{
			if (map == null)
			{
				return null;
			}
			return new ReportUiGlobal
			{
				objectsAtStart = IntValue(map, "objectsAtStart"),
				objectsAtStartValue = IntValue(map, "objectsAtStartValue"),
				objectsExtracted = IntValue(map, "objectsExtracted"),
				objectsExtractedValue = IntValue(map, "objectsExtractedValue"),
				objectsDetected = IntValue(map, "objectsDetected"),
				objectsDetectedPercent = FloatValue(map, "objectsDetectedPercent"),
				objectsMissed = IntValue(map, "objectsMissed"),
				objectsDamaged = IntValue(map, "objectsDamaged"),
				objectsDestroyed =