Decompiled source of PrefabDependencyTree v1.3.0

plugins\PrefabDependencyTree.dll

Decompiled a year ago
using System;
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.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using DotNetGraph.Attributes;
using DotNetGraph.Compilation;
using DotNetGraph.Core;
using DotNetGraph.Extensions;
using JetBrains.Annotations;
using Jotunn.Entities;
using Jotunn.Managers;
using MonoMod.Utils;
using PrefabDependencyTree.Data;
using PrefabDependencyTree.Data.Drops;
using PrefabDependencyTree.Data.Drops.Generic;
using PrefabDependencyTree.Data.Filters;
using PrefabDependencyTree.Model;
using PrefabDependencyTree.Util;
using UnityEngine;

[assembly: AssemblyFileVersion("1.2.5")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PrefabDependencyTree")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("FixItFelix")]
[assembly: AssemblyProduct("PrefabDependencyTree")]
[assembly: AssemblyCopyright("Copyright 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: CompilationRelaxations(8)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.5.0")]
[module: UnverifiableCode]
namespace PrefabDependencyTree
{
	[BepInPlugin("FixItFelix.PrefabDependencyTree", "PrefabDependencyTree", "1.2.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal class PrefabDependencyTreePlugin : BaseUnityPlugin
	{
		public const string PluginAuthor = "FixItFelix";

		public const string PluginGUID = "FixItFelix.PrefabDependencyTree";

		public const string PluginName = "PrefabDependencyTree";

		public const string PluginVersion = "1.2.5";

		private void Awake()
		{
			PrefabManager.OnPrefabsRegistered += DataHarvester.Initialize;
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ConsoleController());
		}
	}
}
namespace PrefabDependencyTree.Util
{
	public class ConsoleController : ConsoleCommand
	{
		private const string prefabDependencyTreeCommand = "prefab_dependency_tree";

		private const string printOption = "print_tree";

		private const string printIncludeFilteredOption = "print_include_filtered_tree";

		private const string printExcludeFilteredOption = "print_exclude_filtered_tree";

		private const string debugLogAll = "debug_log_all";

		private const string debugLogItemsCategories = "debug_log_items_categories";

		private static readonly List<string> ItemTypeEnums = Enum.GetNames(typeof(ItemType)).ToList();

		private static readonly List<ItemType> WeaponsAndArmorEnums = new List<ItemType>
		{
			(ItemType)9,
			(ItemType)4,
			(ItemType)7,
			(ItemType)10,
			(ItemType)12,
			(ItemType)6,
			(ItemType)11,
			(ItemType)5,
			(ItemType)17,
			(ItemType)19,
			(ItemType)15,
			(ItemType)18,
			(ItemType)20,
			(ItemType)23,
			(ItemType)3,
			(ItemType)14,
			(ItemType)22
		};

		private static readonly List<string> WeaponsAndArmorTypes = WeaponsAndArmorEnums.Select((ItemType enumEntry) => ((object)(ItemType)(ref enumEntry)).ToString()).ToList();

		private const string WeaponsAndArmorTypesName = "WeaponsAndArmorTypes";

		public override string Name => "prefab_dependency_tree";

		public override string Help => "Prefab Dependency Tree Console Commands";

		private static List<string> ReplaceTypes(List<string> input)
		{
			if (!input.Contains("WeaponsAndArmorTypes"))
			{
				return input;
			}
			input.Remove("WeaponsAndArmorTypes");
			input.AddRange(WeaponsAndArmorTypes);
			return input;
		}

		public override void Run(string[] args)
		{
			Logger.LogInfo("called '" + ((ConsoleCommand)this).Name + "' with args '" + string.Join(" ", args) + "'");
			if (args.Length != 0)
			{
				switch (args[0])
				{
				case "print_tree":
					WriteGraphOutput(new UnfilteredData());
					break;
				case "print_include_filtered_tree":
					if (args.Length > 1)
					{
						WriteGraphOutput(new IncludeFilterTypes(ReplaceTypes(args[1].Split(new char[1] { ',' }).ToList())));
					}
					else
					{
						Logger.LogWarning("you did not provide item types to filter for, see usage");
						LogUsage();
					}
					break;
				case "print_exclude_filtered_tree":
					if (args.Length > 1)
					{
						WriteGraphOutput(new ExcludeFilterTypes(ReplaceTypes(args[1].Split(new char[1] { ',' }).ToList())));
					}
					else
					{
						Logger.LogWarning("you did not provide item types to filter for, see usage");
						LogUsage();
					}
					break;
				case "debug_log_items_categories":
				{
					List<string> values = DataHarvester.LogAllItemsToCategorizedYaml();
					string text2 = Path.Combine(Paths.ConfigPath, "FixItFelix.PrefabDependencyTree.all.items.categories.yaml");
					File.WriteAllText(text2, string.Join("\n", values));
					Logger.LogInfo("wrote file '" + text2 + "'");
					break;
				}
				case "debug_log_all":
				{
					List<string> list = DataHarvester.LogAllToString();
					list.ForEach(Logger.LogWarning);
					string text = Path.Combine(Paths.ConfigPath, "FixItFelix.PrefabDependencyTree.all.txt");
					File.WriteAllText(text, string.Join("\n", list));
					Logger.LogInfo("wrote file '" + text + "'");
					break;
				}
				default:
					Logger.LogWarning("this option '" + args[0] + "' is not supported, see usage");
					LogUsage();
					break;
				}
			}
			else
			{
				LogUsage();
			}
		}

		private static async void WriteGraphOutput(FilteredData filteredData)
		{
			using StringWriter writer = new StringWriter();
			CompilationContext context = new CompilationContext(writer, new CompilationOptions());
			filteredData.LogOverview();
			await filteredData.CreateGraph().CompileAsync(context);
			string text = Path.Combine(Paths.ConfigPath, "FixItFelix.PrefabDependencyTree.graph.dot");
			File.WriteAllText(text, writer.GetStringBuilder().ToString());
			Logger.LogInfo("wrote graph file '" + text + "'");
			RunGraphVizConverter(text);
		}

		private static void RunGraphVizConverter(string filePath)
		{
			string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Graphviz\\bin\\gv2gml.exe");
			if (!File.Exists(text))
			{
				return;
			}
			Logger.LogInfo("GraphViz installation binary detected at '" + text + "'");
			try
			{
				string text2 = filePath.Replace(".dot", ".gml");
				Process.Start(text, "\"" + filePath + "\" -o \"" + text2 + "\"")?.WaitForExit(100000);
				if (File.Exists(text2))
				{
					Logger.LogInfo("converted to GraphViz file to '" + text2 + "'");
				}
				else
				{
					Logger.LogWarning("converted file was not found as expected");
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning("error converting .dot file using GraphViz: " + ex.Message);
			}
		}

		private static void LogUsage()
		{
			Logger.LogInfo(" - prefab_dependency_tree usage - ");
			Logger.LogInfo("command option:");
			Logger.LogInfo("   print_tree -> will print the whole tree analyzed from game data");
			Logger.LogInfo("   debug_log_all -> will log warn all prefabs (this will be huge log output!)");
			Logger.LogInfo("   debug_log_items_categories -> writes all item prefabs by category into a yaml format file");
			Logger.LogInfo("   print_include_filtered_tree Material,Consumable -> print tree with items of the provided item types included (complete tree with link to any included items)");
			Logger.LogInfo("   print_exclude_filtered_tree Ammo,OneHandedWeapon -> print tree with items of the provided item types excluded (remove the provided types from tree)");
			Logger.LogInfo("        all item types: " + string.Join(", ", ItemTypeEnums) + ", WeaponsAndArmorTypes");
		}

		public override List<string> CommandOptionList()
		{
			return new List<string> { "print_tree", "print_include_filtered_tree", "print_exclude_filtered_tree", "debug_log_all", "debug_log_items_categories" };
		}
	}
	public static class Logger
	{
		private static readonly ManualLogSource LoggerInstance = Logger.CreateLogSource("PrefabDependencyTree");

		public static void LogDebug(string text)
		{
			LoggerInstance.LogDebug((object)text);
		}

		public static void LogInfo(string text)
		{
			LoggerInstance.LogInfo((object)text);
		}

		public static void LogWarning(string text)
		{
			LoggerInstance.LogWarning((object)text);
		}

		public static void LogError(string text)
		{
			LoggerInstance.LogError((object)text);
		}
	}
}
namespace PrefabDependencyTree.Model
{
	public class BaseCrafting
	{
		public readonly string Name;

		public readonly Dictionary<string, GraphRecipe> Recipes = new Dictionary<string, GraphRecipe>();

		protected BaseCrafting(string name)
		{
			Name = name;
		}

		protected BaseCrafting(string name, Dictionary<string, GraphRecipe> recipes)
		{
			Name = name;
			Recipes = recipes;
		}

		public bool ContainsItemTypes(List<string> itemTypes)
		{
			return Recipes.Any((KeyValuePair<string, GraphRecipe> recipe) => itemTypes.Contains(recipe.Value.CraftedItem.Item1.ItemType) || recipe.Value.RequiredItems.Any((KeyValuePair<GraphItem, int> requiredItem) => itemTypes.Contains(requiredItem.Key.ItemType)));
		}

		public void RemoveRecipesForTypes(List<string> itemTypes)
		{
			int count = Recipes.Count;
			Dictionary<string, GraphRecipe> dictionary = Recipes.Where((KeyValuePair<string, GraphRecipe> recipe) => !itemTypes.Contains(recipe.Value.CraftedItem.Item1.ItemType) && !recipe.Value.RequiredItems.Any((KeyValuePair<GraphItem, int> item) => itemTypes.Contains(item.Key.ItemType))).ToDictionary((KeyValuePair<string, GraphRecipe> recipe) => recipe.Key, (KeyValuePair<string, GraphRecipe> recipe) => recipe.Value);
			Recipes.Clear();
			Extensions.AddRange<string, GraphRecipe>(Recipes, dictionary);
			Logger.LogInfo("reduced '" + Name + "' recipes " + $"from original count {count} to reduced count {Recipes.Count}");
		}

		public override string ToString()
		{
			string text = string.Join("\n    ", Recipes.Select((KeyValuePair<string, GraphRecipe> recipe) => recipe.Value.ToString().Replace("\n", "\n    ")));
			return "name '" + Name + "' has recipes:\n    " + text;
		}
	}
	public class GraphCraftingStation : BaseCrafting
	{
		public readonly List<string> ExtensionNames;

		private GraphCraftingStation(string stationName, List<string> extensionNames)
			: base(stationName)
		{
			ExtensionNames = extensionNames;
		}

		public static Dictionary<string, GraphCraftingStation> FromExtensionsAndStations(List<StationExtension> extensions, List<CraftingStation> stations)
		{
			Dictionary<string, GraphCraftingStation> stationsFromExtensions = (from extension in extensions
				select Tuple.Create(((Object)(object)extension.m_craftingStation != (Object)null) ? ((Object)extension.m_craftingStation).name : "missing station", ((Object)extension).name) into tuple
				group tuple by tuple.Item1).ToDictionary((IGrouping<string, Tuple<string, string>> group) => group.Key, (IGrouping<string, Tuple<string, string>> group) => new GraphCraftingStation(group.Key, group.Select((Tuple<string, string> tuple) => tuple.Item2).ToList()));
			GraphCraftingStation value;
			return stations.ToDictionary((CraftingStation station) => ((Object)station).name, (CraftingStation station) => stationsFromExtensions.TryGetValue(((Object)station).name, out value) ? value : new GraphCraftingStation(((Object)station).name, new List<string>()));
		}

		public override string ToString()
		{
			if (ExtensionNames.Count <= 0)
			{
				return "[crafting station " + base.ToString() + "\n]";
			}
			string text = string.Join(", ", ExtensionNames);
			return "[crafting station " + base.ToString() + "\n  has extensions: " + text + "\n]";
		}
	}
	public class GraphItem
	{
		public string ItemName { get; private set; }

		public string ItemType { get; set; }

		private GraphItem()
		{
		}

		public static GraphItem GetOrCreate(string itemName, string itemType)
		{
			if (DataHarvester.Items.TryGetValue(itemName, out var value))
			{
				return value;
			}
			GraphItem graphItem = new GraphItem
			{
				ItemName = itemName,
				ItemType = itemType
			};
			DataHarvester.Items.Add(itemName, graphItem);
			return graphItem;
		}

		public static GraphItem GetOrCreate(ItemDrop drop)
		{
			return GetOrCreate(((Object)drop).name, ((object)(ItemType)(ref drop.m_itemData.m_shared.m_itemType)).ToString());
		}

		[CanBeNull]
		public static GraphItem GetOrCreate(GameObject gameObject)
		{
			ItemDrop val = default(ItemDrop);
			if (!gameObject.TryGetComponent<ItemDrop>(ref val))
			{
				return null;
			}
			return GetOrCreate(((Object)val).name, ((object)(ItemType)(ref val.m_itemData.m_shared.m_itemType)).ToString());
		}

		public override string ToString()
		{
			return "[item name '" + ItemName + "', type '" + ItemType + "']";
		}
	}
	public class GraphPiece
	{
		public string PieceName;

		public string RequiredCraftingStation;

		public Dictionary<GraphItem, int> BuildRequirements;

		public static GraphPiece FromPiece(Piece fromGame)
		{
			return new GraphPiece
			{
				PieceName = ((Object)fromGame).name,
				RequiredCraftingStation = (((Object)(object)fromGame.m_craftingStation == (Object)null) ? "no_station_required" : ((Object)fromGame.m_craftingStation).name),
				BuildRequirements = fromGame.m_resources.Where((Requirement resource) => (Object)(object)resource.m_resItem != (Object)null).ToDictionary((Requirement resource) => GraphItem.GetOrCreate(((Object)resource.m_resItem).name, ((object)(ItemType)(ref resource.m_resItem.m_itemData.m_shared.m_itemType)).ToString()), (Requirement resource) => resource.m_amount)
			};
		}

		public override string ToString()
		{
			if (BuildRequirements.Count <= 0)
			{
				return "[piece name '" + PieceName + "', requires station '" + RequiredCraftingStation + "']";
			}
			string text = string.Join("\n    ", BuildRequirements.Select((KeyValuePair<GraphItem, int> requirement) => $"[{requirement.Key} x{requirement.Value}]"));
			return "[piece name '" + PieceName + "', requires station '" + RequiredCraftingStation + "', requirements:\n    " + text + "\n]";
		}
	}
	public class GraphProcessor : BaseCrafting
	{
		private GraphProcessor(string name, Dictionary<string, GraphRecipe> recipes)
			: base(name, recipes)
		{
		}

		public static GraphProcessor FromSmelter(Smelter smelter)
		{
			return new GraphProcessor(((Object)smelter).name, GraphRecipe.FromSmelter(smelter));
		}

		public static GraphProcessor FromIncinerator(Incinerator incinerator)
		{
			return new GraphProcessor(((Object)incinerator).name, GraphRecipe.FromIncinerator(incinerator));
		}

		public static GraphProcessor FromFermenter(Fermenter fermenter)
		{
			return new GraphProcessor(((Object)fermenter).name, GraphRecipe.FromFermenter(fermenter));
		}

		public static GraphProcessor FromCookingStation(CookingStation cookingStation)
		{
			return new GraphProcessor(((Object)cookingStation).name, GraphRecipe.FromCookingStation(cookingStation));
		}

		public override string ToString()
		{
			return "[processor " + base.ToString() + "\n]";
		}
	}
	public class GraphRecipe
	{
		public string RecipeName;

		public Tuple<GraphItem, int> CraftedItem;

		public Dictionary<GraphItem, int> RequiredItems;

		private GraphRecipe()
		{
		}

		public static GraphRecipe FromRecipe(Recipe fromGame)
		{
			if ((Object)(object)fromGame.m_item == (Object)null)
			{
				throw new ArgumentException("recipe '" + ((Object)fromGame).name + "' does not produce an item");
			}
			if (!fromGame.m_resources.ToList().TrueForAll((Requirement recourse) => (Object)(object)recourse.m_resItem != (Object)null))
			{
				throw new ArgumentException("recipe '" + ((Object)fromGame).name + "' contains a null required resource");
			}
			return new GraphRecipe
			{
				RecipeName = ((Object)fromGame).name,
				CraftedItem = Tuple.Create(GraphItem.GetOrCreate(fromGame.m_item), 1),
				RequiredItems = fromGame.m_resources.ToDictionary((Requirement requirement) => GraphItem.GetOrCreate(requirement.m_resItem), (Requirement requirement) => requirement.m_amount)
			};
		}

		public static Dictionary<string, GraphRecipe> FromSmelter(Smelter smelter)
		{
			string fuel = (((Object)(object)smelter.m_fuelItem == (Object)null) ? "Air" : ((Object)smelter.m_fuelItem).name);
			return (from recipe in smelter.m_conversion.Select(delegate(ItemConversion conversion)
				{
					//IL_004b: Unknown result type (might be due to invalid IL or missing references)
					GraphRecipe obj = new GraphRecipe
					{
						RecipeName = GetProcessorRecipeName(((Object)conversion.m_from).name, ((Object)conversion.m_to).name),
						CraftedItem = Tuple.Create(GraphItem.GetOrCreate(conversion.m_to), 1)
					};
					Dictionary<GraphItem, int> dictionary = new Dictionary<GraphItem, int>();
					string itemName = fuel;
					ItemType val = (ItemType)1;
					dictionary.Add(GraphItem.GetOrCreate(itemName, ((object)(ItemType)(ref val)).ToString()), smelter.m_fuelPerProduct);
					dictionary.Add(GraphItem.GetOrCreate(conversion.m_from), 1);
					obj.RequiredItems = dictionary;
					return obj;
				})
				group recipe by recipe.RecipeName).ToDictionary((IGrouping<string, GraphRecipe> group) => group.Key, (IGrouping<string, GraphRecipe> group) => group.First());
		}

		public static Dictionary<string, GraphRecipe> FromIncinerator(Incinerator incinerator)
		{
			return (from conversion in incinerator.m_conversions
				select new GraphRecipe
				{
					RecipeName = GetProcessorRecipeName("Incinerator_Sacrifice", ((Object)conversion.m_result).name),
					CraftedItem = Tuple.Create(GraphItem.GetOrCreate(conversion.m_result), conversion.m_resultAmount),
					RequiredItems = conversion.m_requirements.ToDictionary((Requirement requirement) => GraphItem.GetOrCreate(requirement.m_resItem), (Requirement requirement) => requirement.m_amount)
				} into recipe
				group recipe by recipe.RecipeName).ToDictionary((IGrouping<string, GraphRecipe> group) => group.Key, (IGrouping<string, GraphRecipe> group) => new GraphRecipe
			{
				RecipeName = group.Key,
				CraftedItem = group.First().CraftedItem,
				RequiredItems = group.SelectMany((GraphRecipe recipe) => recipe.RequiredItems).ToDictionary((KeyValuePair<GraphItem, int> tuple) => tuple.Key, (KeyValuePair<GraphItem, int> tuple) => tuple.Value)
			});
		}

		public static Dictionary<string, GraphRecipe> FromFermenter(Fermenter fermenter)
		{
			return (from conversion in fermenter.m_conversion
				select new GraphRecipe
				{
					RecipeName = GetProcessorRecipeName(((Object)conversion.m_from).name, ((Object)conversion.m_to).name),
					CraftedItem = Tuple.Create(GraphItem.GetOrCreate(conversion.m_to), conversion.m_producedItems),
					RequiredItems = new Dictionary<GraphItem, int> { 
					{
						GraphItem.GetOrCreate(conversion.m_from),
						1
					} }
				} into recipe
				group recipe by recipe.RecipeName).ToDictionary((IGrouping<string, GraphRecipe> group) => group.Key, (IGrouping<string, GraphRecipe> group) => group.First());
		}

		public static Dictionary<string, GraphRecipe> FromCookingStation(CookingStation cookingStation)
		{
			string fuel = (((Object)(object)cookingStation.m_fuelItem == (Object)null) ? "Fire" : ((Object)cookingStation.m_fuelItem).name);
			return (from recipe in cookingStation.m_conversion.Select(delegate(ItemConversion conversion)
				{
					//IL_005d: Unknown result type (might be due to invalid IL or missing references)
					GraphRecipe obj = new GraphRecipe
					{
						RecipeName = GetProcessorRecipeName(((Object)conversion.m_from).name, ((Object)conversion.m_to).name),
						CraftedItem = Tuple.Create(GraphItem.GetOrCreate(conversion.m_to), 1)
					};
					Dictionary<GraphItem, int> obj2 = new Dictionary<GraphItem, int> { 
					{
						GraphItem.GetOrCreate(conversion.m_from),
						1
					} };
					string itemName = fuel;
					ItemType val = (ItemType)1;
					obj2.Add(GraphItem.GetOrCreate(itemName, ((object)(ItemType)(ref val)).ToString()), 1);
					obj.RequiredItems = obj2;
					return obj;
				})
				group recipe by recipe.RecipeName).ToDictionary((IGrouping<string, GraphRecipe> group) => group.Key, (IGrouping<string, GraphRecipe> group) => group.First());
		}

		private static string GetProcessorRecipeName(string fromItem, string toItem)
		{
			return "[processing " + fromItem + " to " + toItem + "]";
		}

		public override string ToString()
		{
			string text = string.Join("\n    ", RequiredItems.Select((KeyValuePair<GraphItem, int> requiredItem) => $"[{requiredItem.Key} x{requiredItem.Value}]"));
			return $"[recipe '{RecipeName}' to create {CraftedItem.Item1} requires:\n" + "    " + text + "\n]";
		}
	}
}
namespace PrefabDependencyTree.Data
{
	public static class DataHarvester
	{
		public static readonly Dictionary<string, GraphItem> Items = new Dictionary<string, GraphItem>();

		public static readonly Dictionary<string, GraphRecipe> UnboundRecipes = new Dictionary<string, GraphRecipe>();

		public static readonly Dictionary<string, GraphCraftingStation> CraftingStations = new Dictionary<string, GraphCraftingStation>();

		public static readonly Dictionary<string, GraphProcessor> Processors = new Dictionary<string, GraphProcessor>();

		public static readonly Dictionary<Tuple<string, DropType>, List<GraphItem>> Drops = new Dictionary<Tuple<string, DropType>, List<GraphItem>>();

		public static readonly Dictionary<string, GraphPiece> Pieces = new Dictionary<string, GraphPiece>();

		public static void Initialize()
		{
			InitializeSmelters();
			InitializeIncinerator();
			InitializeCookingStations();
			InitializeFermenters();
			InitializeCraftingStations();
			InitializePieces();
			InitializeRecipes();
			InitializeDrops();
			LogOverview();
			LogItemTypesOverview();
		}

		private static void LogOverview()
		{
			Logger.LogInfo("vvvv data harvester overview vvvv");
			Logger.LogInfo($"    total {Items.Count} items registered");
			Logger.LogInfo($"    total {Drops.Count} drops registered");
			Logger.LogInfo($"    total {Pieces.Count} pieces registered");
			Logger.LogInfo($"    total {CraftingStations.Count} crafting stations registered");
			Logger.LogInfo($"    total {Processors.Count} processor stations (fermenter, smelter, ...) registered");
			Logger.LogInfo($"    total {UnboundRecipes.Count} unbound recipes registered");
			Logger.LogInfo("^^^^ data harvester overview ^^^^");
		}

		private static void LogItemTypesOverview()
		{
			Dictionary<string, int> source = (from item in Items
				group item by item.Value.ItemType).ToDictionary((IGrouping<string, KeyValuePair<string, GraphItem>> group) => group.Key, (IGrouping<string, KeyValuePair<string, GraphItem>> group) => group.Count());
			Logger.LogInfo("vvvv item types overview vvvv");
			foreach (KeyValuePair<string, int> item in source.OrderBy((KeyValuePair<string, int> pair) => pair.Key))
			{
				Logger.LogInfo($"item type '{item.Key}' -> found {item.Value} items");
			}
			Logger.LogInfo("^^^^ item types overview ^^^^");
		}

		public static List<string> LogAllToString()
		{
			List<string> list = new List<string>();
			list.Add("==== debug log output for all prefabs ====");
			list.AddRange(Items.Select((KeyValuePair<string, GraphItem> item) => item.Value.ToString()));
			list.AddRange(from <>h__TransparentIdentifier0 in Drops.Select(delegate(KeyValuePair<Tuple<string, DropType>, List<GraphItem>> drop)
				{
					KeyValuePair<Tuple<string, DropType>, List<GraphItem>> keyValuePair = drop;
					return new
					{
						drop = drop,
						dropsList = string.Join("\n    ", keyValuePair.Value.Select((GraphItem item) => item.ToString()))
					};
				})
				select $"['{<>h__TransparentIdentifier0.drop.Key}' drops:\n" + "    " + <>h__TransparentIdentifier0.dropsList + "\n]");
			list.AddRange(Pieces.Select((KeyValuePair<string, GraphPiece> piece) => piece.Value.ToString()));
			list.AddRange(CraftingStations.Select((KeyValuePair<string, GraphCraftingStation> station) => station.Value.ToString()));
			list.AddRange(Processors.Select((KeyValuePair<string, GraphProcessor> processor) => processor.Value.ToString()));
			list.AddRange(UnboundRecipes.Select((KeyValuePair<string, GraphRecipe> recipe) => recipe.Value.ToString()));
			list.Add("==== debug log output for all prefabs ====");
			return list;
		}

		public static List<string> LogAllItemsToCategorizedYaml()
		{
			List<GraphItem> second = CraftingStations.SelectMany((KeyValuePair<string, GraphCraftingStation> cs) => cs.Value.Recipes.Select((KeyValuePair<string, GraphRecipe> recipe) => recipe.Value).ToList()).Union(UnboundRecipes.Select((KeyValuePair<string, GraphRecipe> unbound) => unbound.Value)).Union(Processors.SelectMany((KeyValuePair<string, GraphProcessor> processor) => processor.Value.Recipes.Select((KeyValuePair<string, GraphRecipe> recipe) => recipe.Value).ToList()))
				.ToList()
				.SelectMany(delegate(GraphRecipe recipe)
				{
					List<GraphItem> list2 = new List<GraphItem>();
					list2.Add(recipe.CraftedItem.Item1);
					list2.AddRange(recipe.RequiredItems.Select((KeyValuePair<GraphItem, int> req) => req.Key));
					return list2;
				})
				.ToList();
			Dictionary<string, List<string>> dictionary = (from item in Items.Select((KeyValuePair<string, GraphItem> item) => item.Value).Union(second).Union(Drops.SelectMany((KeyValuePair<Tuple<string, DropType>, List<GraphItem>> drop) => drop.Value))
					.ToList()
				group item by item.ItemType).ToDictionary((IGrouping<string, GraphItem> group) => group.Key, (IGrouping<string, GraphItem> group) => (from item in @group.Select((GraphItem item) => item.ItemName).Distinct()
				orderby item
				select item).ToList());
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, List<string>> item in dictionary)
			{
				list.Add(item.Key + ":");
				list.AddRange(item.Value.Select((string item) => "  - " + item));
			}
			return list;
		}

		private static void InitializePieces()
		{
			Dictionary<string, GraphPiece> dictionary = Cache.GetPrefabs(typeof(Piece)).ToDictionary((KeyValuePair<string, Object> kv) => kv.Key, (KeyValuePair<string, Object> kv) => GraphPiece.FromPiece((Piece)kv.Value));
			dictionary.Where((KeyValuePair<string, GraphPiece> kv) => !CraftingStations.ContainsKey(kv.Key) && !Processors.ContainsKey(kv.Key)).ToList().ForEach(delegate(KeyValuePair<string, GraphPiece> piece)
			{
				Pieces.Add(piece.Key, piece.Value);
			});
			Logger.LogInfo($"loaded {dictionary.Count} pieces from game");
		}

		private static void InitializeSmelters()
		{
			Dictionary<string, GraphProcessor> dictionary = Cache.GetPrefabs(typeof(Smelter)).ToDictionary((KeyValuePair<string, Object> kv) => kv.Key, (KeyValuePair<string, Object> kv) => GraphProcessor.FromSmelter((Smelter)kv.Value));
			dictionary.ToList().ForEach(delegate(KeyValuePair<string, GraphProcessor> smelter)
			{
				Processors.Add(smelter.Key, smelter.Value);
			});
			int num = dictionary.Select((KeyValuePair<string, GraphProcessor> smelter) => smelter.Value.Recipes.Count).Sum();
			Logger.LogInfo($"loaded {dictionary.Count} smelters with {num} conversions from game");
		}

		private static void InitializeIncinerator()
		{
			Dictionary<string, GraphProcessor> dictionary = Cache.GetPrefabs(typeof(Incinerator)).ToDictionary((KeyValuePair<string, Object> kv) => kv.Key, (KeyValuePair<string, Object> kv) => GraphProcessor.FromIncinerator((Incinerator)kv.Value));
			dictionary.ToList().ForEach(delegate(KeyValuePair<string, GraphProcessor> incinerator)
			{
				Processors.Add(incinerator.Key, incinerator.Value);
			});
			int num = dictionary.Select((KeyValuePair<string, GraphProcessor> incinerator) => incinerator.Value.Recipes.Count).Sum();
			Logger.LogInfo($"loaded {dictionary.Count} incinerators with {num} conversions from game");
		}

		private static void InitializeCookingStations()
		{
			Dictionary<string, GraphProcessor> dictionary = Cache.GetPrefabs(typeof(CookingStation)).ToDictionary((KeyValuePair<string, Object> kv) => kv.Key, (KeyValuePair<string, Object> kv) => GraphProcessor.FromCookingStation((CookingStation)kv.Value));
			dictionary.ToList().ForEach(delegate(KeyValuePair<string, GraphProcessor> fermenter)
			{
				Processors.Add(fermenter.Key, fermenter.Value);
			});
			int num = dictionary.Select((KeyValuePair<string, GraphProcessor> smelter) => smelter.Value.Recipes.Count).Sum();
			Logger.LogInfo($"loaded {dictionary.Count} cooking stations with {num} conversions from game");
		}

		private static void InitializeFermenters()
		{
			Dictionary<string, GraphProcessor> dictionary = Cache.GetPrefabs(typeof(Fermenter)).ToDictionary((KeyValuePair<string, Object> kv) => kv.Key, (KeyValuePair<string, Object> kv) => GraphProcessor.FromFermenter((Fermenter)kv.Value));
			dictionary.ToList().ForEach(delegate(KeyValuePair<string, GraphProcessor> fermenter)
			{
				Processors.Add(fermenter.Key, fermenter.Value);
			});
			int num = dictionary.Select((KeyValuePair<string, GraphProcessor> smelter) => smelter.Value.Recipes.Count).Sum();
			Logger.LogInfo($"loaded {dictionary.Count} fermenters with {num} conversions from game");
		}

		private static void InitializeCraftingStations()
		{
			Dictionary<string, GraphCraftingStation> dictionary = GraphCraftingStation.FromExtensionsAndStations(((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(StationExtension))).Select((Func<KeyValuePair<string, Object>, StationExtension>)((KeyValuePair<string, Object> kv) => (StationExtension)kv.Value)).ToList(), ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(CraftingStation))).Select((Func<KeyValuePair<string, Object>, CraftingStation>)((KeyValuePair<string, Object> kv) => (CraftingStation)kv.Value)).ToList());
			dictionary.ToList().ForEach(delegate(KeyValuePair<string, GraphCraftingStation> station)
			{
				CraftingStations.Add(station.Key, station.Value);
			});
			Logger.LogInfo($"loaded {dictionary.Count} crafting stations from game");
		}

		private static void InitializeRecipes()
		{
			foreach (KeyValuePair<string, Recipe> item in ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(Recipe))).ToDictionary((Func<KeyValuePair<string, Object>, string>)((KeyValuePair<string, Object> kv) => kv.Key), (Func<KeyValuePair<string, Object>, Recipe>)((KeyValuePair<string, Object> kv) => (Recipe)kv.Value)))
			{
				if ((Object)(object)item.Value.m_item == (Object)null)
				{
					Logger.LogInfo("recipe '" + item.Key + "' does not create an item - skipping");
				}
				else if ((Object)(object)item.Value.m_craftingStation != (Object)null)
				{
					if (CraftingStations.TryGetValue(((Object)item.Value.m_craftingStation).name, out var value))
					{
						value.Recipes.Add(item.Key, GraphRecipe.FromRecipe(item.Value));
						continue;
					}
					Logger.LogWarning("recipe '" + item.Key + "': station '" + ((Object)item.Value.m_craftingStation).name + "' not found");
				}
				else
				{
					UnboundRecipes.Add(item.Key, GraphRecipe.FromRecipe(item.Value));
				}
			}
			int num = CraftingStations.Select((KeyValuePair<string, GraphCraftingStation> station) => station.Value.Recipes.Count).Sum();
			Logger.LogInfo($"loaded {num} recipes bound to crafting stations from game");
			Logger.LogInfo($"loaded {UnboundRecipes.Count} recipes from game that are not bound to a crafting station");
		}

		private static void InitializeDrops()
		{
			List<Initializer> list = new List<Initializer>();
			list.Add(new ContainerDropsInitializer());
			list.Add(new DestructibleDropsInitializer());
			list.Add(new TreeLogDropsInitializer());
			list.Add(new TreeBaseDropsInitializer());
			list.Add(new CharacterDropInitializer());
			list.Add(new MineRockDropsInitializer());
			list.Add(new MineRock5DropInitializer());
			list.Add(new LootSpawnerDropsInitializer());
			list.Add(new PickableDropInitializer());
			list.Add(new PickableExtraDropInitializer());
			list.Add(new PickableItemDropInitializer());
			list.Add(new PickableItemRandomDropInitializer());
			list.ForEach(delegate(Initializer initializer)
			{
				try
				{
					initializer.InitializeDrops();
				}
				catch (Exception ex)
				{
					Logger.LogError("got exception on initializing: " + ex.Message);
					Logger.LogError(ex.StackTrace);
				}
			});
		}
	}
	public class GraphBuilder
	{
		private readonly DotGraph Graph = new DotGraph().WithIdentifier("graph").Directed();

		private readonly Dictionary<string, DotNode> Nodes = new Dictionary<string, DotNode>();

		private readonly Dictionary<string, DotEdge> Edges = new Dictionary<string, DotEdge>();

		public void AddNode(GraphItem item)
		{
			AddNode(item.ItemName, item.ItemType);
		}

		public void AddNode(string name, string nodeType)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(name))
			{
				Logger.LogWarning("tried adding empty node name");
			}
			else
			{
				if (Nodes.ContainsKey(name))
				{
					return;
				}
				DotNode dotNode = new DotNode().WithIdentifier(name).WithLabel(name).WithShape(DotNodeShape.Rectangle);
				if (nodeType != null)
				{
					DropType result2;
					if (Enum.TryParse<ItemType>(nodeType, out ItemType result))
					{
						SetNodeColorItemType(dotNode, result);
					}
					else if (Enum.TryParse<DropType>(nodeType, out result2))
					{
						SetNodeColorDropType(dotNode, result2);
					}
					else
					{
						if (!Enum.TryParse<NodeType>(nodeType, out var result3))
						{
							throw new ArgumentException("node type '" + nodeType + "' does not match any types known");
						}
						SetNodePieceTypeOption(dotNode, result3);
					}
				}
				Nodes.Add(name, dotNode);
			}
		}

		private static void SetNodeColorDropType(DotNode node, DropType dropType)
		{
			switch (dropType)
			{
			case DropType.Character:
				node.WithColor(DotColor.Orange);
				break;
			case DropType.Container:
			case DropType.LootSpawner:
				node.WithColor(DotColor.Beige);
				break;
			case DropType.Destructible:
				node.WithColor(DotColor.Crimson);
				break;
			case DropType.Pickable:
				node.WithColor(DotColor.Ivory);
				break;
			case DropType.Tree:
				node.WithColor(DotColor.Brown);
				break;
			case DropType.MineRock:
				node.WithShape(DotNodeShape.Diamond);
				node.WithColor(DotColor.SlateGrey);
				break;
			default:
				Logger.LogWarning("node type '" + dropType.ToString() + "' not supported");
				break;
			}
		}

		private static void SetNodeColorItemType(DotNode node, ItemType itemType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected I4, but got Unknown
			switch ((int)itemType)
			{
			case 9:
			case 23:
				node.WithColor(DotColor.Cyan);
				break;
			case 6:
			case 7:
			case 11:
			case 12:
			case 17:
				node.WithColor(DotColor.DarkGrey);
				node.WithFontColor(DotColor.White);
				break;
			case 15:
			case 18:
			case 19:
				node.WithColor(DotColor.Indigo);
				break;
			case 3:
			case 4:
			case 5:
			case 14:
			case 20:
			case 22:
				node.WithColor(DotColor.IndianRed);
				break;
			case 2:
				node.WithColor(DotColor.LightGreen);
				break;
			case 21:
				node.WithColor(DotColor.LightBlue);
				break;
			case 1:
				node.WithColor(DotColor.SandyBrown);
				break;
			case 10:
			case 16:
				node.WithColor(DotColor.Grey);
				node.WithFontColor(DotColor.White);
				break;
			case 0:
				node.WithColor(DotColor.Yellow);
				break;
			case 13:
				node.WithColor(DotColor.Violet);
				node.WithFontColor(DotColor.White);
				break;
			default:
				throw new ArgumentException("type '" + ((object)(ItemType)(ref itemType)).ToString() + "' not supported");
			}
		}

		private static void SetNodePieceTypeOption(DotNode node, NodeType nodeType)
		{
			switch (nodeType)
			{
			case NodeType.Piece:
				node.WithShape(DotNodeShape.Assembly);
				node.WithColor(DotColor.Brown);
				break;
			case NodeType.Processor:
				node.WithShape(DotNodeShape.Circle);
				node.WithColor(DotColor.Gold);
				break;
			case NodeType.CraftingStation:
				node.WithShape(DotNodeShape.Circle);
				node.WithColor(DotColor.Gold);
				break;
			case NodeType.Recipe:
				node.WithShape(DotNodeShape.Note);
				node.WithColor(DotColor.Cornsilk);
				break;
			default:
				throw new ArgumentException("type '" + nodeType.ToString() + "' not supported");
			}
		}

		public void AddEdge(string from, string to)
		{
			string key = from + "->" + to;
			if (!Edges.ContainsKey(key))
			{
				Edges.Add(key, new DotEdge().From(from).To(to));
			}
		}

		public DotGraph BuildGraph()
		{
			Nodes.ToList().ForEach(delegate(KeyValuePair<string, DotNode> node)
			{
				Graph.Add(node.Value);
			});
			Edges.ToList().ForEach(delegate(KeyValuePair<string, DotEdge> edge)
			{
				Graph.Add(edge.Value);
			});
			return Graph;
		}
	}
	public enum NodeType
	{
		Piece,
		CraftingStation,
		Processor,
		Recipe
	}
	public enum DropType
	{
		Character,
		Container,
		Destructible,
		LootSpawner,
		MineRock,
		Pickable,
		Tree
	}
	public enum FilterType
	{
		Include,
		Exclude,
		Unfiltered
	}
}
namespace PrefabDependencyTree.Data.Filters
{
	public class ExcludeFilterTypes : FilteredData
	{
		public ExcludeFilterTypes(List<string> itemTypeFilters)
		{
			ItemTypeFilters = itemTypeFilters;
			FilterType = FilterType.Exclude;
			Items = DataHarvester.Items.Where((KeyValuePair<string, GraphItem> item) => !itemTypeFilters.Contains(item.Value.ItemType)).ToDictionary((KeyValuePair<string, GraphItem> pair) => pair.Key, (KeyValuePair<string, GraphItem> pair) => pair.Value);
			UnboundRecipes = DataHarvester.UnboundRecipes.Where((KeyValuePair<string, GraphRecipe> recipe) => !itemTypeFilters.Contains(recipe.Value.CraftedItem.Item1.ItemType) && !recipe.Value.RequiredItems.Any((KeyValuePair<GraphItem, int> requiredItem) => itemTypeFilters.Contains(requiredItem.Key.ItemType))).ToDictionary((KeyValuePair<string, GraphRecipe> pair) => pair.Key, (KeyValuePair<string, GraphRecipe> pair) => pair.Value);
			CraftingStations = (from station in DataHarvester.CraftingStations.ToDictionary((KeyValuePair<string, GraphCraftingStation> station) => station.Key, delegate(KeyValuePair<string, GraphCraftingStation> station)
				{
					station.Value.RemoveRecipesForTypes(itemTypeFilters);
					return station.Value;
				})
				where station.Value.Recipes.Count > 0
				select station).ToDictionary((KeyValuePair<string, GraphCraftingStation> kv) => kv.Key, (KeyValuePair<string, GraphCraftingStation> kv) => kv.Value);
			Processors = (from processor in DataHarvester.Processors.ToDictionary((KeyValuePair<string, GraphProcessor> processor) => processor.Key, delegate(KeyValuePair<string, GraphProcessor> processor)
				{
					processor.Value.RemoveRecipesForTypes(itemTypeFilters);
					return processor.Value;
				})
				where processor.Value.Recipes.Count > 0
				select processor).ToDictionary((KeyValuePair<string, GraphProcessor> kv) => kv.Key, (KeyValuePair<string, GraphProcessor> kv) => kv.Value);
			Drops = (from drop in DataHarvester.Drops.ToDictionary((KeyValuePair<Tuple<string, DropType>, List<GraphItem>> drop) => drop.Key, delegate(KeyValuePair<Tuple<string, DropType>, List<GraphItem>> drop)
				{
					List<GraphItem> collection = drop.Value.Where((GraphItem item) => !itemTypeFilters.Contains(item.ItemType)).ToList();
					drop.Value.Clear();
					drop.Value.AddRange(collection);
					return drop.Value;
				})
				where drop.Value.Count > 0
				select drop).ToDictionary((KeyValuePair<Tuple<string, DropType>, List<GraphItem>> kv) => kv.Key, (KeyValuePair<Tuple<string, DropType>, List<GraphItem>> kv) => kv.Value);
			Pieces = DataHarvester.Pieces.Where((KeyValuePair<string, GraphPiece> piece) => !piece.Value.BuildRequirements.Any((KeyValuePair<GraphItem, int> requirement) => itemTypeFilters.Contains(requirement.Key.ItemType))).ToDictionary((KeyValuePair<string, GraphPiece> pair) => pair.Key, (KeyValuePair<string, GraphPiece> pair) => pair.Value);
		}
	}
	public abstract class FilteredData
	{
		protected Dictionary<string, GraphItem> Items;

