using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using Controllers;
using Controllers.CharacterLogic;
using Controllers.Time;
using Controllers.Weather;
using Data.Resorces;
using GameCustomization;
using Gameplay;
using Gameplay.Buildings;
using Gameplay.GameResources;
using Gameplay.InGameResources;
using Gameplay.Production;
using Gameplay.Rebuilding;
using Gameplay.Rebuilding.Towers;
using Gameplay.Rebuilding.Walls;
using Gameplay.Scavenge;
using Gameplay.Units.Characters;
using Gameplay.Units.Icons;
using Gameplay.Units.Movements;
using Gameplay.Units.Workers.WorkSystem.Works;
using Gameplay.Vehicles;
using HarmonyLib;
using HarmonyLib.Tools;
using MapEssentials.Temp;
using ScriptableObjectScripts.Radio;
using UI.InfoPanels.NestedUIElements;
using UnityEngine;
using Zenject;

// See:
// ConstructableWork
// ProductionWork
// GatherResourcesWork
// GatherableObject
// AreaWork
// Gameplay.Vehicles.Vehicle.Update() : void @060044A1
// System.Single Gameplay.Units.Movements.Movement::CalculateSpeed()
// GameConfig looks interesting
// See dayLengthInSeconds (defaults to 720 actually)
// System.Single
// Controllers.Time.TimeController::GetSunsetHour(System.Int32,System.Double,System.Double)

namespace InfectionFreeZone {
[BepInPlugin(pluginGuid, pluginName, pluginVersion)]
public class Main : BaseUnityPlugin {
  private const string pluginGuid = "InfectionFreeDave";
  private const string pluginName = "InfectionFreeDave";
  private const string pluginVersion = "1.0.0";

  public static ConfigEntry<bool> resourceMultiplierDebug;
  public static ConfigEntry<float> resourceMultiplier;

  public static ConfigEntry<bool> peopleMultiplierDebug;
  public static ConfigEntry<float> peopleMultiplier;

  public static ConfigEntry<bool> resourceGatheringMultiplierDebug;
  public static ConfigEntry<float> resourceGatheringMultiplier;

  public static ConfigEntry<bool> resourceScavengingMultiplierDebug;
  public static ConfigEntry<float> resourceScavengingMultiplier;

  public static ConfigEntry<bool> buildingStorageCapacityMultiplierDebug;
  public static ConfigEntry<float> buildingStorageCapacityMultiplier;

  public static ConfigEntry<bool> buildingQuartersCapacityMultiplierDebug;
  public static ConfigEntry<float> buildingQuartersCapacityMultiplier;

  public static ConfigEntry<bool>
      buildingDeconstructionResourcesMultiplierDebug;
  public static ConfigEntry<float> buildingDeconstructionResourcesMultiplier;

  public static ConfigEntry<float> humanMovementSpeedMultiplier;
  public static ConfigEntry<float> vehicleMovementSpeedMultiplier;

  public static ConfigEntry<bool> vehicleTrunkCapacityMultiplierDebug;
  public static ConfigEntry<float> vehicleTrunkCapacityMultiplier;

  public static ConfigEntry<bool> deconstructionTimeMultiplierDebug;
  public static ConfigEntry<float> deconstructionTimeMultiplier;
  public static ConfigEntry<float> deconstructionWorkersPer100m;

  public static ConfigEntry<bool> constructionTimeMultiplierDebug;
  public static ConfigEntry<float> constructionTimeMultiplier;

  public static ConfigEntry<bool> productionTimeMultiplierDebug;
  public static ConfigEntry<float> productionTimeMultiplier;

  public static ConfigEntry<bool> birthingConfigDebug;
  public static ConfigEntry<int> maxCitizensInZoneToChildrenBorn;
  public static ConfigEntry<float> moraleInfluenceFloorParam;
  public static ConfigEntry<float> moraleInfluenceCeilingParam;
  public static ConfigEntry<float> foodStockInfluenceFloorParam;
  public static ConfigEntry<float> foodStockInfluenceCeilingParam;
  public static ConfigEntry<float> quartersRatioInfluenceFloorParam;
  public static ConfigEntry<float> quartersRatioInfluenceCeilingParam;
  public static ConfigEntry<float> childToAdultRatioInfluenceFloorParam;
  public static ConfigEntry<float> childToAdultRatioInfluenceCeilingParam;
  public static ConfigEntry<int> adultAge;
  public static ConfigEntry<int> oldAge;

  public static ConfigEntry<float>
      rotationTimestepMultiplier; // This is NOT unused!
  public static ConfigEntry<float> sunsetHourOffset;
  public static ConfigEntry<float> sunriseHourOffset;

