﻿using System;
using System.Collections.Generic;
using HarmonyLib;
using TaleWorlds.Core;
using TaleWorlds.MountAndBlade;
using Helpers; // TaleWorlds.MountAndBlade.Helpers.dll
using TaleWorlds.CampaignSystem;
using TaleWorlds.CampaignSystem.SandBox.GameComponents;
using TaleWorlds.Localization;

/* references
 * TaleWorlds.Library.dll
 * TaleWorlds.CampaignSystem.dll
 * TaleWorlds.MountAndBlade.dll
 * TaleWorlds.MountAndBlade.Helpers.dll
 * TaleWorlds.Localization.dll
 * TaleWorlds.Core.dll
 * 0Harmony.dll
*/


// TaleWorlds.CampaignSystem.SandBox.GameComponents.DefaultSettlementFoodModel
// TaleWorlds.CampaignSystem.SandBox.GameComponents.DefaultSettlementGarrisonModel


// Garrisons don't eat food | either edit CalculateTownFoodChangeInternal (CTD) or dupe the garrison effect +-=0 at CalculateTownFoodStocksChange
// Garrisons don't starve

namespace VsMod_GarrisonsDontEatNorStarve
{
	public class VsMod_GarrisonsDontEatNorStarve_SubModule : MBSubModuleBase
	{
		protected override void OnSubModuleLoad()
		{
			base.OnSubModuleLoad();
			var harmony = new Harmony("asdasdadsadsasdasdasdasdadsadsa");
			harmony.PatchAll();
		}
		protected override void OnBeforeInitialModuleScreenSetAsRoot()
		{
			base.OnBeforeInitialModuleScreenSetAsRoot();
			InformationManager.DisplayMessage(new InformationMessage("Vaeringjar's Garrisons Don't Eat Nor Starve"));
		}

		// for reflection? // based on bannerlord tweaks
		protected override void OnGameStart(Game game, IGameStarter gameStarterObject)
		{
			base.OnGameStart(game, gameStarterObject);

			AddModels(gameStarterObject as CampaignGameStarter);
		}
		private void AddModels(CampaignGameStarter gameStarter)
		{
			if (gameStarter != null)
			{
				gameStarter.AddModel(new DefaultSettlementGarrisonModel());
			}
		}
	}


	// -------------------------------------------------------------------------------- # DefaultSettlementFoodModel # 

	// CalculateTownFoodChangeInternal // CalculateTownFoodStocksChange
	[HarmonyPatch(typeof(DefaultSettlementFoodModel), "CalculateTownFoodStocksChange")]
	internal class VzMod_TESTS_Override1
	{
		public static void Postfix(Town town, StatExplainer explanation, ref float __result)
		{
			TextObject _thisisatextobject = new TextObject("V's Garrisons Don't Eat (Nor Starve)");
			ExplainedNumber explainedNumberB = new ExplainedNumber(0f, explanation, null);

			MobileParty garrisonParty = town.GarrisonParty;
			int num = (garrisonParty != null) ? garrisonParty.Party.NumberOfAllMembers : 0;
			num = num / 20;
			explainedNumberB.Add((float)num, _thisisatextobject);

			__result += (int)explainedNumberB.ResultNumber;
		}
	}

	// -------------------------------------------------------------------------------- # DefaultSettlementGarrisonModel # 
	// DefaultSettlementGarrisonModel is inaccessible due to its protection level // need to use "reflection"

	// for e1.0.6

	// thanks "Doombox" 

	// copypasta from dnSpy

	internal class DefaultSettlementGarrisonModel : SettlementGarrisonModel
	{
		// Token: 0x06001EAC RID: 7852 RVA: 0x00077248 File Offset: 0x00075448
		public override int CalculateGarrisonChange(Settlement settlement, StatExplainer explanation = null)
		{
			return DefaultSettlementGarrisonModel.CalculateGarrisonChangeInternal(settlement, explanation);
		}