		protected Dictionary<string, GraphRecipe> UnboundRecipes;

		protected Dictionary<string, GraphCraftingStation> CraftingStations;

		protected Dictionary<string, GraphProcessor> Processors;

		protected Dictionary<Tuple<string, DropType>, List<GraphItem>> Drops;

		protected Dictionary<string, GraphPiece> Pieces;

		protected List<string> ItemTypeFilters;

		protected FilterType FilterType;

		public void LogOverview()
		{
			Logger.LogInfo("vvvv filtered data overview vvvv");
			Logger.LogInfo("  applied " + FilterType.ToString() + " filters: " + string.Join(", ", ItemTypeFilters));
			Logger.LogInfo($"    total {Items.Count} items registered");
			Logger.LogInfo($"    total {Drops.Count} drops registered");
			Logger.LogInfo($"    total {Pieces.Count} pieces registered");
			Logger.LogInfo($"    total {CraftingStations.Count} crafting stations registered");
			Logger.LogInfo($"    total {Processors.Count} processor stations (fermenter, smelter, ...) registered");
			Logger.LogInfo($"    total {UnboundRecipes.Count} unbound recipes registered");
			Logger.LogInfo("^^^^ filtered data overview ^^^^");
		}

		public DotGraph CreateGraph()
		{
			GraphBuilder graphBuilder = new GraphBuilder();
			Items.ToList().ForEach(delegate(KeyValuePair<string, GraphItem> item)
			{
				graphBuilder.AddNode(item.Value.ItemName, item.Value.ItemType);
			});
			foreach (KeyValuePair<Tuple<string, DropType>, List<GraphItem>> drop in Drops)
			{
				graphBuilder.AddNode(drop.Key.Item1, drop.Key.Item2.ToString());
				foreach (GraphItem item in drop.Value)
				{
					graphBuilder.AddNode(item);
					graphBuilder.AddEdge(drop.Key.Item1, item.ItemName);
				}
			}
			AddRecipesToGraph(graphBuilder, UnboundRecipes);
			foreach (KeyValuePair<string, GraphCraftingStation> craftingStation in CraftingStations)
			{
				graphBuilder.AddNode(craftingStation.Value.Name, NodeType.CraftingStation.ToString());
				foreach (string extensionName in craftingStation.Value.ExtensionNames)
				{
					graphBuilder.AddNode(extensionName, NodeType.CraftingStation.ToString());
					graphBuilder.AddEdge(extensionName, craftingStation.Value.Name);
				}
				AddRecipesToGraph(graphBuilder, craftingStation.Value.Recipes);
			}
			foreach (KeyValuePair<string, GraphProcessor> processor in Processors)
			{
				graphBuilder.AddNode(processor.Value.Name, NodeType.Processor.ToString());
				AddRecipesToGraph(graphBuilder, processor.Value.Recipes);
			}
			foreach (KeyValuePair<string, GraphPiece> piece in Pieces)
			{
				graphBuilder.AddNode(piece.Value.PieceName, NodeType.Piece.ToString());
				graphBuilder.AddNode(piece.Value.RequiredCraftingStation, NodeType.CraftingStation.ToString());
				graphBuilder.AddEdge(piece.Value.RequiredCraftingStation, piece.Value.PieceName);
				foreach (KeyValuePair<GraphItem, int> buildRequirement in piece.Value.BuildRequirements)
				{
					graphBuilder.AddNode(buildRequirement.Key);
					graphBuilder.AddEdge(buildRequirement.Key.ItemName, piece.Value.PieceName);
				}
			}
			return graphBuilder.BuildGraph();
		}