  public static ConfigEntry<bool> productionProfitMultiplierDebug;
  public static ConfigEntry<string> productionProfitMultiplier;
  public static Dictionary<ResourceID, float> productionProfitMultiplierDict;

  public static ConfigEntry<bool> towerEchoSelectionDebug;
  public static ConfigEntry<bool> towerEchoSelection;
  public static List<WeakReference<StructureDefenceModule>>
      towerEchoSelectionList;

  public static ConfigEntry<bool> buildingMaxVolumeDebug;
  public static ConfigEntry<float> buildingMaxVolume;

  // See System.Void
  // UI.InfoPanels.NestedUIElements.SelectWeaponPanel::OnWeaponItemClicked(UI.InfoPanels.NestedUIElements.WeaponItem)

  public void Awake() {
    resourceMultiplierDebug =
        Config.Bind("General", "Resource Multiplier Debug", false);
    resourceMultiplier = Config.Bind("General", "Resource Multiplier", 1f);

    peopleMultiplierDebug =
        Config.Bind("General", "People Multiplier Debug", false);
    peopleMultiplier = Config.Bind("General", "People Multiplier", 1f);

    resourceGatheringMultiplierDebug =
        Config.Bind("General", "Resource Gathering Multiplier Debug", false);
    resourceGatheringMultiplier =
        Config.Bind("General", "Resource Gathering Multiplier", 1f);

    resourceScavengingMultiplierDebug =
        Config.Bind("General", "Resource Scavenging Multiplier Debug", false);
    resourceScavengingMultiplier =
        Config.Bind("General", "Resource Scavenging Multiplier", 1f);

    buildingStorageCapacityMultiplierDebug = Config.Bind(
        "General", "Building Storage Capacity Multiplier Debug", false);
    buildingStorageCapacityMultiplier =
        Config.Bind("General", "Building Storage Capacity Multiplier", 1f);

    buildingQuartersCapacityMultiplierDebug = Config.Bind(
        "General", "Building Quarters Capacity Multiplier Debug", false);
    buildingQuartersCapacityMultiplier =
        Config.Bind("General", "Building Quarters Capacity Multiplier", 1f);

    buildingDeconstructionResourcesMultiplierDebug = Config.Bind(
        "General", "Building Deconstruction Resources Multiplier Debug", false);
    buildingDeconstructionResourcesMultiplier = Config.Bind(
        "General", "Building Deconstruction Resources Multiplier", 1f);
    deconstructionWorkersPer100m = Config.Bind(
        "General", "Building Deconstruction Workers Per 100m", 0.0045f);

    humanMovementSpeedMultiplier =
        Config.Bind("General", "Human Movement Speed Multiplier", 1f);
    vehicleMovementSpeedMultiplier =
        Config.Bind("General", "Vehicle Movement Speed Multiplier", 1f);

    vehicleTrunkCapacityMultiplierDebug = Config.Bind(
        "General", "Vehicle Trunk Capacity Multiplier Debug", false);
    vehicleTrunkCapacityMultiplier =
        Config.Bind("General", "Vehicle Trunk Capacity Multiplier", 1f);

    deconstructionTimeMultiplierDebug =
        Config.Bind("General", "Deconstruction Time Multiplier Debug", false);
    deconstructionTimeMultiplier =
        Config.Bind("General", "Deconstruction Time Multiplier", 1f);

    constructionTimeMultiplierDebug =
        Config.Bind("General", "Construction Time Multiplier Debug", false);
    constructionTimeMultiplier =
        Config.Bind("General", "Construction Time Multiplier", 1f);

    productionTimeMultiplierDebug =
        Config.Bind("General", "Production Time Multiplier Debug", false);
    productionTimeMultiplier =
        Config.Bind("General", "Production Time Multiplier", 1f);

    birthingConfigDebug =
        Config.Bind("Birthing Config", "Birthing Config Debug", false);
    maxCitizensInZoneToChildrenBorn = Config.Bind(
        "Birthing Config", "Max Citizens in Zone to Children Born", 600);
    moraleInfluenceFloorParam =
        Config.Bind("Birthing Config", "Morale Influence Floor Param", 0.3f);
    moraleInfluenceCeilingParam =
        Config.Bind("Birthing Config", "Morale Influence Ceiling Param", 2f);
    foodStockInfluenceFloorParam =
        Config.Bind("Birthing Config", "Food Stock Influence Floor Param", 0f);
    foodStockInfluenceCeilingParam = Config.Bind(
        "Birthing Config", "Food Stock Influence Ceiling Param", 1f);
    quartersRatioInfluenceFloorParam = Config.Bind(
        "Birthing Config", "Quarters Ratio Influence Floor Param", 0.6f);
    quartersRatioInfluenceCeilingParam = Config.Bind(
        "Birthing Config", "Quarters Ratio Influence Ceiling Param", 1.4f);
    childToAdultRatioInfluenceFloorParam = Config.Bind(
        "Birthing Config", "Child to Adult Ratio Influence Floor Param", 1f);
    childToAdultRatioInfluenceCeilingParam = Config.Bind(
        "Birthing Config", "Child to Adult Ratio Influence Ceiling Param", 0f);
    adultAge = Config.Bind("Birthing Config", "Adult Age", 12);
    oldAge = Config.Bind("Birthing Config", "Old Age", 60);

    rotationTimestepMultiplier =
        Config.Bind("General", "Rotation Timestep Multiplier", 1f);
    sunsetHourOffset = Config.Bind("General", "Sunset Hour Offset", 0f);
    sunriseHourOffset = Config.Bind("General", "Sunrise Hour Offset", 0f);

    productionProfitMultiplierDebug =
        Config.Bind("General", "Production Profit Multiplier Debug", false);
    productionProfitMultiplier = Config.Bind(
        "General", "Production Profit Multiplier (Resource ID: multiplier)",
        "");
    productionProfitMultiplierDict = new Dictionary<ResourceID, float>();
    productionProfitMultiplier.SettingChanged += delegate {
      UpdateProductionProfitMultiplierDict();
    };
    UpdateProductionProfitMultiplierDict();

    towerEchoSelectionDebug =
        Config.Bind("General", "Tower Echo Selection Debug", false);
    towerEchoSelection = Config.Bind("General", "Tower Echo Selection", false);
    towerEchoSelectionList = new List<WeakReference<StructureDefenceModule>>();

    buildingMaxVolumeDebug =
        Config.Bind("General", "Building Max Volume Debug", false);
    buildingMaxVolume = Config.Bind("General", "Building Max Volume", 50000f);

    Logger.LogInfo("Cyka mod loaded");
    HarmonyFileLog.Enabled = true;
    Harmony harmony = new Harmony(pluginGuid);
    harmony.PatchAll();
    var originalMethods = harmony.GetPatchedMethods();
    Logger.LogInfo("Patched " + originalMethods.Count() + " methods");
    foreach (var method in originalMethods) {
      Logger.LogInfo("Patched " + method.Name);
    }
  }

