using System.Text;
using Verse;
using RimWorld;
using Multiplayer.API;
using System.Collections.Generic;
using rjw.Modules.Shared.Logs;
using System.Runtime.InteropServices;
using System.Linq;

namespace rjw
{
	/// <summary>
	/// Comp for things
	/// </summary>
	public class CompThingBodyPart : ThingComp
	{
		/// <summary>
		/// Comp for rjw Thing parts.
		/// </summary>

		public HediffDef_SexPart hediffDef;
		public string sizeLabel;                                       //eventually replace with below, maybe
		public float size;                                  //base size when part created, someday alter by operation
		private float? depth;
		private float? maxGirth;
		private float? length;
		private float? girth;
		private string cupSize;

		public float originalOwnerSize;
		public string originalOwnerRace;           //race of 1st owner race
		public string previousOwner;               //erm

		public SexFluidDef fluid;                               //cummies/milk - insectjelly/honey etc
		public float fluidAmount;                               //amount of Milk/Ejaculation/Wetness
		private bool initialised;

		public CompProperties_ThingBodyPart Props => (CompProperties_ThingBodyPart)props;

		/// <summary>
		/// Thing/part size in label
		/// </summary>
		public override string TransformLabel(string label)
		{
			if (!initialised)
				Init();

			if (sizeLabel != "")
				return $"{label} ({sizeLabel})";

			return label;
		}

		public override string CompInspectStringExtra()
		{
			return InspectStringLines().ToLineList();
		}

		private IEnumerable<string> InspectStringLines()
		{
			if (length.HasValue)
			{
				yield return "RJW_PartInfo_length".Translate(length.Value.ToString("F1"));
			}
			if (girth.HasValue)
			{
				yield return "RJW_PartInfo_girth".Translate(girth.Value.ToString("F1"));
			}
			if (depth.HasValue && hediffDef.genitalFamily != GenitalFamily.FemaleOvipositor)
			{
				yield return "RJW_PartInfo_depth".Translate(depth.Value.ToString("F1"));
			}
			if (maxGirth.HasValue && hediffDef.genitalFamily != GenitalFamily.FemaleOvipositor)
			{
				yield return "RJW_PartInfo_maxGirth".Translate(maxGirth.Value.ToString("F1"));
			}
			if (cupSize != null)
			{
				yield return "RJW_PartInfo_braCup".Translate(cupSize);
			}
			if (fluid != null)
			{
				yield return "RJW_PartInfo_fluidTypeFluidAmountHeading".Translate(fluid.label, fluidAmount.ToString("F0")).CapitalizeFirst();
			}
		}

		public override void PostExposeData()
		{
			base.PostExposeData();
			Scribe_Defs.Look(ref hediffDef, "hediffDef");
			Scribe_Defs.Look(ref fluid, "fluid");
			Scribe_Values.Look(ref sizeLabel, "sizeLabel");
			Scribe_Values.Look(ref size, "size");
			Scribe_Values.Look(ref originalOwnerSize, "originalOwnerSize");
			Scribe_Values.Look(ref originalOwnerRace, "originalOwnerRace");
			Scribe_Values.Look(ref previousOwner, "previousOwner");
			Scribe_Values.Look(ref fluidAmount, "fluidAmount");
			Scribe_Values.Look(ref initialised, "initialised", defaultValue: true);
			Scribe_Values.Look(ref cupSize, "cupSize");
			Scribe_Values.Look(ref length, "length");
			Scribe_Values.Look(ref depth, "depth");
			Scribe_Values.Look(ref maxGirth, "maxGirth");
			Scribe_Values.Look(ref girth, "girth");
		}

		public override IEnumerable<StatDrawEntry> SpecialDisplayStats()
		{
			// Absolutely terrible idea, but this check was originally in the description override as some kind of failsafe,
			// so sticking it here for now so as not to break things
			// TODO: Ensure this can be removed
			if (!initialised)
			{
				Init();
			}

			// Chosen semi-arbitrarily. Change these if stat ordering seems janky.
			int statOrder = StatDisplayOrder.Thing_BodyPartEfficiency + 10;
			var implantCategory = StatCategoryDefOf.Implant;
			var ownerCategory = RJWStatCategoryDefOf.OriginalPartOwner;

			//yield return new StatDrawEntry(category, "RJW_StatEntry_PartSize".Translate(), size.ToString("F2"), "", statOrder++);

			if (fluid != null)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_Fluid".Translate(), fluid.LabelCap, "", statOrder++);
			}

			if (fluidAmount != 0)
			{
				string fluidAmountKey = hediffDef.genitalFamily switch
				{
					GenitalFamily.Penis or GenitalFamily.MaleOvipositor => "RJW_StatEntry_FluidAmount_ejaculation",
					GenitalFamily.Vagina or GenitalFamily.Anus => "RJW_StatEntry_FluidAmount_wetness",
					_ => "RJW_StatEntry_FluidAmount"
				};

				yield return new StatDrawEntry(implantCategory, fluidAmountKey.Translate(), fluidAmount.ToString("F2"), "", statOrder++);
			}

			if (length.HasValue)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartLength".Translate(), length.Value.ToString("F2"), "", statOrder++);
			}

