<commit_before>﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Object = UnityEngine.Object;

public static class CheckSupport
{
	static Text alertText;
	static Transform alertProgressBar;
	static List<GameObject> gameObjects = new List<GameObject>();

	public static IEnumerator FindSupportedModules()
	{
		yield return null;

		UnityWebRequest request = UnityWebRequest.Get("https://ktane.timwi.de/json/raw");

		yield return request.SendWebRequest();

		if (!request.isNetworkError && !request.isHttpError)
		{
			var alert = Object.Instantiate(IRCConnection.Instance.ConnectionAlert, IRCConnection.Instance.ConnectionAlert.transform.parent);
			gameObjects.Add(alert);
			alert.SetActive(true);
			alertText = alert.transform.Find("Text").GetComponent<Text>();
			alertProgressBar = alert.transform.Find("ProgressBar");

			var json = JsonConvert.DeserializeObject<WebsiteJSON>(request.downloadHandler.text);
			yield return TestComponents(GetUntestedComponents(json));

			Object.Destroy(alert);
		}
	}

	public static void Cleanup()
	{
		foreach (GameObject gameObject in gameObjects)
			Object.Destroy(gameObject);

		gameObjects.Clear();
	}

	static IEnumerator TestComponents(IEnumerable<BombComponent> untestedComponents)
	{
		GameObject fakeModule = new GameObject();
		gameObjects.Add(fakeModule);
		TwitchModule module = fakeModule.AddComponent<TwitchModule>();
		module.enabled = false;

		List<string> unsupportedModules = new List<string>();
		Dictionary<string, bool> supportStatus = new Dictionary<string, bool>();
		ComponentSolverFactory.SilentMode = true;

		// Try to create a ComponentSolver for each module so we can see what modules are supported.
		foreach (BombComponent bombComponent in untestedComponents)
		{
			ComponentSolver solver = null;
			try
			{
				module.BombComponent = bombComponent.GetComponent<BombComponent>();

				solver = ComponentSolverFactory.CreateSolver(module);

				module.StopAllCoroutines(); // Stop any coroutines to prevent any exceptions or from affecting the next module.
			}
			catch (Exception e)
			{
				DebugHelper.LogException(e, $"Couldn't create a component solver for \"{bombComponent.GetModuleDisplayName()}\" during startup for the following reason:");
			}

			ModuleData.DataHasChanged |= solver != null;

			DebugHelper.Log(solver != null
				? $"Found a solver of type \"{solver.GetType().FullName}\" for component \"{bombComponent.GetModuleDisplayName()}\". This module is {(solver.UnsupportedModule ? "not supported" : "supported")} by Twitch Plays."
				: $"No solver found for component \"{bombComponent.GetModuleDisplayName()}\". This module is not supported by Twitch Plays.");

			string moduleID = bombComponent.GetComponent<KMBombModule>()?.ModuleType ?? bombComponent.GetComponent<KMNeedyModule>()?.ModuleType;
			if (solver?.UnsupportedModule != false && moduleID != null)
			{
				unsupportedModules.Add(moduleID);
			}

			supportStatus[bombComponent.GetModuleDisplayName()] = !(solver?.UnsupportedModule != false && moduleID != null);

			yield return null;
		}

		ComponentSolverFactory.SilentMode = false;
		ModuleData.WriteDataToFile();
		Object.Destroy(fakeModule);

		// Using the list of unsupported module IDs stored in unsupportedModules, make a Mod Selector profile.
		string profilesPath = Path.Combine(Application.persistentDataPath, "ModProfiles");
		if (Directory.Exists(profilesPath))
		{
			Dictionary<string, object> profileData = new Dictionary<string, object>()
			{
				{ "DisabledList", unsupportedModules },
				{ "Operation", 1 }
			};

			File.WriteAllText(Path.Combine(profilesPath, "TP_Supported.json"), SettingsConverter.Serialize(profileData));
		}

		alertProgressBar.localScale = Vector3.one;

		// Send a message to chat if any modules aren't marked as having support
		if (supportStatus.Values.Count(status => status) > 0)
		{
			var supportedList = supportStatus.Where(pair => pair.Value).Select(pair => pair.Key).Join(", ");
			IRCConnection.SendMessage($"Let the Scoring Team know that the following modules have TP support: {supportedList}");
			alertText.text = $"These modules have TP support: {supportedList}";
			yield return new WaitForSeconds(4);
		}
		else
		{
			alertText.text = "Support checks passed succesfully!";
			yield return new WaitForSeconds(2);
		}

		// Log out the full results of the testing
		DebugHelper.Log($"Support testing results:\n{supportStatus.OrderByDescending(pair => pair.Value).Select(pair => $"{pair.Key} - {(pair.Value ? "" : "Not ")}Supported").Join("\n")}");
	}