		// Token: 0x06001EAD RID: 7853 RVA: 0x00077254 File Offset: 0x00075454
		private static int CalculateGarrisonChangeInternal(Settlement settlement, StatExplainer explanation = null)
		{
			ExplainedNumber explainedNumber = new ExplainedNumber(0f, explanation, null);
			if (settlement.IsTown || settlement.IsCastle)
			{
				float loyalty = settlement.Town.Loyalty;
				//if (settlement.IsStarving)												// <- <- <- <- <- <- <- <- <-
				//{
				//	float foodChange = settlement.Town.FoodChange;
				//	int num = (settlement.Town.Owner.IsStarving && foodChange < 0f) ? ((int)(foodChange / 8f)) : 0;
				//	explainedNumber.Add((float)num, DefaultSettlementGarrisonModel._foodShortageText);
				//} 
				if (settlement.Town.GarrisonParty != null && ((float)settlement.Town.GarrisonParty.Party.NumberOfHealthyMembers + explainedNumber.ResultNumber) / (float)settlement.Town.GarrisonParty.Party.PartySizeLimit > settlement.Town.GarrisonParty.PaymentRatio)
				{
					int num2 = 0;
					do
					{
						num2++;
					}
					while (((float)settlement.Town.GarrisonParty.Party.NumberOfHealthyMembers + explainedNumber.ResultNumber - (float)num2) / (float)settlement.Town.GarrisonParty.Party.PartySizeLimit >= settlement.Town.GarrisonParty.PaymentRatio && (float)settlement.Town.GarrisonParty.Party.NumberOfHealthyMembers + explainedNumber.ResultNumber - (float)num2 > 0f && num2 < 20);
					explainedNumber.Add((float)(-(float)num2), DefaultSettlementGarrisonModel._paymentIsLess);
				}
				if (settlement.SiegeEvent != null)
				{
					int num3 = 0;
					foreach (Village village in settlement.BoundVillages)
					{
						if (village.VillageState == Village.VillageStates.Normal)
						{
							int num4 = 0;
							using (List<Hero>.Enumerator enumerator2 = village.Settlement.Notables.GetEnumerator())
							{
								while (enumerator2.MoveNext())
								{
									if (enumerator2.Current.SupporterOf == settlement.OwnerClan)
									{
										num4++;
									}
								}
							}
							if (num4 > 0)
							{
								explainedNumber.Add((float)num4, village.Name);
							}
							num3 += num4;
						}
					}
					if (num3 > 0)
					{
						explainedNumber.Add((float)num3, DefaultSettlementGarrisonModel._recruitFromVillageNotablesText);
					}
				}
			}
			DefaultSettlementGarrisonModel.GetSettlementGarrisonChangeDueToIssues(settlement, ref explainedNumber);
			return (int)explainedNumber.ResultNumber;
		}

		// Token: 0x06001EAE RID: 7854 RVA: 0x000774B0 File Offset: 0x000756B0
		private static void GetSettlementGarrisonChangeDueToIssues(Settlement settlement, ref ExplainedNumber result)
		{
			float value;
			if (IssueManager.DoesSettlementHasIssueEffect(DefaultIssueEffects.SettlementGarrison, settlement, out value))
			{
				result.Add(value, DefaultSettlementGarrisonModel._issues);
			}
		}

		// Token: 0x06001EAF RID: 7855 RVA: 0x000774D8 File Offset: 0x000756D8
		public override int FindNumberOfTroopsToTakeFromGarrison(MobileParty mobileParty, Settlement settlement)
		{
			MobileParty garrisonParty = settlement.Town.GarrisonParty;
			if (garrisonParty != null)
			{
				float totalStrength = garrisonParty.Party.TotalStrength;
				float num = FactionHelper.FindIdealGarrisonStrengthPerWalledCenter(mobileParty.MapFaction as Kingdom, settlement.OwnerClan);
				num *= (settlement.IsTown ? 2f : 1f);
				float num2 = (float)mobileParty.Party.PartySizeLimit * mobileParty.PaymentRatio;
				int numberOfAllMembers = mobileParty.Party.NumberOfAllMembers;
				float num3 = Math.Min(12f, num2 / (float)numberOfAllMembers) - 1f;
				float num4 = (float)Math.Pow((double)(totalStrength / num), 1.5);
				float num5 = (mobileParty.LeaderHero.Clan.Leader == mobileParty.LeaderHero) ? 2f : 1f;
				int num6 = MBRandom.RoundRandomized(num3 * num4 * num5);
				int num7 = 20;
				num7 *= (settlement.IsTown ? 2 : 1);
				if (num6 > garrisonParty.Party.MemberRoster.TotalRegulars - num7)
				{
					num6 = garrisonParty.Party.MemberRoster.TotalRegulars - num7;
				}
				return num6;
			}
			return 0;
		}