		private static void AddRecipesToGraph(GraphBuilder graphBuilder, Dictionary<string, GraphRecipe> recipes)
		{
			foreach (KeyValuePair<string, GraphRecipe> recipe in recipes)
			{
				graphBuilder.AddNode(recipe.Value.RecipeName, NodeType.Recipe.ToString());
				graphBuilder.AddNode(recipe.Value.CraftedItem.Item1);
				graphBuilder.AddEdge(recipe.Value.RecipeName, recipe.Value.CraftedItem.Item1.ItemName);
				foreach (KeyValuePair<GraphItem, int> requiredItem in recipe.Value.RequiredItems)
				{
					graphBuilder.AddNode(requiredItem.Key);
					graphBuilder.AddEdge(requiredItem.Key.ItemName, recipe.Value.RecipeName);
				}
			}
		}
	}
	public class IncludeFilterTypes : FilteredData
	{
		public IncludeFilterTypes(List<string> itemTypeFilters)
		{
			ItemTypeFilters = itemTypeFilters;
			FilterType = FilterType.Include;
			Items = DataHarvester.Items.Where((KeyValuePair<string, GraphItem> item) => itemTypeFilters.Contains(item.Value.ItemType)).ToDictionary((KeyValuePair<string, GraphItem> pair) => pair.Key, (KeyValuePair<string, GraphItem> pair) => pair.Value);
			UnboundRecipes = DataHarvester.UnboundRecipes.Where((KeyValuePair<string, GraphRecipe> recipe) => itemTypeFilters.Contains(recipe.Value.CraftedItem.Item1.ItemType) || recipe.Value.RequiredItems.Any((KeyValuePair<GraphItem, int> requiredItem) => itemTypeFilters.Contains(requiredItem.Key.ItemType))).ToDictionary((KeyValuePair<string, GraphRecipe> pair) => pair.Key, (KeyValuePair<string, GraphRecipe> pair) => pair.Value);
			CraftingStations = DataHarvester.CraftingStations.Where((KeyValuePair<string, GraphCraftingStation> station) => station.Value.ContainsItemTypes(itemTypeFilters)).ToDictionary((KeyValuePair<string, GraphCraftingStation> pair) => pair.Key, (KeyValuePair<string, GraphCraftingStation> pair) => pair.Value);
			Processors = DataHarvester.Processors.Where((KeyValuePair<string, GraphProcessor> processor) => processor.Value.ContainsItemTypes(itemTypeFilters)).ToDictionary((KeyValuePair<string, GraphProcessor> pair) => pair.Key, (KeyValuePair<string, GraphProcessor> pair) => pair.Value);
			Drops = DataHarvester.Drops.Where((KeyValuePair<Tuple<string, DropType>, List<GraphItem>> drop) => drop.Value.Any((GraphItem item) => itemTypeFilters.Contains(item.ItemType))).ToDictionary((KeyValuePair<Tuple<string, DropType>, List<GraphItem>> pair) => pair.Key, (KeyValuePair<Tuple<string, DropType>, List<GraphItem>> pair) => pair.Value);
			Pieces = DataHarvester.Pieces.Where((KeyValuePair<string, GraphPiece> piece) => piece.Value.BuildRequirements.Any((KeyValuePair<GraphItem, int> requirement) => itemTypeFilters.Contains(requirement.Key.ItemType))).ToDictionary((KeyValuePair<string, GraphPiece> pair) => pair.Key, (KeyValuePair<string, GraphPiece> pair) => pair.Value);
		}
	}
	public class UnfilteredData : FilteredData
	{
		public UnfilteredData()
		{
			FilterType = FilterType.Unfiltered;
			ItemTypeFilters = new List<string>();
			Items = DataHarvester.Items;
			UnboundRecipes = DataHarvester.UnboundRecipes;
			CraftingStations = DataHarvester.CraftingStations;
			Processors = DataHarvester.Processors;
			Drops = DataHarvester.Drops;
			Pieces = DataHarvester.Pieces;
		}
	}
}
namespace PrefabDependencyTree.Data.Drops
{
	public class CharacterDropInitializer : DropsInitializer<CharacterDrop, Drop>
	{
		protected override Dictionary<Tuple<string, DropType>, CharacterDrop> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(Character))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Character)), (Func<KeyValuePair<string, Object>, CharacterDrop>)((KeyValuePair<string, Object> kv) => (CharacterDrop)((Component)(Character)kv.Value).GetComponent(typeof(CharacterDrop))));
		}

		protected override List<Drop> GetDropList(CharacterDrop input)
		{
			if ((Object)(object)input != (Object)null && input.m_drops != null)
			{
				return input.m_drops.ToList();
			}
			return new List<Drop>();
		}

		protected override GameObject GetObject(Drop input)
		{
			return input.m_prefab;
		}
	}
	public class ContainerDropsInitializer : DropsInitializerDropData<Container>
	{
		protected override Dictionary<Tuple<string, DropType>, Container> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(Container))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Container)), (Func<KeyValuePair<string, Object>, Container>)((KeyValuePair<string, Object> kv) => (Container)kv.Value));
		}

		protected override List<DropData> GetDropList(Container input)
		{
			if (input.m_defaultItems != null)
			{
				return FromTable(input.m_defaultItems);
			}
			Logger.LogWarning($"'m_defaultItems' not found for {typeof(Container)}");
			return new List<DropData>();
		}
	}
	public class DestructibleDropsInitializer : DropsInitializerDropData<DropOnDestroyed>
	{
		protected override Dictionary<Tuple<string, DropType>, DropOnDestroyed> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(DropOnDestroyed))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Destructible)), (Func<KeyValuePair<string, Object>, DropOnDestroyed>)((KeyValuePair<string, Object> kv) => (DropOnDestroyed)kv.Value));
		}

		protected override List<DropData> GetDropList(DropOnDestroyed input)
		{
			if (input.m_dropWhenDestroyed != null)
			{
				return FromTable(input.m_dropWhenDestroyed);
			}
			Logger.LogWarning($"'m_dropWhenDestroyed' not found for {typeof(DropOnDestroyed)}");
			return new List<DropData>();
		}
	}
	public class LootSpawnerDropsInitializer : DropsInitializerDropData<LootSpawner>
	{
		protected override Dictionary<Tuple<string, DropType>, LootSpawner> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(LootSpawner))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.LootSpawner)), (Func<KeyValuePair<string, Object>, LootSpawner>)((KeyValuePair<string, Object> kv) => (LootSpawner)kv.Value));
		}

		protected override List<DropData> GetDropList(LootSpawner input)
		{
			if (input.m_items != null)
			{
				return FromTable(input.m_items);
			}
			Logger.LogWarning($"'m_items' not found for {typeof(LootSpawner)}");
			return new List<DropData>();
		}
	}
	public class MineRock5DropInitializer : DropsInitializerDropData<MineRock5>
	{
		protected override Dictionary<Tuple<string, DropType>, MineRock5> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(MineRock5))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.MineRock)), (Func<KeyValuePair<string, Object>, MineRock5>)((KeyValuePair<string, Object> kv) => (MineRock5)kv.Value));
		}

		protected override List<DropData> GetDropList(MineRock5 input)
		{
			if (input.m_dropItems != null)
			{
				return FromTable(input.m_dropItems);
			}
			Logger.LogWarning($"'m_dropItems' not found for {typeof(MineRock5)}");
			return new List<DropData>();
		}
	}
	public class MineRockDropsInitializer : DropsInitializerDropData<MineRock>
	{
		protected override Dictionary<Tuple<string, DropType>, MineRock> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(MineRock))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.MineRock)), (Func<KeyValuePair<string, Object>, MineRock>)((KeyValuePair<string, Object> kv) => (MineRock)kv.Value));
		}

		protected override List<DropData> GetDropList(MineRock input)
		{
			if (input.m_dropItems != null)
			{
				return FromTable(input.m_dropItems);
			}
			Logger.LogWarning($"'m_dropItems' not found for {typeof(MineRock)}");
			return new List<DropData>();
		}
	}
	public class PickableDropInitializer : DropsInitializer<Pickable, GameObject>
	{
		protected override Dictionary<Tuple<string, DropType>, Pickable> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(Pickable))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Pickable)), (Func<KeyValuePair<string, Object>, Pickable>)((KeyValuePair<string, Object> kv) => (Pickable)kv.Value));
		}

		protected override List<GameObject> GetDropList(Pickable input)
		{
			return new List<GameObject> { input.m_itemPrefab };
		}

		protected override GameObject GetObject(GameObject input)
		{
			return input;
		}
	}
	public class PickableExtraDropInitializer : DropsInitializerDropData<Pickable>
	{
		protected override Dictionary<Tuple<string, DropType>, Pickable> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(Pickable))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Pickable)), (Func<KeyValuePair<string, Object>, Pickable>)((KeyValuePair<string, Object> kv) => (Pickable)kv.Value));
		}

		protected override List<DropData> GetDropList(Pickable input)
		{
			if (input.m_extraDrops != null)
			{
				return FromTable(input.m_extraDrops);
			}
			Logger.LogWarning($"'m_extraDrops' not found for {typeof(Pickable)}");
			return new List<DropData>();
		}
	}
	public class PickableItemDropInitializer : DropsInitializer<PickableItem, ItemDrop>
	{
		protected override Dictionary<Tuple<string, DropType>, PickableItem> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(PickableItem))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Pickable)), (Func<KeyValuePair<string, Object>, PickableItem>)((KeyValuePair<string, Object> kv) => (PickableItem)kv.Value));
		}

		protected override List<ItemDrop> GetDropList(PickableItem input)
		{
			return new List<ItemDrop> { input.m_itemPrefab };
		}

		protected override GameObject GetObject(ItemDrop input)
		{
			return ((Component)input).gameObject;
		}
	}
	public class PickableItemRandomDropInitializer : DropsInitializer<PickableItem, RandomItem>
	{
		protected override Dictionary<Tuple<string, DropType>, PickableItem> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(PickableItem))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Pickable)), (Func<KeyValuePair<string, Object>, PickableItem>)((KeyValuePair<string, Object> kv) => (PickableItem)kv.Value));
		}

		protected override List<RandomItem> GetDropList(PickableItem input)
		{
			if (input.m_randomItemPrefabs != null)
			{
				return input.m_randomItemPrefabs.ToList();
			}
			Logger.LogWarning($"'m_randomItemPrefabs' not found for {typeof(PickableItem)}");
			return new List<RandomItem>();
		}

		protected override GameObject GetObject(RandomItem input)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return ((Component)input.m_itemPrefab).gameObject;
		}
	}
	public class TreeBaseDropsInitializer : DropsInitializerDropData<TreeBase>
	{
		protected override Dictionary<Tuple<string, DropType>, TreeBase> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(TreeBase))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Tree)), (Func<KeyValuePair<string, Object>, TreeBase>)((KeyValuePair<string, Object> kv) => (TreeBase)kv.Value));
		}

		protected override List<DropData> GetDropList(TreeBase input)
		{
			if (input.m_dropWhenDestroyed != null)
			{
				return FromTable(input.m_dropWhenDestroyed);
			}
			Logger.LogWarning($"'m_dropWhenDestroyed' not found for {typeof(TreeBase)}");
			return new List<DropData>();
		}
	}
	public class TreeLogDropsInitializer : DropsInitializerDropData<TreeLog>
	{
		protected override Dictionary<Tuple<string, DropType>, TreeLog> GetGameObjects()
		{
			return ((IEnumerable<KeyValuePair<string, Object>>)Cache.GetPrefabs(typeof(TreeLog))).ToDictionary((Func<KeyValuePair<string, Object>, Tuple<string, DropType>>)((KeyValuePair<string, Object> kv) => Tuple.Create(kv.Key, DropType.Tree)), (Func<KeyValuePair<string, Object>, TreeLog>)((KeyValuePair<string, Object> kv) => (TreeLog)kv.Value));
		}

		protected override List<DropData> GetDropList(TreeLog input)
		{
			if (input.m_dropWhenDestroyed != null)
			{
				return FromTable(input.m_dropWhenDestroyed);
			}
			Logger.LogWarning($"'m_dropWhenDestroyed' not found for {typeof(TreeLog)}");
			return new List<DropData>();
		}
	}
}
namespace PrefabDependencyTree.Data.Drops.Generic
{
	public abstract class DropsInitializer<T, U> : Initializer
	{
		protected abstract Dictionary<Tuple<string, DropType>, T> GetGameObjects();