  public static void UpdateProductionProfitMultiplierDict() {
    Main.productionProfitMultiplierDict.Clear();
    var str = productionProfitMultiplier.Value;
    if (str == "")
      return;
    var split = str.Split(',');
    foreach (var s in split) {
      if (productionProfitMultiplierDebug.Value)
        Console.WriteLine($"Parsing {s}");
      var split2 = s.Split(':');
      if (split2.Length != 2)
        continue;
      var success = Enum.TryParse<ResourceID>(split2[0], out var resourceID);
      if (!success) {
        Console.WriteLine($"Failed to parse {split2[0]} as a resource ID");
        continue;
      }

      var multiplier = float.Parse(split2[1]);
      productionProfitMultiplierDict.Add(resourceID, multiplier);
      if (productionProfitMultiplierDebug.Value)
        Console.WriteLine($"Added {resourceID} with multiplier {multiplier}");
    }
  }
}

[HarmonyPatch]
public class Patches {
  [HarmonyPrefix]
  [HarmonyPatch(typeof(GameCustomize), "LoadResourcesSettings")]
  public static void PostfixResourceMultiplier(ref float multiplier) {
    if (Main.resourceMultiplierDebug.Value)
      Console.WriteLine($"Resource multiplier is {multiplier}");
    if (multiplier > 0) {
      multiplier = (int)(multiplier * Main.resourceMultiplier.Value);
      if (Main.resourceMultiplierDebug.Value)
        Console.WriteLine($"Resource multiplier modified to {multiplier}");
    }
  }

  [HarmonyPrefix]
  [HarmonyPatch(typeof(GameCustomize), "LoadPeopleSettings")]
  public static void PostfixPeopleMultiplier(ref float people) {
    if (Main.peopleMultiplierDebug.Value)
      Console.WriteLine($"People multiplier is {people}");
    if (people > 0) {
      people = (int)(people * Main.peopleMultiplier.Value);
      if (Main.peopleMultiplierDebug.Value)
        Console.WriteLine($"People multiplier modified to {people}");
    }
  }

