using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Mirror;
using TMPro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace OrderTabletAssistantBoxFix;
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("local.ordertabletassistant.physicalboxfix", "Order Tablet Assistant - Physical Box Fix", "0.2.1")]
public sealed class Plugin : BaseUnityPlugin
{
private Harmony harmony;
private static Plugin instance;
private static readonly FieldInfo NpcEquipment = AccessTools.Field(typeof(NPC_Info), "equippedItem");
private ConfigEntry<KeyCode> absentProductsKey;
private FieldInfo originalInstance;
private FieldInfo originalEnabled;
private FieldInfo activeDevice;
private FieldInfo scanInProgress;
private MethodInfo getStock;
private MethodInfo addToOrder;
private MethodInfo refreshTotals;
private MethodInfo showNotification;
private FieldInfo panelText;
private FieldInfo panelRect;
private MethodInfo refreshPanelText;
private bool absentFeatureReady;
private float lastAbsentScan = float.NegativeInfinity;
private void Awake()
{
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_008c: Expected O, but got Unknown
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Expected O, but got Unknown
//IL_022b: Unknown result type (might be due to invalid IL or missing references)
//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
//IL_02f3: Expected O, but got Unknown
instance = this;
Type type = AccessTools.TypeByName("OrderTabletAssistant.Plugin");
MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "CountEmptyLabelledStorageSlots", (Type[])null, (Type[])null));
if (methodInfo == null || methodInfo.ReturnType != typeof(Dictionary<int, int>) || NpcEquipment == null)
{
((BaseUnityPlugin)this).Logger.LogError((object)"Box Fix: incompatible mod/game version; patch not applied.");
return;
}
harmony = new Harmony("local.ordertabletassistant.physicalboxfix");
harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AdjustEmptyStorageSlots", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Box Fix active: non-empty physical boxes count as one storage slot each.");
absentProductsKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Absent products key", (KeyCode)110, "While the order tablet is open, add one box of each unlocked product with no stock anywhere. Purchases remain manual.");
originalInstance = AccessTools.Field(type, "Instance");
originalEnabled = AccessTools.Field(type, "Enabled");
activeDevice = AccessTools.Field(type, "_activeDevice");
scanInProgress = AccessTools.Field(type, "_scanInProgress");
getStock = AccessTools.Method(typeof(ManagerBlackboard), "GetProductsExistences", new Type[1] { typeof(int) }, (Type[])null);
addToOrder = AccessTools.DeclaredMethod(type, "AddProductToOrderWithoutRefresh", (Type[])null, (Type[])null);
refreshTotals = AccessTools.DeclaredMethod(type, "RefreshOrderTotals", (Type[])null, (Type[])null);
showNotification = AccessTools.DeclaredMethod(type, "ShowNativeNotification", (Type[])null, (Type[])null);
absentFeatureReady = originalInstance != null && originalEnabled != null && activeDevice != null && scanInProgress != null && getStock != null && addToOrder != null && refreshTotals != null && showNotification != null;
if (absentFeatureReady)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)string.Concat("Absent products: press ", absentProductsKey.Value, " with the tablet open."));
}
else
{
((BaseUnityPlugin)this).Logger.LogError((object)"Absent products: incompatible API; hotkey disabled. Storage fix remains active.");
}
panelText = AccessTools.Field(type, "_nativePanelText");
panelRect = AccessTools.Field(type, "_nativePanelRect");
refreshPanelText = AccessTools.DeclaredMethod(type, "RefreshPanelText", (Type[])null, (Type[])null);
if (absentFeatureReady && panelText != null && panelRect != null && refreshPanelText != null)
{
harmony.Patch((MethodBase)refreshPanelText, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AppendAbsentProductsHint", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
absentProductsKey.SettingChanged += delegate
{
RefreshHint();
};
RefreshHint();
}
else
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Absent products: native panel hint could not be installed.");
}
}
private void RefreshHint()
{
object value = originalInstance.GetValue(null);
if (value != null)
{
refreshPanelText.Invoke(value, new object[0]);
}
}
private static void AppendAbsentProductsHint(object __instance)
{
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)instance == (Object)null || !instance.absentFeatureReady)
{
return;
}
object? value = instance.panelText.GetValue(__instance);
TextMeshProUGUI val = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null);
object? value2 = instance.panelRect.GetValue(__instance);
RectTransform val2 = (RectTransform)((value2 is RectTransform) ? value2 : null);
if (!((Object)(object)val == (Object)null))
{
object text = ((TMP_Text)val).text;
((TMP_Text)val).text = string.Concat(text, "\n", instance.absentProductsKey.Value, " : Nouveaux produits");
if ((Object)(object)val2 != (Object)null)
{
Vector2 sizeDelta = val2.sizeDelta;
val2.sizeDelta = new Vector2(sizeDelta.x, Math.Max(sizeDelta.y, 84f));
}
}
}
private void Update()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
//IL_01c4: Expected O, but got Unknown
if (!absentFeatureReady || !Input.GetKeyDown(absentProductsKey.Value) || Time.unscaledTime - lastAbsentScan < 0.5f)
{
return;
}
OrderingDevice val = null;
bool flag = false;
int num = 0;
try
{
ConfigEntry<bool> val2 = originalEnabled.GetValue(null) as ConfigEntry<bool>;
object value = originalInstance.GetValue(null);
if (val2 == null || !val2.Value || value == null)
{
return;
}
object? value2 = activeDevice.GetValue(value);
val = (OrderingDevice)((value2 is OrderingDevice) ? value2 : null);
if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled)
{
return;
}
if ((bool)scanInProgress.GetValue(value))
{
Notify("PRODUITS ABSENTS\nAttendez la fin du remplissage, puis reessayez.");
return;
}
lastAbsentScan = Time.unscaledTime;
ProductListing val3 = ProductListing.Instance;
if ((Object)(object)val3 == (Object)null || (Object)(object)GameData.Instance == (Object)null || (Object)(object)NPC_Manager.Instance == (Object)null)
{
throw new InvalidOperationException("Game state is not ready.");
}
ManagerBlackboard component = ((Component)GameData.Instance).GetComponent<ManagerBlackboard>();
if ((Object)(object)component == (Object)null || (Object)(object)component.shoppingListParent == (Object)null || component.idsToSpawn == null)
{
throw new InvalidOperationException("Shopping list / delivery state is not ready.");
}
val3.updateProductList();
HashSet<int> hashSet = new HashSet<int>(component.idsToSpawn);
HashSet<int> hashSet2 = new HashSet<int>();
foreach (Transform item in component.shoppingListParent.transform)
{
Transform val4 = item;
InteractableData component2 = ((Component)val4).GetComponent<InteractableData>();
if ((Object)(object)component2 != (Object)null)
{
hashSet2.Add(component2.thisSkillIndex);
}
}
OrderingListReferences[] componentsInChildren = ((Component)val).GetComponentsInChildren<OrderingListReferences>(true);
foreach (OrderingListReferences val5 in componentsInChildren)
{
hashSet2.Add(val5.productID);
}
List<int> list = new List<int>();
HashSet<int> hashSet3 = new HashSet<int>();
bool flag2 = false;
foreach (int availableProduct in val3.availableProducts)
{
if (!hashSet3.Add(availableProduct) || availableProduct < 0 || availableProduct >= val3.productsData.Length)
{
continue;
}
ProductData val6 = val3.productsData[availableProduct];
if (val6 != null && val6.maxItemsPerBox > 0)
{
if (!flag2)
{
getStock.Invoke(component, new object[1] { availableProduct });
flag2 = true;
}
int[] stock = getStock.Invoke(component, new object[1] { availableProduct }) as int[];
if (MissingProductRules.ShouldAdd(stock, hashSet2.Contains(availableProduct), hashSet.Contains(availableProduct)))
{
list.Add(availableProduct);
}
}
}
foreach (int item2 in list)
{
flag = true;
addToOrder.Invoke(null, new object[2] { val, item2 });
num++;
((BaseUnityPlugin)this).Logger.LogInfo((object)("Absent products: added one box for product " + item2));
}
Notify((num == 0) ? "PRODUITS ABSENTS\nAucun produit a ajouter." : ("PRODUITS ABSENTS\n" + num + " carton(s) ajoute(s).\nVerifiez puis validez votre achat."));
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("Absent products scan failed after " + num + " additions: " + ex));
Notify("PRODUITS ABSENTS\nErreur : verifiez le panier et le journal.");
}
finally
{
if (flag && (Object)(object)val != (Object)null)
{
try
{
refreshTotals.Invoke(null, new object[1] { val });
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("Absent products: total refresh failed: " + ex));
}
}
}
}
private void Notify(string message)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)message.Replace("\n", " | "));
try
{
if (showNotification != null)
{
showNotification.Invoke(null, new object[1] { message });
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Notification failed: " + ex.Message));
}
}
private void OnDestroy()
{
if (harmony != null)
{
harmony.UnpatchSelf();
}
if ((Object)(object)instance == (Object)(object)this)
{
instance = null;
}
}
private static void AdjustEmptyStorageSlots(ref Dictionary<int, int> __result)
{
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Expected O, but got Unknown
//IL_0155: Unknown result type (might be due to invalid IL or missing references)
//IL_015b: Expected O, but got Unknown
if (__result == null || __result.Count == 0)
{
return;
}
try
{
if ((Object)(object)GameData.Instance == (Object)null || (Object)(object)NPC_Manager.Instance == (Object)null)
{
throw new InvalidOperationException("Game state is not ready.");
}
ManagerBlackboard component = ((Component)GameData.Instance).GetComponent<ManagerBlackboard>();
if ((Object)(object)component == (Object)null || (Object)(object)component.boxParent == (Object)null || (Object)(object)NPC_Manager.Instance.employeeParentOBJ == (Object)null)
{
throw new InvalidOperationException("Box/employee containers are not ready.");
}
Dictionary<int, int> dictionary = new Dictionary<int, int>();
HashSet<int> hashSet = new HashSet<int>();
foreach (Transform item in component.boxParent)
{
Transform val = item;
BoxData component2 = ((Component)val).GetComponent<BoxData>();
if ((Object)(object)component2 != (Object)null && hashSet.Add(((Object)component2).GetInstanceID()))
{
BoxCounts.AddNonEmptyBox(dictionary, component2.productID, component2.numberOfProducts);
}
}
foreach (Transform item2 in NPC_Manager.Instance.employeeParentOBJ.transform)
{
Transform val = item2;
NPC_Info component3 = ((Component)val).GetComponent<NPC_Info>();
if ((Object)(object)component3 != (Object)null && hashSet.Add(((Object)component3).GetInstanceID()) && (int)NpcEquipment.GetValue(component3) == 1)
{
BoxCounts.AddNonEmptyBox(dictionary, component3.boxProductID, component3.boxNumberOfProducts);
}
}
NetworkManager singleton = NetworkManager.singleton;
CustomNetworkManager val2 = (CustomNetworkManager)(object)((singleton is CustomNetworkManager) ? singleton : null);
if ((Object)(object)val2 != (Object)null)
{
foreach (PlayerObjectController gamePlayer in val2.GamePlayers)
{
if ((Object)(object)gamePlayer == (Object)null || !hashSet.Add(((Object)gamePlayer).GetInstanceID()))
{
continue;
}
PlayerNetwork component4 = ((Component)gamePlayer).GetComponent<PlayerNetwork>();
if ((Object)(object)component4 == (Object)null || component4.equippedItem != 1)
{
continue;
}
if (((NetworkBehaviour)component4).isLocalPlayer)
{
BoxCounts.AddNonEmptyBox(dictionary, component4.extraParameter1, component4.extraParameter2);
continue;
}
PlayerSyncCharacter component5 = ((Component)gamePlayer).GetComponent<PlayerSyncCharacter>();
if ((Object)(object)component5 != (Object)null)
{
BoxCounts.AddNonEmptyBox(dictionary, component5.syncedProductID, component5.syncedNumberOfProducts);
}
}
}
Dictionary<int, int> dictionary2 = BoxCounts.SubtractAvailable(__result, dictionary);
foreach (KeyValuePair<int, int> item3 in __result)
{
dictionary.TryGetValue(item3.Key, out var value);
((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Box Fix product {item3.Key}: empty slots={item3.Value}, available boxes={value}, storage order={dictionary2[item3.Key]}");
}
__result = dictionary2;
}
catch (Exception ex)
{
__result = new Dictionary<int, int>();
((BaseUnityPlugin)instance).Logger.LogError((object)("Box Fix: storage fill skipped because stock could not be read: " + ex));
}
}
}
public static class BoxCounts
{
public static void AddNonEmptyBox(Dictionary<int, int> counts, int productId, int units)
{
if (productId >= 0 && units > 0)
{
counts.TryGetValue(productId, out var value);
counts[productId] = value + 1;
}
}
public static Dictionary<int, int> SubtractAvailable(Dictionary<int, int> emptySlots, Dictionary<int, int> availableBoxes)
{
Dictionary<int, int> dictionary = new Dictionary<int, int>();
foreach (KeyValuePair<int, int> emptySlot in emptySlots)
{
availableBoxes.TryGetValue(emptySlot.Key, out var value);
dictionary[emptySlot.Key] = Math.Max(0, emptySlot.Value - value);
}
return dictionary;
}
}
public static class MissingProductRules
{
public static bool ShouldAdd(int[] stock, bool inCart, bool awaitingDelivery)
{
if (stock == null || stock.Length != 3)
{
throw new InvalidOperationException("The game's stock reader returned an invalid result.");
}
for (int i = 0; i < stock.Length; i++)
{
if (stock[i] < 0)
{
throw new InvalidOperationException("The game's stock count is negative.");
}
}
return !inCart && !awaitingDelivery && stock[0] == 0 && stock[1] == 0 && stock[2] == 0;
}
}