		protected abstract List<U> GetDropList(T input);

		protected abstract GameObject GetObject(U input);

		public void InitializeDrops()
		{
			int count = DataHarvester.Drops.Count;
			foreach (KeyValuePair<Tuple<string, DropType>, T> gameObject in GetGameObjects())
			{
				List<U> list = (from drop in GetDropList(gameObject.Value)
					where drop != null
					select drop).ToList();
				if (list.Count <= 0)
				{
					Logger.LogInfo($"{typeof(T)} '{gameObject.Key}' does not have drop data - skipping");
					continue;
				}
				List<GraphItem> value;
				List<GraphItem> list2 = (DataHarvester.Drops.TryGetValue(gameObject.Key, out value) ? value : new List<GraphItem>());
				foreach (U item in list)
				{
					if (item == null)
					{
						Logger.LogInfo($"{typeof(T)} - {typeof(U)}: found null drop in drop data (length {list.Count})");
						continue;
					}
					object obj = item;
					ItemDrop val = (ItemDrop)((obj is ItemDrop) ? obj : null);
					if (val != null)
					{
						list2.Add(GraphItem.GetOrCreate(val));
						continue;
					}
					GraphItem orCreate = GraphItem.GetOrCreate(GetObject(item));
					if (orCreate != null)
					{
						list2.Add(orCreate);
					}
				}
				DataHarvester.Drops[Tuple.Create(gameObject.Key.Item1, gameObject.Key.Item2)] = list2.Distinct().ToList();
			}
			Logger.LogInfo($"loaded {DataHarvester.Drops.Count - count} drops from {typeof(T)} ({typeof(U)}) from game");
		}
	}
	public abstract class DropsInitializerDropData<T> : DropsInitializer<T, DropData>
	{
		protected override GameObject GetObject(DropData input)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return input.m_item;
		}