  // [HarmonyPostfix]
  // [HarmonyPatch(typeof(ScavengeWork), MethodType.Constructor,
  // 	typeof(float), typeof(List<ResourceCrate>))]
  public static void PostfixScavengeWork(ref ScavengeWork __instance) {
    // This is saved to save file...
    // So every time it's fucking modified
    // Fuck me
    if (Main.resourceScavengingMultiplierDebug.Value)
      Console.WriteLine($"Scavenge work created");
    if (Main.resourceGatheringMultiplier.Value > 0) {
      for (int i = 0; i < __instance.ResourcesToFind.Count; i++) {
        var resource = __instance.ResourcesToFind[i];
        if (Main.resourceScavengingMultiplierDebug.Value)
          Console.WriteLine($"Resource {resource} to find");
        var rtraverse = Traverse.Create(resource);
        var resourceCount = rtraverse.Field<float>($"_quantity");
        if (Main.resourceScavengingMultiplierDebug.Value)
          Console.WriteLine($"Resource count is {resourceCount}");
        resourceCount.Value *= Main.resourceGatheringMultiplier.Value;
        if (Main.resourceScavengingMultiplierDebug.Value)
          Console.WriteLine($"Resource count modified to {resourceCount}");
        rtraverse.Field<float>($"_quantity").Value = resourceCount.Value;
      }
    }
  }