	static IEnumerable<BombComponent> GetUntestedComponents(WebsiteJSON json)
	{
		int progress = 0;

		// Get local mods that need to be tested
		if (!(typeof(ModManager).GetField("loadedMods", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(ModManager.Instance) is Dictionary<string, Mod> loadedMods))
			yield break;

		var validLocalMods = loadedMods.Values
			.SelectMany(mod => mod.GetModObjects<BombComponent>())
			.Where(bombComponent =>
			{
				string moduleID = bombComponent.GetComponent<KMBombModule>()?.ModuleType ?? bombComponent.GetComponent<KMNeedyModule>()?.ModuleType;
				return json.KtaneModules.Any(module => module.ModuleID == moduleID && module.TwitchPlays == null);<commit_msg>Check support for modules that aren't on the repo<commit_after>﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Object = UnityEngine.Object;

public static class CheckSupport
{
	static Text alertText;
	static Transform alertProgressBar;
	static List<GameObject> gameObjects = new List<GameObject>();

	public static IEnumerator FindSupportedModules()
	{
		yield return null;

		UnityWebRequest request = UnityWebRequest.Get("https://ktane.timwi.de/json/raw");

		yield return request.SendWebRequest();

		if (!request.isNetworkError && !request.isHttpError)
		{
			var alert = Object.Instantiate(IRCConnection.Instance.ConnectionAlert, IRCConnection.Instance.ConnectionAlert.transform.parent);
			gameObjects.Add(alert);
			alert.SetActive(true);
			alertText = alert.transform.Find("Text").GetComponent<Text>();
			alertProgressBar = alert.transform.Find("ProgressBar");

			var json = JsonConvert.DeserializeObject<WebsiteJSON>(request.downloadHandler.text);
			yield return TestComponents(GetUntestedComponents(json));

			Object.Destroy(alert);
		}
	}

	public static void Cleanup()
	{
		foreach (GameObject gameObject in gameObjects)
			Object.Destroy(gameObject);

		gameObjects.Clear();
	}

	static IEnumerator TestComponents(IEnumerable<BombComponent> untestedComponents)
	{
		GameObject fakeModule = new GameObject();
		gameObjects.Add(fakeModule);
		TwitchModule module = fakeModule.AddComponent<TwitchModule>();
		module.enabled = false;

		List<string> unsupportedModules = new List<string>();
		Dictionary<string, bool> supportStatus = new Dictionary<string, bool>();
		ComponentSolverFactory.SilentMode = true;

		// Try to create a ComponentSolver for each module so we can see what modules are supported.
		foreach (BombComponent bombComponent in untestedComponents)
		{
			ComponentSolver solver = null;
			try
			{
				module.BombComponent = bombComponent.GetComponent<BombComponent>();

				solver = ComponentSolverFactory.CreateSolver(module);

				module.StopAllCoroutines(); // Stop any coroutines to prevent any exceptions or from affecting the next module.
			}
			catch (Exception e)
			{
				DebugHelper.LogException(e, $"Couldn't create a component solver for \"{bombComponent.GetModuleDisplayName()}\" during startup for the following reason:");
			}

			ModuleData.DataHasChanged |= solver != null;

			DebugHelper.Log(solver != null
				? $"Found a solver of type \"{solver.GetType().FullName}\" for component \"{bombComponent.GetModuleDisplayName()}\". This module is {(solver.UnsupportedModule ? "not supported" : "supported")} by Twitch Plays."
				: $"No solver found for component \"{bombComponent.GetModuleDisplayName()}\". This module is not supported by Twitch Plays.");

			string moduleID = bombComponent.GetComponent<KMBombModule>()?.ModuleType ?? bombComponent.GetComponent<KMNeedyModule>()?.ModuleType;
			if (solver?.UnsupportedModule != false && moduleID != null)
			{
				unsupportedModules.Add(moduleID);
			}

			supportStatus[bombComponent.GetModuleDisplayName()] = !(solver?.UnsupportedModule != false && moduleID != null);

			yield return null;
		}

		ComponentSolverFactory.SilentMode = false;
		ModuleData.WriteDataToFile();
		Object.Destroy(fakeModule);

		// Using the list of unsupported module IDs stored in unsupportedModules, make a Mod Selector profile.
		string profilesPath = Path.Combine(Application.persistentDataPath, "ModProfiles");
		if (Directory.Exists(profilesPath))
		{
			Dictionary<string, object> profileData = new Dictionary<string, object>()
			{
				{ "DisabledList", unsupportedModules },
				{ "Operation", 1 }
			};

			File.WriteAllText(Path.Combine(profilesPath, "TP_Supported.json"), SettingsConverter.Serialize(profileData));
		}

		alertProgressBar.localScale = Vector3.one;

		// Send a message to chat if any modules aren't marked as having support
		if (supportStatus.Values.Count(status => status) > 0)
		{
			var supportedList = supportStatus.Where(pair => pair.Value).Select(pair => pair.Key).Join(", ");
			IRCConnection.SendMessage($"Let the Scoring Team know that the following modules have TP support: {supportedList}");
			alertText.text = $"These modules have TP support: {supportedList}";
			yield return new WaitForSeconds(4);
		}
		else
		{
			alertText.text = "Support checks passed succesfully!";
			yield return new WaitForSeconds(2);
		}

		// Log out the full results of the testing
		DebugHelper.Log($"Support testing results:\n{supportStatus.OrderByDescending(pair => pair.Value).Select(pair => $"{pair.Key} - {(pair.Value ? "" : "Not ")}Supported").Join("\n")}");
	}

	static IEnumerable<BombComponent> GetUntestedComponents(WebsiteJSON json)
	{
		int progress = 0;

		// Get local mods that need to be tested
		if (!(typeof(ModManager).GetField("loadedMods", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(ModManager.Instance) is Dictionary<string, Mod> loadedMods))
			yield break;

		var validLocalMods = loadedMods.Values
			.SelectMany(mod => mod.GetModObjects<BombComponent>())
			.Where(bombComponent =>
			{
				string moduleID = bombComponent.GetComponent<KMBombModule>()?.ModuleType ?? bombComponent.GetComponent<KMNeedyModule>()?.ModuleType;
				return json.KtaneModules.Any(module => module.ModuleID == moduleID && module.TwitchPlays == null) || !json.KtaneModules.Any(module => module.ModuleID == moduleID);
{'fraction_non_alphanumeric': 0.1162238656075961, 'fraction_numerical': 0.0010955902492467817, 'mean_word_length': 5.038588754134509, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 62, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4815593', 'n_tokens_mistral': 3484, 'n_tokens_neox': 3093, 'n_words': 764}