		protected List<DropData> FromTable(DropTable table)
		{
			if (table != null)
			{
				return table.m_drops;
			}
			Logger.LogWarning($"missing drop table for type '{typeof(T)}'");
			return new List<DropData>();
		}
	}
	public interface Initializer
	{
		void InitializeDrops();
	}
}
namespace DotNetGraph.Extensions
{
	internal static class DotBaseGraphExtensions
	{
		public static T WithIdentifier<T>(this T graph, string identifier, bool isHtml = false) where T : DotBaseGraph
		{
			graph.Identifier = new DotIdentifier(identifier, isHtml);
			return graph;
		}

		public static T WithRankDir<T>(this T graph, DotRankDir rankDir) where T : DotBaseGraph
		{
			graph.RankDir = new DotRankDirAttribute(rankDir);
			return graph;
		}

		public static T Add<T>(this T graph, IDotElement element) where T : DotBaseGraph
		{
			graph.Elements.Add(element);
			return graph;
		}
	}
	internal static class DotEdgeExtensions
	{
		public static DotEdge From(this DotEdge edge, string from, bool isHtml = false)
		{
			edge.From = new DotIdentifier(from, isHtml);
			return edge;
		}

		public static DotEdge From(this DotEdge edge, DotNode from)
		{
			edge.From = from.Identifier;
			return edge;
		}

		public static DotEdge To(this DotEdge edge, string to, bool isHtml = false)
		{
			edge.To = new DotIdentifier(to, isHtml);
			return edge;
		}

		public static DotEdge To(this DotEdge edge, DotNode to)
		{
			edge.To = to.Identifier;
			return edge;
		}

		public static DotEdge WithColor(this DotEdge edge, string color)
		{
			edge.Color = new DotColorAttribute(color);
			return edge;
		}

		public static DotEdge WithColor(this DotEdge edge, DotColor color)
		{
			edge.Color = new DotColorAttribute(color);
			return edge;
		}

		public static DotEdge WithStyle(this DotEdge edge, string style)
		{
			edge.Style = new DotEdgeStyleAttribute(style);
			return edge;
		}

		public static DotEdge WithStyle(this DotEdge edge, DotEdgeStyle style)
		{
			edge.Style = new DotEdgeStyleAttribute(style);
			return edge;
		}

		public static DotEdge WithPenWidth(this DotEdge edge, double width)
		{
			edge.PenWidth = new DotDoubleAttribute(width);
			return edge;
		}

		public static DotEdge WithArrowHead(this DotEdge edge, string arrowType)
		{
			edge.ArrowHead = new DotEdgeArrowTypeAttribute(arrowType);
			return edge;
		}

		public static DotEdge WithArrowHead(this DotEdge edge, DotEdgeArrowType arrowType)
		{
			edge.ArrowHead = new DotEdgeArrowTypeAttribute(arrowType);
			return edge;
		}

		public static DotEdge WithArrowTail(this DotEdge edge, string arrowType)
		{
			edge.ArrowTail = new DotEdgeArrowTypeAttribute(arrowType);
			return edge;
		}

		public static DotEdge WithArrowTail(this DotEdge edge, DotEdgeArrowType arrowType)
		{
			edge.ArrowTail = new DotEdgeArrowTypeAttribute(arrowType);
			return edge;
		}

		public static DotEdge WithPos(this DotEdge edge, string value)
		{
			edge.Pos = new DotPointAttribute(value);
			return edge;
		}

		public static DotEdge WithPos(this DotEdge edge, int x, int y, bool @fixed = false)
		{
			edge.Pos = new DotPointAttribute(new DotPoint(x, y, @fixed));
			return edge;
		}

		public static DotEdge WithPos(this DotEdge edge, int x, int y, int z, bool @fixed = false)
		{
			edge.Pos = new DotPointAttribute(new DotPoint(x, y, z, @fixed));
			return edge;
		}
	}
	internal static class DotElementExtensions
	{
		public static T WithAttribute<T>(this T element, string name, IDotAttribute attribute) where T : DotElement
		{
			element.SetAttribute(name, attribute);
			return element;
		}

		public static T WithAttribute<T>(this T element, string name, string value) where T : DotElement
		{
			element.SetAttribute(name, new DotAttribute(value));
			return element;
		}

		public static T WithLabel<T>(this T element, string label, bool isHtml = false) where T : DotElement
		{
			element.Label = new DotLabelAttribute(label, isHtml);
			return element;
		}

		public static T WithFontColor<T>(this T element, string color) where T : DotElement
		{
			element.FontColor = new DotColorAttribute(color);
			return element;
		}

		public static T WithFontColor<T>(this T element, DotColor color) where T : DotElement
		{
			element.FontColor = new DotColorAttribute(color);
			return element;
		}
	}
	internal static class DotGraphExtensions
	{
		public static DotGraph Directed(this DotGraph graph, bool directed = true)
		{
			graph.Directed = directed;
			return graph;
		}

