using System;
using System.Reflection;
using HarmonyLib;
using RobocraftX.Services;
using UnityEngine;
using RobocraftX.Common;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.App
{
///
/// The Techblox application that is running this code right now.
///
public class Client
{
public static Client Instance { get; } = new Client();
protected static Func ErrorHandlerInstanceGetter;
protected static Action EnqueueError;
///
/// An event that fires whenever the main menu is loaded.
///
public static event EventHandler EnterMenu
{
add => Game.menuEngine.EnterMenu += value;
remove => Game.menuEngine.EnterMenu -= value;
}
///
/// An event that fire whenever the main menu is exited.
///
public static event EventHandler ExitMenu
{
add => Game.menuEngine.ExitMenu += value;
remove => Game.menuEngine.ExitMenu -= value;
}
///
/// Techblox build version string.
/// Usually this is in the form YYYY.mm.DD.HH.MM.SS
///
/// The version.
public string Version
{
get => Application.version;
}
///
/// Unity version string.
///
/// The unity version.
public string UnityVersion
{
get => Application.unityVersion;
}
///
/// Game saves currently visible in the menu.
/// These take a second to completely populate after the EnterMenu event fires.
///
/// My games.
public Game[] MyGames
{
get
{
if (!Game.menuEngine.IsInMenu) return Array.Empty();
return Game.menuEngine.GetMyGames();
}
}
///
/// Whether Techblox is in the Main Menu
///
/// true if in menu; false when loading or in a game.
public bool InMenu
{
get => Game.menuEngine.IsInMenu;
}
///
/// Open a popup which prompts the user to click a button.
/// This reuses Techblox's error dialog popup
///
/// The popup to display. Use an instance of SingleChoicePrompt or DualChoicePrompt.
public void PromptUser(Error popup)
{
// if the stuff wasn't mostly set to internal, this would be written as:
// RobocraftX.Services.ErrorHandler.Instance.EqueueError(error);
object errorHandlerInstance = ErrorHandlerInstanceGetter();
EnqueueError(errorHandlerInstance, popup);
}
public void CloseCurrentPrompt()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.Close();
}
public void SelectFirstPromptButton()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.FirstButton();
}
public void SelectSecondPromptButton()
{
object errorHandlerInstance = ErrorHandlerInstanceGetter();
var popup = GetPopupCloseMethods(errorHandlerInstance);
popup.SecondButton();
}
internal void CloseBetaPopup()
{
Game.menuEngine.CloseBetaPopup();
}
internal static void Init()
{
// this would have been so much simpler if this didn't involve a bunch of internal fields & classes
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
Type errorHandle = AccessTools.TypeByName("RobocraftX.Services.ErrorHandle");
ErrorHandlerInstanceGetter = (Func) AccessTools.Method("TechbloxModdingAPI.App.Client:GenInstanceGetter")
.MakeGenericMethod(errorHandler)
.Invoke(null, new object[0]);
EnqueueError = (Action) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
.MakeGenericMethod(errorHandler, errorHandle)
.Invoke(null, new object[0]);
}
// Creating delegates once is faster than reflection every time
// Admittedly, this way is more difficult to code and less readable
private static Func GenInstanceGetter()
{
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo instance = AccessTools.PropertyGetter(errorHandler, "Instance");
Func getterSimple = (Func) Delegate.CreateDelegate(typeof(Func), null, instance);
Func getterCasted = () => (object) getterSimple();
return getterCasted;
}
private static Action GenEnqueueError()
{
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo enqueueError = AccessTools.Method(errorHandler, "EnqueueError");
Func enqueueSimple =
(Func) Delegate.CreateDelegate(typeof(Func), enqueueError);
Action enqueueCasted =
(object instance, Error error) => { enqueueSimple((T) instance, error); };
return enqueueCasted;
}
private static (Action Close, Action FirstButton, Action SecondButton) _errorPopup;
private static (Action Close, Action FirstButton, Action SecondButton) GetPopupCloseMethods(object handler)
{
if (_errorPopup.Close != null)
return _errorPopup;
Type errorHandler = handler.GetType();
FieldInfo field = AccessTools.Field(errorHandler, "errorPopup");
var errorPopup = (ErrorPopup)field.GetValue(handler);
MethodInfo info = AccessTools.Method(errorPopup.GetType(), "ClosePopup");
var close = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
info = AccessTools.Method(errorPopup.GetType(), "HandleFirstOption");
var first = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
info = AccessTools.Method(errorPopup.GetType(), "HandleSecondOption");
var second = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
_errorPopup = (close, first, second);
return _errorPopup;
}
}
}