  [HarmonyPrefix]
  [HarmonyPatch(typeof(GatherResourcesWork), "InstantiateResources",
                new Type[] {})]
  public static void PrefixGatheredResource(ref ScavengeWork __instance) {
    var trav = Traverse.Create(__instance);
    var resourcesToDropNew = new Dictionary<ResourceID, int>();
    var resourcesToDrop =
        trav.Field<Dictionary<ResourceID, int>>("_resourcesToDrop");
    if (Main.resourceGatheringMultiplierDebug.Value)
      Console.WriteLine($"Resources to drop: {resourcesToDrop}");
    if (resourcesToDrop.Value.Count > 0) {
      foreach (KeyValuePair<ResourceID, int> resource in resourcesToDrop
                   .Value) {
        if (Main.resourceGatheringMultiplierDebug.Value)
          Console.WriteLine(
              $"Resource {resource.Key} to drop: {resource.Value}");
        var newResourceValue =
            (int)(resource.Value * Main.resourceScavengingMultiplier.Value);
        resourcesToDropNew.Add(resource.Key, newResourceValue);
        if (Main.resourceGatheringMultiplierDebug.Value)
          Console.WriteLine(
              $"Resource {resource.Key} to drop modified to: {newResourceValue}");
      }
    }

    resourcesToDrop.Value = resourcesToDropNew;
    if (Main.resourceGatheringMultiplierDebug.Value)
      Console.WriteLine($"Resources to drop modified: {resourcesToDrop}");
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(PlaceableObjectDraft), "GetCapacity", typeof(float),
                typeof(float))]
  public static void PostfixGetCapacity(ref int __result) {
    if (Main.buildingStorageCapacityMultiplier.Value <= 0)
      return;

    if (Main.buildingStorageCapacityMultiplierDebug.Value)
      Console.WriteLine($"Building capacity is {__result}");
    __result = (int)(__result * Main.buildingStorageCapacityMultiplier.Value);
    if (Main.buildingStorageCapacityMultiplierDebug.Value)
      Console.WriteLine($"Building capacity modified to {__result}");
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(PlaceableObjectDraft), "GetLivingCapacity")]
  public static void PostfixGetLivingCapacity(ref int __result) {
    if (Main.buildingQuartersCapacityMultiplier.Value <= 0)
      return;

    if (Main.buildingQuartersCapacityMultiplierDebug.Value)
      Console.WriteLine($"Building capacity is {__result}");
    __result = (int)(__result * Main.buildingQuartersCapacityMultiplier.Value);
    if (Main.buildingQuartersCapacityMultiplierDebug.Value)
      Console.WriteLine($"Building capacity modified to {__result}");
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(Building), "DeconstructionResources", MethodType.Getter)]
  public static ResourceQuantity[] PostfixDeconstructionResources(
      ResourceQuantity[] __result) {
    if (Main.buildingDeconstructionResourcesMultiplier.Value <= 0)
      return __result;

    ResourceQuantity[] modifiedResult = new ResourceQuantity[__result.Length];
    for (int i = 0; i < __result.Length; i++) {
      var resourceQuantity = __result[i];
      if (Main.buildingDeconstructionResourcesMultiplierDebug.Value)
        Console.WriteLine($"Resource quantity is {resourceQuantity}");

      if (Main.buildingDeconstructionResourcesMultiplierDebug.Value)
        Console.WriteLine(
            $"Resource quantity value is {resourceQuantity.value}");
      modifiedResult[i] = new ResourceQuantity(
          resourceQuantity.resourceType,
          resourceQuantity.value *
              Main.buildingDeconstructionResourcesMultiplier.Value);
      // resourceQuantity.value *=
      // Main.buildingDeconstructionResourcesMultiplier.Value;
      if (Main.buildingDeconstructionResourcesMultiplierDebug.Value)
        Console.WriteLine(
            $"Resource quantity value modified to {modifiedResult[i].value}");
    }

    return modifiedResult;
  }

  [HarmonyPrefix]
  [HarmonyPatch(typeof(BuildingDestruction), "Initialize")]
  public static void
  PrefixBuildingDeconstruction(ref BuildingDestruction __instance) {
    if (Main.deconstructionWorkersPer100m.Value <= 0)
      return;

    var trav = Traverse.Create(__instance);
    var config = trav.Field<BuildingDestructionConfig>("_deconstructionConfig");
    var configV = config.Value;
    if (Main.buildingDeconstructionResourcesMultiplierDebug.Value)
      Console.WriteLine(
          $"Building deconstruction workers per 100m is {configV.deconstructWorkersPer100m3OfBuilding}");

    configV.deconstructWorkersPer100m3OfBuilding =
        Main.deconstructionWorkersPer100m.Value;
    config.Value = configV;
    if (Main.buildingDeconstructionResourcesMultiplierDebug.Value)
      Console.WriteLine(
          $"Building deconstruction workers per 100m modified to {configV.deconstructWorkersPer100m3OfBuilding}");
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(Movement), "CalculateSpeed")]
  public static void PostfixCalculateSpeedHuman(Movement __instance,
                                                ref float __result) {
    // Humans are "Human(Clone)", infected are "inf_human(Clone)" and vehicles
    // are "ve_<name>"
    var name = __instance.ToString();
    if (name.StartsWith("Human"))
      __result *= Main.humanMovementSpeedMultiplier.Value;
    if (name.StartsWith("ve_"))
      __result *= Main.vehicleMovementSpeedMultiplier.Value;
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(Vehicle), "Constructor", typeof(GroupIconsPool),
                typeof(SignalBus), typeof(WeatherController))]
  public static void PostfixVehicle(ref Vehicle __instance) {
    if (Main.vehicleTrunkCapacityMultiplier.Value <= 0)
      return;

    if (Main.vehicleTrunkCapacityMultiplierDebug.Value)
      Console.WriteLine(
          $"Vehicle trunk capacity is {__instance.Trunk.Capacity}");
    __instance.Trunk.SetCapacity(
        (int)(__instance.Trunk.Capacity *
              Main.vehicleTrunkCapacityMultiplier.Value),
        true);
    if (Main.vehicleTrunkCapacityMultiplierDebug.Value)
      Console.WriteLine(
          $"Vehicle trunk capacity modified to {__instance.Trunk.Capacity}");
  }

  // GatherResourcesWork
  // ResearchWork
  // AreaWork
  // ProductionWork
  [HarmonyPrefix]
  [HarmonyPatch(typeof(DisassembleWork), MethodType.Constructor,
                typeof(Structure), typeof(float), typeof(int),
                typeof(ResourcesController), typeof(SignalBus))]
  public static void PrefixDisassembleWork(Structure structure,
                                           ref float timeToDeconstruct) {
    if (Main.deconstructionTimeMultiplier.Value <= 0)
      return;

    if (Main.deconstructionTimeMultiplierDebug.Value)
      Console.WriteLine($"Deconstruction time is {timeToDeconstruct}");
    timeToDeconstruct *= Main.deconstructionTimeMultiplier.Value;
    if (Main.deconstructionTimeMultiplierDebug.Value)
      Console.WriteLine($"Deconstruction time modified to {timeToDeconstruct}");
  }

  [HarmonyPrefix]
  [HarmonyPatch(typeof(ConstructableWork), MethodType.Constructor,
                typeof(Structure), typeof(Dictionary<ResourceID, int>),
                typeof(float), typeof(StockroomsController), typeof(SignalBus))]
  public static void PrefixConstructWork(Structure structure,
                                         Dictionary<ResourceID, int> cost,
                                         ref float timeToConstruct) {
    if (Main.deconstructionTimeMultiplier.Value <= 0)
      return;

    if (Main.constructionTimeMultiplierDebug.Value)
      Console.WriteLine($"Construction time is {timeToConstruct}");
    timeToConstruct *= Main.constructionTimeMultiplier.Value;
    if (Main.constructionTimeMultiplierDebug.Value)
      Console.WriteLine($"Construction time modified to {timeToConstruct}");
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(ProductionData), "GetProductionTime")]
  public static void PostfixProductionTime(ref float __result) {
    if (Main.productionTimeMultiplier.Value <= 0)
      return;

    if (Main.productionTimeMultiplierDebug.Value)
      Console.WriteLine($"Production time is {__result}");
    __result *= Main.productionTimeMultiplier.Value;
    if (Main.productionTimeMultiplierDebug.Value)
      Console.WriteLine($"Production time modified to {__result}");
  }

  [HarmonyPrefix]
  [HarmonyPatch(typeof(CitizensController), "IncreasePopulation")]
  // Could not find a better way to do this
  public static void
  PostfixCharactersConfig(ref CitizensController __instance) {
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine($"Citizens controller IncreasePopulation");
    var trav = Traverse.Create(__instance);
    var charactersConfig = trav.Field<CharactersConfig>("_charactersConfig");
    var charactersConfigV = charactersConfig.Value;
    var bornConfig = charactersConfigV.bornConfig;

    charactersConfigV.adultAge = Main.adultAge.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine($"Adult age is {charactersConfigV.adultAge}");

    charactersConfigV.oldAge = Main.oldAge.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine($"Old age is {charactersConfigV.oldAge}");

    bornConfig.MaxCitizensInZoneToChildrenBorn =
        Main.maxCitizensInZoneToChildrenBorn.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Max citizens in zone to children born is {bornConfig.MaxCitizensInZoneToChildrenBorn}");

    bornConfig.MoraleInfluenceFloorParam = Main.moraleInfluenceFloorParam.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Morale influence floor param is {bornConfig.MoraleInfluenceFloorParam}");

    bornConfig.MoraleInfluenceCeilingParam =
        Main.moraleInfluenceCeilingParam.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Morale influence ceiling param is {bornConfig.MoraleInfluenceCeilingParam}");

    bornConfig.FoodStockInfluenceFloorParam =
        Main.foodStockInfluenceFloorParam.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Food stock influence floor param is {bornConfig.FoodStockInfluenceFloorParam}");

    bornConfig.FoodStockInfluenceCeilingParam =
        Main.foodStockInfluenceCeilingParam.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Food stock influence ceiling param is {bornConfig.FoodStockInfluenceCeilingParam}");

    bornConfig.QuartersRatioInfluenceFloorParam =
        Main.quartersRatioInfluenceFloorParam.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Quarters ratio influence floor param is {bornConfig.QuartersRatioInfluenceFloorParam}");

    bornConfig.QuartersRatioInfluenceCeilingParam =
        Main.quartersRatioInfluenceCeilingParam.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Quarters ratio influence ceiling param is {bornConfig.QuartersRatioInfluenceCeilingParam}");

    bornConfig.ChildToAdultRatioInfluenceFloorParam =
        Main.childToAdultRatioInfluenceFloorParam.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Child to adult ratio influence floor param is {bornConfig.ChildToAdultRatioInfluenceFloorParam}");

    bornConfig.ChildToAdultRatioInfluenceCeilingParam =
        Main.childToAdultRatioInfluenceCeilingParam.Value;
    if (Main.birthingConfigDebug.Value)
      Console.WriteLine(
          $"Child to adult ratio influence ceiling param is {bornConfig.ChildToAdultRatioInfluenceCeilingParam}");

    charactersConfigV.bornConfig = bornConfig;
    charactersConfig.Value = charactersConfigV;
  }

  [HarmonyTranspiler]
  [HarmonyPatch(typeof(PlaceObjectsInSingleLineCursor), "RotateRight")]
  public static IEnumerable<CodeInstruction>
  PostfixRotateRight(IEnumerable<CodeInstruction> instructions) {
    var codes = new List<CodeInstruction>(instructions);
    for (var i = 0; i < codes.Count; i++) {
      var code = codes[i];
      Console.WriteLine($"Opcode is {code.opcode}");
      if (code.opcode == OpCodes.Ldc_R4 &&
          Mathf.Approximately((float)code.operand, 100f)) {
        Console.WriteLine($"Operand is {code.operand}");
        codes[i] = new CodeInstruction(
            OpCodes.Ldsfld,
            AccessTools.Field(typeof(Main), "rotationTimestepMultiplier"));
        codes.Insert(i + 1, new CodeInstruction(
                                OpCodes.Call,
                                AccessTools.PropertyGetter(
                                    typeof(Main)
                                        .GetField("rotationTimestepMultiplier")
                                        .FieldType,
                                    "Value")));
        Console.WriteLine($"Operand modified to {codes[i].operand}");
      }
    }

    return codes.AsEnumerable();
  }

  [HarmonyTranspiler]
  [HarmonyPatch(typeof(PlaceObjectsInSingleLineCursor), "RotateLeft")]
  public static IEnumerable<CodeInstruction>
  PostfixRotateLeft(IEnumerable<CodeInstruction> instructions) {
    var codes = new List<CodeInstruction>(instructions);
    for (var i = 0; i < codes.Count; i++) {
      var code = codes[i];
      Console.WriteLine($"Opcode is {code.opcode}");
      if (code.opcode == OpCodes.Ldc_R4 &&
          Mathf.Approximately((float)code.operand, 100f)) {
        Console.WriteLine($"Operand is {code.operand}");
        codes[i] = new CodeInstruction(
            OpCodes.Ldsfld,
            AccessTools.Field(typeof(Main), "rotationTimestepMultiplier"));
        codes.Insert(i + 1, new CodeInstruction(
                                OpCodes.Call,
                                AccessTools.PropertyGetter(
                                    typeof(Main)
                                        .GetField("rotationTimestepMultiplier")
                                        .FieldType,
                                    "Value")));
        Console.WriteLine($"Operand modified to {codes[i].operand}");
      }
    }

    return codes.AsEnumerable();
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(TimeController), "GetSunsetHour")]
  public static void PostfixGetSunsetHour(ref float __result) {
    __result += Main.sunsetHourOffset.Value;
    __result = Mathf.Clamp(__result, 0f, 24f);
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(TimeController), "GetSunriseHour")]
  public static void PostfixGetSunriseHour(ref float __result) {
    __result += Main.sunriseHourOffset.Value;
    __result = Mathf.Clamp(__result, 0f, 24f);
  }

  [HarmonyPrefix]
  [HarmonyPatch(typeof(ProductionWork), "ProduceResources")]
  public static void
  PrefixProduceResources(List<ProductionDataPart> productionDataParts) {
    if (Main.productionProfitMultiplierDict.Count == 0)
      return;
    if (productionDataParts.Count == 0)
      return;

    for (var i = 0; i < productionDataParts.Count; i++) {
      var productionDataPart = productionDataParts[i];
      if (Main.productionProfitMultiplierDebug.Value)
        Console.WriteLine(
            $"Production data part is {productionDataPart.type} x {productionDataPart.amount}");
      if (Main.productionProfitMultiplierDict.TryGetValue(
              productionDataPart.type, out var setpoint)) {
        if (Main.productionProfitMultiplierDebug.Value)
          Console.WriteLine($"Production data part has setpoint {setpoint}");
        productionDataPart.amount = (int)setpoint;
        if (Main.productionProfitMultiplierDebug.Value)
          Console.WriteLine(
              $"Production data part modified to {productionDataPart.type} x {productionDataPart.amount}");
      }
    }
  }

  [HarmonyPostfix]
  [HarmonyPatch(typeof(StructureDefenceModule), "Awake")]
  public static void
  PostfixStructureDefenceModule(StructureDefenceModule __instance) {
    if (Main.towerEchoSelectionDebug.Value)
      Console.WriteLine($"Structure defence module created");
    Main.towerEchoSelectionList.Add(
        new WeakReference<StructureDefenceModule>(__instance));
  }

  [HarmonyPrefix]
  [HarmonyPatch(typeof(SelectWeaponPanel), "OnWeaponItemClicked")]
  public static void PostfixSelectWeaponPanel(WeaponItem selectedItem) {
    if (!Main.towerEchoSelection.Value)
      return;
    if (selectedItem == null) {
      if (Main.towerEchoSelectionDebug.Value)
        Console.WriteLine("Selected weapon item is null !?");
      return;
    }

    if (selectedItem.WeaponData == null) {
      if (Main.towerEchoSelectionDebug.Value)
        Console.WriteLine("Selected weapon data is null !?");
      return;
    }

    if (Main.towerEchoSelectionDebug.Value) {
      Console.WriteLine($"Selected weapon item is {selectedItem.name}");
      Console.WriteLine(
          $"Echoing weapon to {Main.towerEchoSelectionList.Count} towers");
    }

    for (var i = 0; i < Main.towerEchoSelectionList.Count; i++) {
      try {
        var towerEchoSelection = Main.towerEchoSelectionList[i];
        if (towerEchoSelection.TryGetTarget(out var towerEchoSelectionT)) {
          if (towerEchoSelectionT == null) {
            if (Main.towerEchoSelectionDebug.Value)
              Console.WriteLine("Tower defence module is null !?");
            continue;
          }

          if (Main.towerEchoSelectionDebug.Value)
            Console.WriteLine(
                $"Tower defence weapon is {towerEchoSelectionT.SelectedWeapon.ToString()}");

          towerEchoSelectionT.SetFieldOfView(selectedItem.WeaponData);
          towerEchoSelectionT.SetupWeapon(selectedItem.WeaponData);

          var selectedWeapon =
              towerEchoSelectionT.SelectedWeapon == null
                  ? "null"
                  : towerEchoSelectionT.SelectedWeapon.ToString();
          if (Main.towerEchoSelectionDebug.Value)
            Console.WriteLine(
                $"Tower defence weapon modified to {selectedWeapon}");
        }
      } catch (NullReferenceException e) {
        Console.WriteLine($"Failed to set tower defence weapon: {e}");
      }
    }
  }

  // TODO: Add toggle for this
  [HarmonyTranspiler]
  [HarmonyPatch(typeof(ValidatableStructure), "Awake")]
  public static IEnumerable<CodeInstruction>
  PostfixValidatableStructure(IEnumerable<CodeInstruction> instructions) {
    var codes = new List<CodeInstruction>(instructions);
    int start = 0;
    int start2 = 0;
    int end = 0;
    int end2 = 0;
    for (var i = 0; i < codes.Count; i++) {
      if (codes[i].opcode == OpCodes.Ldc_I4_5) {
        Console.WriteLine(
            $"Opcode is {codes[i].opcode} and operand is {codes[i].operand} at index {i}");
        codes[i] = new CodeInstruction(
            OpCodes.Ldc_I4_2); // Array size after our deletion is 3 from 5
      }

      if (codes[i].opcode == OpCodes.Ldstr &&
          codes[i].operand.ToString() == "Tower") {
        Console.WriteLine(
            $"Opcode is {codes[i].opcode} and operand is {codes[i].operand} at index {i}");
        start2 = i - 2;
        end2 = i + 2;
      }

      if (codes[i].opcode == OpCodes.Ldstr &&
          codes[i].operand.ToString() == "Farmland") {
        Console.WriteLine(
            $"Opcode is {codes[i].opcode} and operand is {codes[i].operand} at index {i}");
        Console.WriteLine(
            $"Opcode is {codes[i - 1].opcode} and operand is {codes[i - 1].operand} at index {i - 1}");
        codes[i - 1].opcode =
            OpCodes.Ldc_I4_1; // Because we deleted the previous element this
                              // one is now index 1 instead of 2
      }

      if (codes[i].opcode == OpCodes.Ldstr &&
          codes[i].operand.ToString() == "Wall") {
        Console.WriteLine(
            $"Opcode is {codes[i].opcode} and operand is {codes[i].operand} at index {i}");
        start = i - 2;
        end = i + 6;
        break;
      }
    }

    Console.WriteLine(
        $"Start is {start} with opcode {codes[start].opcode} and operand {codes[start].operand}");
    Console.WriteLine(
        $"End is {end} with opcode {codes[end].opcode} and operand {codes[end].operand}");
    codes.RemoveRange(start, end - start);
    Console.WriteLine($"Removed {end - start} instructions");

    Console.WriteLine(
        $"Start2 is {start2} with opcode {codes[start2].opcode} and operand {codes[start2].operand}");
    Console.WriteLine(
        $"End2 is {end2} with opcode {codes[end2].opcode} and operand {codes[end2].operand}");
    codes.RemoveRange(start2, end2 - start2);
    Console.WriteLine($"Removed {end2 - start2} instructions");

    return codes.AsEnumerable();
  }

  // TODO: Add toggle for this
  [HarmonyPrefix]
  [HarmonyPatch(typeof(WallConstructor), MethodType.Constructor,
                typeof(Wall.Factory), typeof(WorkersRadio), typeof(WallConfig))]
  public static void PostfixWallConstructor(Wall.Factory factory,
                                            WorkersRadio workersRadio,
                                            ref WallConfig wallConfig) {
    Console.WriteLine($"Wall config created");
    wallConfig.maxAngel = 2f;
  }

  [HarmonyPrefix]
  [HarmonyPatch(typeof(AdaptController), MethodType.Constructor,
                typeof(AdaptConfig))]
  public static void PostfixAdaptConfig(ref AdaptConfig adaptConfig) {
    var trav = Traverse.Create(adaptConfig);
    var buildingsMaxVolume = trav.Field<float>("buildingsMaxVolume");

    if (Main.buildingMaxVolumeDebug.Value)
      Console.WriteLine($"Building max volume is {buildingsMaxVolume.Value}");

    buildingsMaxVolume.Value = Main.buildingMaxVolume.Value;

    if (Main.buildingMaxVolumeDebug.Value)
      Console.WriteLine(
          $"Building max volume modified to {buildingsMaxVolume.Value}");
  }
}
}