		public static DotGraph Strict(this DotGraph graph, bool strict = true)
		{
			graph.Strict = strict;
			return graph;
		}
	}
	internal static class DotNodeExtensions
	{
		public static DotNode WithIdentifier(this DotNode node, string identifier, bool isHtml = false)
		{
			node.Identifier = new DotIdentifier(identifier, isHtml);
			return node;
		}

		public static DotNode WithColor(this DotNode node, string color)
		{
			node.Color = new DotColorAttribute(color);
			return node;
		}

		public static DotNode WithColor(this DotNode node, DotColor color)
		{
			node.Color = new DotColorAttribute(color);
			return node;
		}

		public static DotNode WithFillColor(this DotNode node, string color)
		{
			node.FillColor = new DotColorAttribute(color);
			return node;
		}

		public static DotNode WithFillColor(this DotNode node, DotColor color)
		{
			node.FillColor = new DotColorAttribute(color);
			return node;
		}

		public static DotNode WithShape(this DotNode node, string shape)
		{
			node.Shape = new DotNodeShapeAttribute(shape);
			return node;
		}

		public static DotNode WithShape(this DotNode node, DotNodeShape shape)
		{
			node.Shape = new DotNodeShapeAttribute(shape);
			return node;
		}

		public static DotNode WithStyle(this DotNode node, string style)
		{
			node.Style = new DotNodeStyleAttribute(style);
			return node;
		}

		public static DotNode WithStyle(this DotNode node, DotNodeStyle style)
		{
			node.Style = new DotNodeStyleAttribute(style);
			return node;
		}

		public static DotNode WithWidth(this DotNode node, double width)
		{
			node.Width = new DotDoubleAttribute(width);
			return node;
		}

		public static DotNode WithHeight(this DotNode node, double height)
		{
			node.Height = new DotDoubleAttribute(height);
			return node;
		}

		public static DotNode WithPenWidth(this DotNode node, double width)
		{
			node.PenWidth = new DotDoubleAttribute(width);
			return node;
		}

		public static DotNode WithPos(this DotNode node, string value)
		{
			node.Pos = new DotPointAttribute(value);
			return node;
		}

		public static DotNode WithPos(this DotNode node, int x, int y, bool @fixed = false)
		{
			node.Pos = new DotPointAttribute(new DotPoint(x, y, @fixed));
			return node;
		}

		public static DotNode WithPos(this DotNode node, int x, int y, int z, bool @fixed = false)
		{
			node.Pos = new DotPointAttribute(new DotPoint(x, y, z, @fixed));
			return node;
		}
	}
	internal static class DotSubgraphExtensions
	{
		public static DotSubgraph WithColor(this DotSubgraph subgraph, string color)
		{
			subgraph.Color = new DotColorAttribute(color);
			return subgraph;
		}

		public static DotSubgraph WithColor(this DotSubgraph subgraph, DotColor color)
		{
			subgraph.Color = new DotColorAttribute(color);
			return subgraph;
		}

		public static DotSubgraph WithStyle(this DotSubgraph subgraph, string style)
		{
			subgraph.Style = new DotSubgraphStyleAttribute(style);
			return subgraph;
		}

		public static DotSubgraph WithStyle(this DotSubgraph subgraph, DotSubgraphStyle style)
		{
			subgraph.Style = new DotSubgraphStyleAttribute(style);
			return subgraph;
		}
	}
	internal static class EnumExtensions
	{
		public static string FlagsToString<T>(this T enumValue) where T : Enum
		{
			if (typeof(T).GetCustomAttribute<FlagsAttribute>() == null)
			{
				throw new InvalidOperationException($"The type '{typeof(T)}' doesn't have the [Flags] attribute specified.");
			}
			return string.Join(",", from T a in Enum.GetValues(typeof(T))
				where enumValue.HasFlag(a)
				select a.ToString().ToLowerInvariant());
		}
	}
	internal static class StringExtensions
	{
		internal static string FormatGraphvizEscapedCharacters(this string value)
		{
			return value?.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r\n", "\\n")
				.Replace("\n", "\\n");
		}
	}
}
namespace DotNetGraph.Core
{
	internal abstract class DotBaseGraph : DotElement
	{
		public DotIdentifier Identifier { get; set; }

		public DotRankDirAttribute RankDir
		{
			get
			{
				return GetAttributeOrDefault<DotRankDirAttribute>("rankdir");
			}
			set
			{
				SetAttribute("rankdir", value);
			}
		}

		public List<IDotElement> Elements { get; } = new List<IDotElement>();


		public DotNode GetNodeByIdentifier(string identifier, bool isHtml = false)
		{
			return Elements.Where((IDotElement e) => e is DotNode).Cast<DotNode>().FirstOrDefault((DotNode node) => node.Identifier == new DotIdentifier(identifier, isHtml));
		}

		public abstract override Task CompileAsync(CompilationContext context);

		protected async Task CompileBodyAsync(CompilationContext context)
		{
			await Identifier.CompileAsync(context);
			await context.WriteLineAsync(" {");
			context.IndentationLevel++;
			await CompileAttributesAsync(context);
			foreach (IDotElement element in Elements)
			{
				await element.CompileAsync(context);
			}
			context.IndentationLevel--;
			await context.WriteIndentationAsync();
			await context.WriteLineAsync("}");
		}
	}
	internal struct DotColor : IEquatable<DotColor>
	{
		public byte R;

		public byte G;

		public byte B;

		public byte A;

		public static DotColor AliceBlue { get; } = new DotColor(240, 248, byte.MaxValue);


		public static DotColor AntiqueWhite { get; } = new DotColor(250, 235, 215);


		public static DotColor Aqua { get; } = new DotColor(0, byte.MaxValue, byte.MaxValue);


		public static DotColor Aquamarine { get; } = new DotColor(127, byte.MaxValue, 212);


		public static DotColor Azure { get; } = new DotColor(240, byte.MaxValue, byte.MaxValue);


		public static DotColor Beige { get; } = new DotColor(245, 245, 220);


		public static DotColor Bisque { get; } = new DotColor(byte.MaxValue, 228, 196);


		public static DotColor Black { get; } = new DotColor(0, 0, 0);


		public static DotColor BlanchedAlmond { get; } = new DotColor(byte.MaxValue, 235, 205);


		public static DotColor Blue { get; } = new DotColor(0, 0, byte.MaxValue);


		public static DotColor BlueViolet { get; } = new DotColor(138, 43, 226);


		public static DotColor Brown { get; } = new DotColor(165, 42, 42);


		public static DotColor Burlywood { get; } = new DotColor(222, 184, 135);


		public static DotColor CadetBlue { get; } = new DotColor(95, 158, 160);


		public static DotColor Chartreuse { get; } = new DotColor(127, byte.MaxValue, 0);


		public static DotColor Chocolate { get; } = new DotColor(210, 105, 30);


		public static DotColor Coral { get; } = new DotColor(byte.MaxValue, 127, 80);


		public static DotColor CornflowerBlue { get; } = new DotColor(100, 149, 237);


		public static DotColor Cornsilk { get; } = new DotColor(byte.MaxValue, 248, 220);


		public static DotColor Crimson { get; } = new DotColor(220, 20, 60);


		public static DotColor Cyan { get; } = new DotColor(0, byte.MaxValue, byte.MaxValue);


		public static DotColor DarkBlue { get; } = new DotColor(0, 0, 139);


		public static DotColor DarkCyan { get; } = new DotColor(0, 139, 139);


		public static DotColor DarkGoldenrod { get; } = new DotColor(184, 134, 11);


		public static DotColor DarkGray { get; } = new DotColor(169, 169, 169);


		public static DotColor DarkGreen { get; } = new DotColor(0, 100, 0);


		public static DotColor DarkGrey { get; } = new DotColor(169, 169, 169);


		public static DotColor DarkKhaki { get; } = new DotColor(189, 183, 107);


		public static DotColor DarkMagenta { get; } = new DotColor(139, 0, 139);


		public static DotColor DarkOliveGreen { get; } = new DotColor(85, 107, 47);


		public static DotColor Darkorange { get; } = new DotColor(byte.MaxValue, 140, 0);


		public static DotColor DarkOrchid { get; } = new DotColor(153, 50, 204);


		public static DotColor DarkRed { get; } = new DotColor(139, 0, 0);


		public static DotColor DarkSalmon { get; } = new DotColor(233, 150, 122);


		public static DotColor DarkSeaGreen { get; } = new DotColor(143, 188, 143);


		public static DotColor DarkSlateBlue { get; } = new DotColor(72, 61, 139);


		public static DotColor DarkSlateGray { get; } = new DotColor(47, 79, 79);


		public static DotColor DarkSlateGrey { get; } = new DotColor(47, 79, 79);


		public static DotColor DarkTurquoise { get; } = new DotColor(0, 206, 209);


		public static DotColor DarkViolet { get; } = new DotColor(148, 0, 211);


		public static DotColor DeepPink { get; } = new DotColor(byte.MaxValue, 20, 147);


		public static DotColor DeepSkyBlue { get; } = new DotColor(0, 191, byte.MaxValue);


		public static DotColor DimGray { get; } = new DotColor(105, 105, 105);


		public static DotColor DimGrey { get; } = new DotColor(105, 105, 105);


		public static DotColor DodgerBlue { get; } = new DotColor(30, 144, byte.MaxValue);


		public static DotColor Firebrick { get; } = new DotColor(178, 34, 34);


		public static DotColor FloralWhite { get; } = new DotColor(byte.MaxValue, 250, 240);


		public static DotColor ForestGreen { get; } = new DotColor(34, 139, 34);


		public static DotColor Fuchsia { get; } = new DotColor(byte.MaxValue, 0, byte.MaxValue);


		public static DotColor Gainsboro { get; } = new DotColor(220, 220, 220);


		public static DotColor GhostWhite { get; } = new DotColor(248, 248, byte.MaxValue);


		public static DotColor Gold { get; } = new DotColor(byte.MaxValue, 215, 0);


		public static DotColor Goldenrod { get; } = new DotColor(218, 165, 32);


		public static DotColor Gray { get; } = new DotColor(128, 128, 128);


		public static DotColor Green { get; } = new DotColor(0, 128, 0);


		public static DotColor GreenYellow { get; } = new DotColor(173, byte.MaxValue, 47);


		public static DotColor Grey { get; } = new DotColor(128, 128, 128);


		public static DotColor Honeydew { get; } = new DotColor(240, byte.MaxValue, 240);


		public static DotColor HotPink { get; } = new DotColor(byte.MaxValue, 105, 180);


		public static DotColor IndianRed { get; } = new DotColor(205, 92, 92);


		public static DotColor Indigo { get; } = new DotColor(75, 0, 130);


		public static DotColor Ivory { get; } = new DotColor(byte.MaxValue, byte.MaxValue, 240);


		public static DotColor Khaki { get; } = new DotColor(240, 230, 140);


		public static DotColor Lavender { get; } = new DotColor(230, 230, 250);


		public static DotColor LavenderBlush { get; } = new DotColor(byte.MaxValue, 240, 245);


		public static DotColor LawnGreen { get; } = new DotColor(124, 252, 0);


		public static DotColor LemonChiffon { get; } = new DotColor(byte.MaxValue, 250, 205);


		public static DotColor LightBlue { get; } = new DotColor(173, 216, 230);


		public static DotColor LightCoral { get; } = new DotColor(240, 128, 128);


		public static DotColor LightCyan { get; } = new DotColor(224, byte.MaxValue, byte.MaxValue);


		public static DotColor LightGoldenrodYellow { get; } = new DotColor(250, 250, 210);


		public static DotColor LightGray { get; } = new DotColor(211, 211, 211);


		public static DotColor LightGrey { get; } = new DotColor(211, 211, 211);


		public static DotColor LightGreen { get; } = new DotColor(144, 238, 144);


		public static DotColor LightPink { get; } = new DotColor(byte.MaxValue, 182, 193);


		public static DotColor LightSalmon { get; } = new DotColor(byte.MaxValue, 160, 122);


		public static DotColor LightSeaGreen { get; } = new DotColor(32, 178, 170);


		public static DotColor LightSkyBlue { get; } = new DotColor(135, 206, 250);


		public static DotColor LightSlateGray { get; } = new DotColor(119, 136, 153);


		public static DotColor LightSlateGrey { get; } = new DotColor(119, 136, 153);


		public static DotColor LightSteelBlue { get; } = new DotColor(176, 196, 222);


		public static DotColor LightYellow { get; } = new DotColor(byte.MaxValue, byte.MaxValue, 224);


		public static DotColor Lime { get; } = new DotColor(0, byte.MaxValue, 0);


		public static DotColor LimeGreen { get; } = new DotColor(50, 205, 50);


		public static DotColor Linen { get; } = new DotColor(250, 240, 230);


		public static DotColor Magenta { get; } = new DotColor(byte.MaxValue, 0, byte.MaxValue);


		public static DotColor Maroon { get; } = new DotColor(128, 0, 0);


		public static DotColor MediumAquamarine { get; } = new DotColor(102, 205, 170);


		public static DotColor MediumBlue { get; } = new DotColor(0, 0, 205);


		public static DotColor MediumOrchid { get; } = new DotColor(186, 85, 211);


		public static DotColor MediumPurple { get; } = new DotColor(147, 112, 219);