		// Token: 0x06001EB0 RID: 7856 RVA: 0x000775FC File Offset: 0x000757FC
		public override int FindNumberOfTroopsToLeaveToGarrison(MobileParty mobileParty, Settlement settlement)
		{
			MobileParty garrisonParty = settlement.Town.GarrisonParty;
			float num = 0f;
			if (garrisonParty != null)
			{
				num = garrisonParty.Party.TotalStrength;
			}
			float num2 = FactionHelper.FindIdealGarrisonStrengthPerWalledCenter(mobileParty.MapFaction as Kingdom, settlement.OwnerClan);
			num2 *= (settlement.IsTown ? 2f : 1f);
			if ((settlement.OwnerClan.Leader != Hero.MainHero || (mobileParty.LeaderHero != null && mobileParty.LeaderHero.Clan == Clan.PlayerClan)) && num < num2)
			{
				int numberOfRegularMembers = mobileParty.Party.NumberOfRegularMembers;
				float num3 = 1f + (float)mobileParty.Party.NumberOfWoundedRegularMembers / (float)mobileParty.Party.NumberOfRegularMembers;
				float num4 = (float)mobileParty.Party.PartySizeLimit * mobileParty.PaymentRatio;
				float num5 = (float)(Math.Pow((double)Math.Min(2f, (float)numberOfRegularMembers / num4), 1.2000000476837158) * 0.75);
				float num6 = (1f - num / num2) * (1f - num / num2);
				if (mobileParty.Army != null)
				{
					num6 = Math.Min(num6, 0.5f);
				}
				float num7 = 0.5f;
				if (settlement.OwnerClan == mobileParty.Leader.HeroObject.Clan || settlement.OwnerClan == mobileParty.Party.Owner.MapFaction.Leader.Clan)
				{
					num7 = 1f;
				}
				float num8 = (mobileParty.Army != null) ? 1.25f : 1f;
				float num9 = 1f;
				List<float> list = new List<float>(5);
				for (int i = 0; i < 5; i++)
				{
					list.Add(Campaign.MapDiagonal * Campaign.MapDiagonal);
				}
				foreach (Kingdom kingdom in Kingdom.All)
				{
					if (kingdom.IsKingdomFaction && mobileParty.MapFaction.IsAtWarWith(kingdom))
					{
						foreach (Settlement settlement2 in kingdom.Settlements)
						{
							float num10 = settlement2.Position2D.DistanceSquared(mobileParty.Position2D);
							for (int j = 0; j < 5; j++)
							{
								if (num10 < list[j])
								{
									for (int k = 4; k >= j + 1; k--)
									{
										list[k] = list[k - 1];
									}
									list[j] = num10;
									break;
								}
							}
						}
					}
				}
				float num11 = 0f;
				for (int l = 0; l < 5; l++)
				{
					num11 += (float)Math.Sqrt((double)list[l]);
				}
				num11 /= 5f;
				float num12 = Math.Max(0f, Math.Min(Campaign.MapDiagonal / 15f - Campaign.MapDiagonal / 30f, num11 - Campaign.MapDiagonal / 30f));
				float num13 = Campaign.MapDiagonal / 15f - Campaign.MapDiagonal / 30f;
				float num14 = num12 / num13;
				float num15 = Math.Min(0.7f, num9 * num5 * num6 * num7 * num8 * num3);
				return MBRandom.RoundRandomized((float)numberOfRegularMembers * num15);
			}
			return 0;
		}

		// Token: 0x04000C2C RID: 3116
		private static readonly TextObject _townWallsText = new TextObject("{=SlmhqqH8}Town Walls", null);

		// Token: 0x04000C2D RID: 3117
		private static readonly TextObject _moraleText = new TextObject("{=UjL7jVYF}Morale", null);

		// Token: 0x04000C2E RID: 3118
		private static readonly TextObject _foodShortageText = new TextObject("{=qTFKvGSg}Food Shortage", null);

		// Token: 0x04000C2F RID: 3119
		private static readonly TextObject _surplusFoodText = GameTexts.FindText("str_surplus_food", null);

		// Token: 0x04000C30 RID: 3120
		private static readonly TextObject _recruitFromCenterNotablesText = GameTexts.FindText("str_center_notables", null);

		// Token: 0x04000C31 RID: 3121
		private static readonly TextObject _recruitFromVillageNotablesText = GameTexts.FindText("str_village_notables", null);

		// Token: 0x04000C32 RID: 3122
		private static readonly TextObject _villageBeingRaided = GameTexts.FindText("str_village_being_raided", null);

		// Token: 0x04000C33 RID: 3123
		private static readonly TextObject _villageLooted = GameTexts.FindText("str_village_looted", null);

		// Token: 0x04000C34 RID: 3124
		private static readonly TextObject _townIsUnderSiege = GameTexts.FindText("str_villages_under_siege", null);

		// Token: 0x04000C35 RID: 3125
		private static readonly TextObject _retiredText = GameTexts.FindText("str_retired", null);

		// Token: 0x04000C36 RID: 3126
		private static readonly TextObject _paymentIsLess = GameTexts.FindText("str_payment_is_less", null);

		// Token: 0x04000C37 RID: 3127
		private static readonly TextObject _issues = new TextObject("{=D7KllIPI}Issues", null);
	}


	// --------------------------------------------------------------------------------

}