			if (girth.HasValue)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartGirth".Translate(), girth.Value.ToString("F2"), "", statOrder++);
			}

			if (depth.HasValue)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartDepth".Translate(), depth.Value.ToString("F2"), "", statOrder++);
			}

			if (maxGirth.HasValue)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartMaxGirth".Translate(), maxGirth.Value.ToString("F2"), "", statOrder++);
			}

			if (cupSize != null)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartCupSize".Translate(), cupSize, "", statOrder++);
			}

			if (!hediffDef.partTags.NullOrEmpty())
			{
				var tags = hediffDef.partTags;
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartTags".Translate(), tags.ToCommaList(), "", statOrder++);
			}

			// Ownership info
			if (!previousOwner.NullOrEmpty())
			{
				yield return new StatDrawEntry(ownerCategory, "RJW_StatEntry_PreviousOwner".Translate(), previousOwner, "", 1);
			}
			if (!originalOwnerRace.NullOrEmpty())
			{
				yield return new StatDrawEntry(ownerCategory, "RJW_StatEntry_OriginalOwnerRace".Translate(), originalOwnerRace, "", 2);
			}
			if (originalOwnerSize > 0f)
			{
				yield return new StatDrawEntry(ownerCategory, "RJW_StatEntry_OriginalOwnerSize".Translate(), originalOwnerSize.ToString("F1"), "", 3);
			}
		}

		public override void PostPostMake()
		{
			base.PostPostMake();
			Init();
		}

		/// <summary>
		/// fill comp data
		/// </summary>
		[SyncMethod]
		private void Init(Pawn pawn = null)
		{
			// TODO: Maybe un-link this from ThingDef xml and instead set randomly or inherit from removed part
			hediffDef = Props.hediffDef;

			bool originalOwnerIsKnown = true;

			if (pawn == null)
			{
				InitFromDef(hediffDef);
				initialised = true;
			}
			else
			{
				Hediff hd = HediffMaker.MakeHediff(hediffDef, pawn);
	
				HediffComp_SexPart hediffComp = hd.TryGetComp<HediffComp_SexPart>();
				if (hediffComp != null)
				{
					InitFromComp(hediffComp, originalOwnerIsKnown);
				}
			}
		}

		public void InitFromComp(HediffComp_SexPart hediffComp, bool recordOriginalOwner = true)
		{
			hediffDef = hediffComp.Def;
			fluid = hediffComp.Fluid;
			fluidAmount = hediffComp.partFluidFactor;
			size = hediffComp.Size;
			sizeLabel = hediffDef.GetStandardSizeLabel(size);
			if (!hediffDef.genitalTags.NullOrEmpty())
			{
				if (hediffDef.genitalTags.Contains(GenitalTag.CanPenetrate))
				{
					if (PartSizeCalculator.TryGetLength(hediffComp.parent, out float length))
					{
						this.length = length;
					}
					if (PartSizeCalculator.TryGetGirth(hediffComp.parent, out float girth))
					{
						this.girth = girth;
					}
				}
				if (hediffDef.genitalTags.Contains(GenitalTag.CanBePenetrated))
				{
					if (PartSizeCalculator.TryGetLength(hediffComp.parent, out float depth))
					{
						this.depth = depth;
					}
					if (PartSizeCalculator.TryGetGirth(hediffComp.parent, out float maxGirth))
					{
						this.maxGirth = maxGirth;
					}
				}
			}

			if (PartSizeCalculator.TryGetCupSize(hediffComp.parent, out float numericCupSize))
			{
				cupSize = BraSizeConfigDef.GetCupSizeLabel(numericCupSize);
			}

			if (recordOriginalOwner)
			{
				originalOwnerSize = hediffComp.originalOwnerSize;
				previousOwner = hediffComp.Pawn?.LabelNoCount;
				if (hediffComp.Pawn.IsHuman() || hediffComp.Pawn.Name != null)
				{
					originalOwnerRace = hediffComp.Pawn?.kindDef.race.LabelCap;
				}
			}
			initialised = true;
		}
	
		private void InitFromDef(HediffDef_SexPart hediffDef)
		{
			this.hediffDef = hediffDef;
			fluid = hediffDef.fluid;
			var bodySize = DefDatabase<PawnKindDef>.GetRandom().RaceProps.baseBodySize;
			size = hediffDef.GetRandomSize() * bodySize;
			fluidAmount = hediffDef.GetRandomFluidAmount(bodySize, size);
			sizeLabel = hediffDef.GetStandardSizeLabel(size);
			if (!hediffDef.genitalTags.NullOrEmpty())
			{
				if (hediffDef.genitalTags.Contains(GenitalTag.CanPenetrate))
				{
					if (PartSizeCalculator.TryGetLength(hediffDef, size, out float length))
					{
						this.length = length;
					}
					if (PartSizeCalculator.TryGetGirth(hediffDef, size, out float girth))
					{
						this.girth = girth;
					}
				}
				if (hediffDef.genitalTags.Contains(GenitalTag.CanBePenetrated))
				{
					if (PartSizeCalculator.TryGetLength(hediffDef, size, out float depth))
					{
						this.depth = depth;
					}
					if (PartSizeCalculator.TryGetGirth(hediffDef, size, out float maxGirth))
					{
						this.maxGirth = maxGirth;
					}
				}
			}

			if (PartSizeCalculator.TryGetCupSize(hediffDef, size, out float numericCupSize))
			{
				cupSize = BraSizeConfigDef.GetCupSizeLabel(numericCupSize);
			}
		}
	}
}