		public static DotColor MediumSeaGreen { get; } = new DotColor(60, 179, 113);


		public static DotColor MediumSlateBlue { get; } = new DotColor(123, 104, 238);


		public static DotColor MediumSpringGreen { get; } = new DotColor(0, 250, 154);


		public static DotColor MediumTurquoise { get; } = new DotColor(72, 209, 204);


		public static DotColor MediumVioletRed { get; } = new DotColor(199, 21, 133);


		public static DotColor MidnightBlue { get; } = new DotColor(25, 25, 112);


		public static DotColor MintCream { get; } = new DotColor(245, byte.MaxValue, 250);


		public static DotColor MistyRose { get; } = new DotColor(byte.MaxValue, 228, 225);


		public static DotColor Moccasin { get; } = new DotColor(byte.MaxValue, 228, 181);


		public static DotColor NavajoWhite { get; } = new DotColor(byte.MaxValue, 222, 173);


		public static DotColor Navy { get; } = new DotColor(0, 0, 128);


		public static DotColor OldLace { get; } = new DotColor(253, 245, 230);


		public static DotColor Olive { get; } = new DotColor(128, 128, 0);


		public static DotColor OliveDrab { get; } = new DotColor(107, 142, 35);


		public static DotColor Orange { get; } = new DotColor(byte.MaxValue, 165, 0);


		public static DotColor OrangeRed { get; } = new DotColor(byte.MaxValue, 69, 0);


		public static DotColor Orchid { get; } = new DotColor(218, 112, 214);


		public static DotColor PaleGoldenrod { get; } = new DotColor(238, 232, 170);


		public static DotColor PaleGreen { get; } = new DotColor(152, 251, 152);


		public static DotColor PaleTurquoise { get; } = new DotColor(175, 238, 238);


		public static DotColor PaleVioletRed { get; } = new DotColor(219, 112, 147);


		public static DotColor PapayaWhip { get; } = new DotColor(byte.MaxValue, 239, 213);


		public static DotColor PeachPuff { get; } = new DotColor(byte.MaxValue, 218, 185);


		public static DotColor Peru { get; } = new DotColor(205, 133, 63);


		public static DotColor Pink { get; } = new DotColor(byte.MaxValue, 192, 203);


		public static DotColor Plum { get; } = new DotColor(221, 160, 221);


		public static DotColor PowderBlue { get; } = new DotColor(176, 224, 230);


		public static DotColor Purple { get; } = new DotColor(128, 0, 128);


		public static DotColor Red { get; } = new DotColor(byte.MaxValue, 0, 0);


		public static DotColor RosyBrown { get; } = new DotColor(188, 143, 143);


		public static DotColor RoyalBlue { get; } = new DotColor(65, 105, 225);


		public static DotColor SaddleBrown { get; } = new DotColor(139, 69, 19);


		public static DotColor Salmon { get; } = new DotColor(250, 128, 114);


		public static DotColor SandyBrown { get; } = new DotColor(244, 164, 96);


		public static DotColor SeaGreen { get; } = new DotColor(46, 139, 87);


		public static DotColor SeaShell { get; } = new DotColor(byte.MaxValue, 245, 238);


		public static DotColor Sienna { get; } = new DotColor(160, 82, 45);


		public static DotColor Silver { get; } = new DotColor(192, 192, 192);


		public static DotColor SkyBlue { get; } = new DotColor(135, 206, 235);


		public static DotColor SlateBlue { get; } = new DotColor(106, 90, 205);


		public static DotColor SlateGray { get; } = new DotColor(112, 128, 144);


		public static DotColor SlateGrey { get; } = new DotColor(112, 128, 144);


		public static DotColor Snow { get; } = new DotColor(byte.MaxValue, 250, 250);


		public static DotColor SpringGreen { get; } = new DotColor(0, byte.MaxValue, 127);


		public static DotColor SteelBlue { get; } = new DotColor(70, 130, 180);


		public static DotColor Tan { get; } = new DotColor(210, 180, 140);


		public static DotColor Teal { get; } = new DotColor(0, 128, 128);


		public static DotColor Thistle { get; } = new DotColor(216, 191, 216);


		public static DotColor Tomato { get; } = new DotColor(byte.MaxValue, 99, 71);


		public static DotColor Turquoise { get; } = new DotColor(64, 224, 208);


		public static DotColor Violet { get; } = new DotColor(238, 130, 238);


		public static DotColor Wheat { get; } = new DotColor(245, 222, 179);


		public static DotColor White { get; } = new DotColor(byte.MaxValue, byte.MaxValue, byte.MaxValue);


		public static DotColor WhiteSmoke { get; } = new DotColor(245, 245, 245);


		public static DotColor Yellow { get; } = new DotColor(byte.MaxValue, byte.MaxValue, 0);


		public static DotColor YellowGreen { get; } = new DotColor(154, 205, 50);


		public DotColor(byte r, byte g, byte b, byte a = byte.MaxValue)
		{
			R = r;
			G = g;
			B = b;
			A = a;
		}

		public bool Equals(DotColor other)
		{
			if (R == other.R && G == other.G && B == other.B)
			{
				return A == other.A;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj is DotColor other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (((((R.GetHashCode() * 397) ^ G.GetHashCode()) * 397) ^ B.GetHashCode()) * 397) ^ A.GetHashCode();
		}

		public override string ToString()
		{
			return $"[DotColor, R:{R}, G:{G}, B:{B}, A:{A}]";
		}

		public string ToHexString()
		{
			if (A != byte.MaxValue)
			{
				return $"#{R:X2}{G:X2}{B:X2}{A:X2}";
			}
			return $"#{R:X2}{G:X2}{B:X2}";
		}
	}
	internal class DotEdge : DotElement
	{
		public DotIdentifier From { get; set; }

		public DotIdentifier To { get; set; }

		public DotColorAttribute Color
		{
			get
			{
				return GetAttributeOrDefault<DotColorAttribute>("color");
			}
			set
			{
				SetAttribute("color", value);
			}
		}

		public DotEdgeStyleAttribute Style
		{
			get
			{
				return GetAttributeOrDefault<DotEdgeStyleAttribute>("style");
			}
			set
			{
				SetAttribute("style", value);
			}
		}

		public DotDoubleAttribute PenWidth
		{
			get
			{
				return GetAttribute<DotDoubleAttribute>("penwidth");
			}
			set
			{
				SetAttribute("penwidth", value);
			}
		}

		public DotEdgeArrowTypeAttribute ArrowHead
		{
			get
			{
				return GetAttribute<DotEdgeArrowTypeAttribute>("arrowhead");
			}
			set
			{
				SetAttribute("arrowhead", value);
			}
		}

		public DotEdgeArrowTypeAttribute ArrowTail
		{
			get
			{
				return GetAttribute<DotEdgeArrowTypeAttribute>("arrowtail");
			}
			set
			{
				SetAttribute("arrowtail", value);
			}
		}

		public DotPointAttribute Pos
		{
			get
			{
				return GetAttribute<DotPointAttribute>("pos");
			}
			set
			{
				SetAttribute("pos", value);
			}
		}

		public override async Task CompileAsync(CompilationContext context)
		{
			if ((object)From == null || (object)To == null)
			{
				throw new Exception("Can't compile edge with null From and/or To");
			}
			await context.WriteIndentationAsync();
			await From.CompileAsync(context);
			await context.WriteAsync(" " + (context.DirectedGraph ? "->" : "--") + " ");
			await To.CompileAsync(context);
			if (Attributes.Any())
			{
				await context.WriteLineAsync(" [");
				context.IndentationLevel++;
				await CompileAttributesAsync(context);
				context.IndentationLevel--;
				await context.WriteIndentationAsync();
				await context.WriteLineAsync("]");
			}
			else
			{
				await context.WriteLineAsync();
			}
		}
	}
	internal enum DotEdgeArrowType
	{
		Normal,
		Inv,
		Dot,
		InvDot,
		ODot,
		InvODot,
		None,
		Tee,
		Empty,
		InvEmpty,
		Diamond,
		ODiamond,
		EDiamond,
		Crow,
		Box,
		OBox,
		Open,
		HalfOpen,
		Vee
	}
	[Flags]
	internal enum DotEdgeStyle
	{
		Solid = 1,
		Dashed = 2,
		Dotted = 4,
		Bold = 8,
		Tapered = 0x10,
		Invis = 0x20
	}
	internal abstract class DotElement : IDotElement
	{
		protected readonly Dictionary<string, IDotAttribute> Attributes = new Dictionary<string, IDotAttribute>();

		public DotLabelAttribute Label
		{
			get
			{
				return GetAttributeOrDefault<DotLabelAttribute>("label");
			}
			set
			{
				SetAttribute("label", value);
			}
		}

		public DotColorAttribute FontColor
		{
			get
			{
				return GetAttributeOrDefault<DotColorAttribute>("fontcolor");
			}
			set
			{
				SetAttribute("fontcolor", value);
			}
		}

		public bool HasAttribute(string name)
		{
			return Attributes.ContainsKey(name);
		}

		public IDotAttribute GetAttribute(string name)
		{
			if (Attributes.TryGetValue(name, out var value))
			{
				return value;
			}
			throw new Exception("There is no attribute named '" + name + "'");
		}

		public IDotAttribute GetAttributeOrDefault(string name, IDotAttribute defaultValue = null)
		{
			if (Attributes.TryGetValue(name, out var value))
			{
				return value;
			}
			return defaultValue;
		}

		public T GetAttribute<T>(string name) where T : IDotAttribute
		{
			IDotAttribute attribute = GetAttribute(name);
			if (attribute is T)
			{
				return (T)attribute;
			}
			throw new Exception($"Attribute with name '{name}' doesn't match the expected type (expected: {typeof(T)}, current: {attribute.GetType()})");
		}

		public T GetAttributeOrDefault<T>(string name, T defaultValue = default(T)) where T : IDotAttribute
		{
			if (Attributes.TryGetValue(name, out var value))
			{
				if (value is T)
				{
					return (T)value;
				}
				throw new Exception($"Attribute with name '{name}' doesn't match the expected type (expected: {typeof(T)}, current: {value.GetType()})");
			}
			return defaultValue;
		}

		public bool TryGetAttribute(string name, out IDotAttribute attribute)
		{
			if (Attributes.TryGetValue(name, out var value))
			{
				attribute = value;
				return true;
			}
			attribute = null;
			return false;
		}

		public bool TryGetAttribute<T>(string name, out T attribute) where T : IDotAttribute
		{
			if (!TryGetAttribute(name, out var attribute2))
			{
				attribute = default(T);
				return false;
			}
			if (attribute2 is T val)
			{
				attribute = val;
				return true;
			}
			throw new Exception($"Attribute with name '{name}' doesn't match the expected type (expected: {typeof(T)}, current: {attribute2.GetType()})");
		}

		public void SetAttribute(string name, IDotAttribute value)
		{
			if (value == null)
			{
				RemoveAttribute(name);
			}
			else
			{
				Attributes[name] = value;
			}
		}

		public bool RemoveAttribute(string name)
		{
			return Attributes.Remove(name);
		}

		protected async Task CompileAttributesAsync(CompilationContext context)
		{
			foreach (KeyValuePair<string, IDotAttribute> attributePair in Attributes)
			{
				await context.WriteIndentationAsync();
				await context.WriteAsync("\"" + attributePair.Key + "\"=");
				await attributePair.Value.CompileAsync(context);
				await context.WriteLineAsync();
			}
		}

		public abstract Task CompileAsync(CompilationContext context);
	}
	internal class DotGraph : DotBaseGraph
	{
		public bool Strict { get; set; }

		public bool Directed { get; set; }

		public override async Task CompileAsync(CompilationContext context)
		{
			context.DirectedGraph = Directed;
			await context.WriteIndentationAsync();
			if (Strict)
			{
				await context.WriteAsync("strict ");
			}
			await context.WriteAsync(Directed ? "digraph " : "graph ");
			await CompileBodyAsync(context);
		}
	}
	internal class DotIdentifier : IDotElement, IEquatable<DotIdentifier>
	{
		private static readonly Regex NoQuotesRequiredRegex = new Regex("^([a-zA-Z\\200-\\377_][a-zA-Z\\200-\\3770-9_]*|[-]?(.[0-9]+|[0-9]+(.[0-9]+)?))$");

		private static readonly string[] ReservedWords = new string[6] { "graph", "digraph", "subgraph", "strict", "node", "edge" };

		public string Value { get; set; }

		public bool IsHtml { get; set; }

		public DotIdentifier(string value, bool isHtml = false)
		{
			Value = value;
			IsHtml = isHtml;
		}

		public async Task CompileAsync(CompilationContext context)
		{
			if (IsHtml)
			{
				await context.TextWriter.WriteAsync("<" + Value + ">");
				return;
			}
			string text = (context.Options.AutomaticEscapedCharactersFormat ? Value.FormatGraphvizEscapedCharacters() : Value);
			if (RequiresDoubleQuotes(text))
			{
				await context.TextWriter.WriteAsync("\"" + text + "\"");
			}
			else
			{
				await context.TextWriter.WriteAsync(text ?? "");
			}
		}

		private static bool RequiresDoubleQuotes(string value)
		{
			if (!ReservedWords.Contains(value))
			{
				return !NoQuotesRequiredRegex.IsMatch(value);
			}
			return true;
		}

		public bool Equals(DotIdentifier other)
		{
			if ((object)other == null)
			{
				return false;
			}
			if ((object)this == other)
			{
				return true;
			}
			if (Value == other.Value)
			{
				return IsHtml == other.IsHtml;
			}
			return false;
		}

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

		public override int GetHashCode()
		{
			return (((Value != null) ? Value.GetHashCode() : 0) * 397) ^ IsHtml.GetHashCode();
		}

		public static bool operator ==(DotIdentifier identifier1, DotIdentifier identifier2)
		{
			return identifier1?.Equals(identifier2) ?? ((object)identifier2 == null);
		}

		public static bool operator !=(DotIdentifier identifier1, DotIdentifier identifier2)
		{
			return !(identifier1 == identifier2);
		}
	}
	internal class DotNode : DotElement
	{
		public DotIdentifier Identifier { get; set; }

		public DotColorAttribute Color
		{
			get
			{
				return GetAttributeOrDefault<DotColorAttribute>("color");
			}
			set
			{
				SetAttribute("color", value);
			}
		}

		public DotColorAttribute FillColor
		{
			get
			{
				return GetAttributeOrDefault<DotColorAttribute>("fillcolor");
			}
			set
			{
				SetAttribute("fillcolor", value);
			}
		}

		public DotNodeShapeAttribute Shape
		{
			get
			{
				return GetAttributeOrDefault<DotNodeShapeAttribute>("shape");
			}
			set
			{
				SetAttribute("shape", value);
			}
		}

		public DotNodeStyleAttribute Style
		{
			get
			{
				return GetAttributeOrDefault<DotNodeStyleAttribute>("style");
			}
			set
			{
				SetAttribute("style", value);
			}
		}

		public DotDoubleAttribute Width
		{
			get
			{
				return GetAttribute<DotDoubleAttribute>("width");
			}
			set
			{
				SetAttribute("width", value);
			}
		}

		public DotDoubleAttribute Height
		{
			get
			{
				return GetAttribute<DotDoubleAttribute>("height");
			}
			set
			{
				SetAttribute("height", value);
			}
		}

		public DotDoubleAttribute PenWidth
		{
			get
			{
				return GetAttribute<DotDoubleAttribute>("penwidth");
			}
			set
			{
				SetAttribute("penwidth", value);
			}
		}

		public DotPointAttribute Pos
		{
			get
			{
				return GetAttribute<DotPointAttribute>("pos");
			}
			set
			{
				SetAttribute("pos", value);
			}
		}

		public override async Task CompileAsync(CompilationContext context)
		{
			await context.WriteIndentationAsync();
			await Identifier.CompileAsync(context);
			if (Attributes.Any())
			{
				await context.WriteLineAsync(" [");
				context.IndentationLevel++;
				await CompileAttributesAsync(context);
				context.IndentationLevel--;
				await context.WriteIndentationAsync();
				await context.WriteLineAsync("]");
			}
			else
			{
				await context.WriteLineAsync();
			}
		}
	}
	internal enum DotNodeShape
	{
		Box,
		Polygon,
		Ellipse,
		Oval,
		Circle,
		Point,
		Egg,
		Triangle,
		PlainText,
		Plain,
		Diamond,
		Trapezium,
		Parallelogram,
		House,
		Pentagon,
		Hexagon,
		Septagon,
		Octagon,
		DoubleCircle,
		DoubleOctagon,
		TripleOctagon,
		InvTriangle,
		InvTrapezium,
		InvHouse,
		MDiamond,
		MSquare,
		MCircle,
		Rect,
		Rectangle,
		Square,
		Star,
		None,
		Underline,
		Cylinder,
		Note,
		Tab,
		Folder,
		Box3D,
		Component,
		Promoter,
		CDS,
		Terminator,
		UTR,
		PrimerSite,
		RestrictionSite,
		FivePOverHang,
		ThreePOveHang,
		NOverHang,
		Assembly,
		Signature,
		Insulator,
		Ribosite,
		RNasTab,
		ProteaseSite,
		ProteinsTab,
		RPromoter,
		RArrow,
		LArrow,
		LPromoter
	}
	[Flags]
	internal enum DotNodeStyle
	{
		Solid = 1,
		Dashed = 2,
		Dotted = 4,
		Bold = 8,
		Rounded = 0x10,
		Diagonals = 0x20,
		Filled = 0x40,
		Striped = 0x80,
		Wedged = 0x100,
		Invis = 0x200
	}
	internal struct DotPoint
	{
		public int X;

		public int Y;

		public int? Z;

		public bool Fixed;

		public DotPoint(int x, int y, bool @fixed = false)
		{
			X = x;
			Y = y;
			Z = null;
			Fixed = @fixed;
		}

		public DotPoint(int x, int y, int z, bool @fixed = false)
		{
			X = x;
			Y = y;
			Z = z;
			Fixed = @fixed;
		}

		public override string ToString()
		{
			string text = $"{X},{Y}";
			if (Z.HasValue)
			{
				text += $",{Z}";
			}
			if (Fixed)
			{
				text += "!";
			}
			return text;
		}
	}
	internal enum DotRankDir
	{
		TB,
		BT,
		LR,
		RL
	}
	internal class DotSubgraph : DotBaseGraph
	{
		public DotColorAttribute Color
		{
			get
			{
				return GetAttributeOrDefault<DotColorAttribute>("color");
			}
			set
			{
				SetAttribute("color", value);
			}
		}

		public DotSubgraphStyleAttribute Style
		{
			get
			{
				return GetAttributeOrDefault<DotSubgraphStyleAttribute>("style");
			}
			set
			{
				SetAttribute("style", value);
			}
		}

		public override async Task CompileAsync(CompilationContext context)
		{
			await context.WriteIndentationAsync();
			await context.WriteAsync("subgraph ");
			await CompileBodyAsync(context);
		}
	}
	[Flags]
	internal enum DotSubgraphStyle
	{
		Solid = 1,
		Dashed = 2,
		Dotted = 4,
		Bold = 8,
		Rounded = 0x10,
		Filled = 0x20,
		Striped = 0x40,
		Invis = 0x80
	}
	internal interface IDotElement
	{
		Task CompileAsync(CompilationContext context);
	}
}
namespace DotNetGraph.Compilation
{
	internal class CompilationContext
	{
		public TextWriter TextWriter { get; }

		public CompilationOptions Options { get; }

		public int IndentationLevel { get; set; }

		public bool DirectedGraph { get; set; }

		public CompilationContext(TextWriter textWriter, CompilationOptions options)
		{
			TextWriter = textWriter;
			Options = options;
			IndentationLevel = 0;
			DirectedGraph = false;
		}

		public async Task WriteIndentationAsync()
		{
			if (Options.Indented)
			{
				for (int i = 0; i < IndentationLevel; i++)
				{
					await TextWriter.WriteAsync("\t");
				}
			}
		}

		public async Task WriteAsync(string value)
		{
			await TextWriter.WriteAsync(value);
		}

		public async Task WriteLineAsync(string value = null)
		{
			await TextWriter.WriteAsync(value);
			await TextWriter.WriteAsync(Options.Indented ? '\n' : ' ');
		}
	}
	internal class CompilationOptions
	{
		public bool AutomaticEscapedCharactersFormat { get; set; } = true;


		public bool Indented { get; set; } = true;

	}
}
namespace DotNetGraph.Attributes
{
	internal class DotAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotAttribute(string value)
		{
			Value = value;
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync(Value);
		}
	}
	internal class DotColorAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotColorAttribute(DotColor color)
		{
			Value = color.ToHexString();
		}

		public DotColorAttribute(string value)
		{
			Value = value;
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync("\"" + Value + "\"");
		}

		public static implicit operator DotColorAttribute(DotColor value)
		{
			return new DotColorAttribute(value);
		}

		public static implicit operator DotColorAttribute(string value)
		{
			return new DotColorAttribute(value);
		}
	}
	internal class DotDoubleAttribute : IDotAttribute, IDotElement
	{
		public double Value { get; set; }

		public string Format { get; set; }

		public DotDoubleAttribute(double value, string format = "F2")
		{
			Value = value;
			Format = format;
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync(Value.ToString(Format, NumberFormatInfo.InvariantInfo));
		}

		public static implicit operator DotDoubleAttribute(double value)
		{
			return new DotDoubleAttribute(value);
		}
	}
	internal class DotEdgeArrowTypeAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotEdgeArrowTypeAttribute(string value)
		{
			Value = value;
		}

		public DotEdgeArrowTypeAttribute(DotEdgeArrowType type)
		{
			Value = type.ToString().ToLowerInvariant();
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync("\"" + Value + "\"");
		}

		public static implicit operator DotEdgeArrowTypeAttribute(DotEdgeArrowType value)
		{
			return new DotEdgeArrowTypeAttribute(value);
		}

		public static implicit operator DotEdgeArrowTypeAttribute(string value)
		{
			return new DotEdgeArrowTypeAttribute(value);
		}
	}
	internal class DotEdgeStyleAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotEdgeStyleAttribute(string value)
		{
			Value = value;
		}

		public DotEdgeStyleAttribute(DotEdgeStyle style)
		{
			Value = style.FlagsToString();
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync("\"" + Value + "\"");
		}

		public static implicit operator DotEdgeStyleAttribute(DotEdgeStyle value)
		{
			return new DotEdgeStyleAttribute(value);
		}

		public static implicit operator DotEdgeStyleAttribute(string value)
		{
			return new DotEdgeStyleAttribute(value);
		}
	}
	internal class DotLabelAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public bool IsHtml { get; set; }

		public DotLabelAttribute(string value, bool isHtml = false)
		{
			Value = value;
			IsHtml = isHtml;
		}

		public async Task CompileAsync(CompilationContext context)
		{
			if (IsHtml)
			{
				await context.TextWriter.WriteAsync("<" + Value + ">");
				return;
			}
			string text = (context.Options.AutomaticEscapedCharactersFormat ? Value.FormatGraphvizEscapedCharacters() : Value);
			await context.TextWriter.WriteAsync("\"" + text + "\"");
		}

		public static implicit operator DotLabelAttribute(string value)
		{
			return new DotLabelAttribute(value);
		}
	}
	internal class DotNodeShapeAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotNodeShapeAttribute(string value)
		{
			Value = value;
		}

		public DotNodeShapeAttribute(DotNodeShape shape)
		{
			Value = shape.ToString().ToLowerInvariant();
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync("\"" + Value + "\"");
		}

		public static implicit operator DotNodeShapeAttribute(DotNodeShape value)
		{
			return new DotNodeShapeAttribute(value);
		}

		public static implicit operator DotNodeShapeAttribute(string value)
		{
			return new DotNodeShapeAttribute(value);
		}
	}
	internal class DotNodeStyleAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotNodeStyleAttribute(string value)
		{
			Value = value;
		}

		public DotNodeStyleAttribute(DotNodeStyle style)
		{
			Value = style.FlagsToString();
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync("\"" + Value + "\"");
		}

		public static implicit operator DotNodeStyleAttribute(DotNodeStyle value)
		{
			return new DotNodeStyleAttribute(value);
		}

		public static implicit operator DotNodeStyleAttribute(string value)
		{
			return new DotNodeStyleAttribute(value);
		}
	}
	internal class DotPointAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotPointAttribute(string value)
		{
			Value = value;
		}

		public DotPointAttribute(DotPoint point)
		{
			Value = point.ToString();
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync(Value);
		}

		public static implicit operator DotPointAttribute(DotPoint value)
		{
			return new DotPointAttribute(value);
		}

		public static implicit operator DotPointAttribute(string value)
		{
			return new DotPointAttribute(value);
		}
	}
	internal class DotRankDirAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotRankDirAttribute(string value)
		{
			Value = value;
		}

		public DotRankDirAttribute(DotRankDir rankDir)
		{
			Value = rankDir.ToString();
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync("\"" + Value + "\"");
		}

		public static implicit operator DotRankDirAttribute(DotRankDir value)
		{
			return new DotRankDirAttribute(value);
		}

		public static implicit operator DotRankDirAttribute(string value)
		{
			return new DotRankDirAttribute(value);
		}
	}
	internal class DotSubgraphStyleAttribute : IDotAttribute, IDotElement
	{
		public string Value { get; set; }

		public DotSubgraphStyleAttribute(string value)
		{
			Value = value;
		}

		public DotSubgraphStyleAttribute(DotSubgraphStyle style)
		{
			Value = style.FlagsToString();
		}

		public async Task CompileAsync(CompilationContext context)
		{
			await context.WriteAsync("\"" + Value + "\"");
		}

		public static implicit operator DotSubgraphStyleAttribute(DotSubgraphStyle value)
		{
			return new DotSubgraphStyleAttribute(value);
		}

		public static implicit operator DotSubgraphStyleAttribute(string value)
		{
			return new DotSubgraphStyleAttribute(value);
		}
	}
	internal interface IDotAttribute : IDotElement
	{
	}
}