﻿using HarmonyLib;
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;
using Verse.AI;

namespace rjw
{
	[StaticConstructorOnStartup]
	static class RMB_Menu
	{

		static RMB_Menu()
		{
			Harmony harmony = new Harmony("rjw");
			//start sex options
			harmony.Patch(AccessTools.Method(typeof(FloatMenuMakerMap), "AddHumanlikeOrders"), prefix: null,
				postfix: new HarmonyMethod(typeof(RMB_Menu), nameof(SexFloatMenuOption)));
			//change sex options
			//harmony.Patch(AccessTools.Method(typeof(FloatMenuMakerMap), "AddHumanlikeOrders"), prefix: null,
			//	postfix: new HarmonyMethod(typeof(RMB_Menu), nameof(SexFloatMenuOption)));
		}

		//show rmb on
		public static TargetingParameters TargetParameters
		{
			get
			{
				if (targetParameters == null)
				{
					targetParameters = new TargetingParameters()
					{
						canTargetHumans = true,
						canTargetAnimals = true,
						mapObjectTargetsMustBeAutoAttackable = false,
					};
				}
				return targetParameters;
			}
		}

		private static TargetingParameters targetParameters = null;

		public static TargetingParameters TargetParemetersMasturbationChairOrBed(LocalTargetInfo target)
		{
			return new TargetingParameters()
			{
				canTargetBuildings = true,
				mapObjectTargetsMustBeAutoAttackable = false,
				validator = (TargetInfo target) =>
				{
					if (!target.HasThing)
						return false;
					Building building = target.Thing as Building;
					if (building == null)
						return false;
					if (building.def.building.isSittable)
						return true;
					if (building is Building_Bed)
						return true;
					return false;
				}
			};
		}

		public static TargetingParameters TargetParemetersMasturbationLoc(LocalTargetInfo target)
		{
			return new TargetingParameters()
			{
				canTargetLocations = true,
				mapObjectTargetsMustBeAutoAttackable = false,
				validator = (TargetInfo target) =>
				{
					if (!target.HasThing)
						return true;
					return false;
				}
			};
		}

		//TODO: dildo selection for masturbation/sex
		public static TargetingParameters TargetParemetersDildos(LocalTargetInfo target)
		{
			return new TargetingParameters()
			{
				canTargetItems = true,
				mapObjectTargetsMustBeAutoAttackable = false,
				validator = ((TargetInfo target) =>
				{
					if (!target.HasThing)
						return false;
					Thing dildo = target.Thing as Thing;
					if (dildo == null)
						return false;

					return true;
				})
			};
		}

		public static void SexFloatMenuOption(Vector3 clickPos, Pawn pawn, List<FloatMenuOption> opts)
		{
			// If the pawn in question cannot take jobs, don't bother.
			if (pawn.jobs == null)
				return;

			// If the pawn is drafted - quit.
			if (pawn.Drafted)
				return;

			// No menu for you - quit.
			//if (!SaveStorage.DataStore.GetPawnData(pawn).ShowRMB_Menu)
			//	return;

			//TODO: move checks below into ShowRMB_Menu
			//not heromode or override_control - quit
			if (!(RJWSettings.RPG_hero_control || RJWSettings.override_control))
				return;

			var HeroOK0 = false;    //is hero
			var HeroOK1 = false;    //owned hero?
			var HeroOK2 = false;    //not owned hero? maybe prison check etc in future
			if (RJWSettings.RPG_hero_control)
			{
				HeroOK0 = pawn.IsDesignatedHero();
				HeroOK1 = HeroOK0 && pawn.IsHeroOwner();
				HeroOK2 = HeroOK0 && !pawn.IsHeroOwner();
			}
			else if (!RJWSettings.override_control)
				return;

			//not hero, not override_control - quit
			if (!HeroOK0 && !RJWSettings.override_control)
				return;

			//Log.Message("show options HeroOK0 " + HeroOK0);
			//Log.Message("show options HeroOK1 " + HeroOK1);
			//Log.Message("show options HeroOK2 " + HeroOK2);

			//not owned hero - quit
			if (HeroOK0 && HeroOK2)
				return;

			//Log.Message("show options");

			// Find valid targets for sex.
			var validtargets = GenUI.TargetsAt(clickPos, TargetParameters);
			//Log.Message("targets count " + validtargets.Count());
			foreach (LocalTargetInfo target in validtargets)
			{
				// Ensure target is reachable.
				if (!pawn.CanReach(target, PathEndMode.ClosestTouch, Danger.Deadly))
				{
					//option = new FloatMenuOption("CannotReach".Translate(target.Thing.LabelCap, target.Thing) + " (" + "NoPath".Translate() + ")", null);
					continue;
				}

				//Log.Message("target " +  target.Label);
				opts.AddRange(GenerateSexOptions(pawn, target).Where(x => x.action != null));
				//sex-role?-pose ?
				//rjw?-sex(do fuck/rape checks)-role?-pose ?

			}
		}
		
		public static List<FloatMenuOption> GenerateSexOptions(Pawn pawn, LocalTargetInfo target)
		{
			List<FloatMenuOption> opts = new List<FloatMenuOption>();
			FloatMenuOption option = null;

			// Add menu options to masturbate, if self
			if (target.Pawn == pawn)
			{
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate".Translate(), delegate ()
				{
					FloatMenuUtility.MakeMenu(GenerateSoloSexRoleOptions(pawn, target).Where(x => x.action != null), (FloatMenuOption opt) => opt.Label, (FloatMenuOption opt) => opt.action);
				}, MenuOptionPriority.High), pawn, target);
				opts.AddDistinct(option);
			}

			// Add menu options for con sex, if not self
			if (target.Thing != pawn as Thing)
			{
				if (target.Pawn != null)
					if (!target.Pawn.HostileTo(pawn) && !target.Pawn.Downed && (xxx.can_be_fucked(target.Pawn) || xxx.can_fuck(target.Pawn)))
					{
						//TODO change whore JD to work manually
						// Add menu option to whore sex with target
						//option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("Solicit " + target.Pawn.Name, delegate ()
						//{
						//	if (!target.Pawn.IsColonist && !target.Pawn.IsPrisoner && pawn.CurrentBed() != null)
						//		pawn.jobs.TryTakeOrderedJob(new Job(xxx.whore_is_serving_visitors, target.Pawn, pawn.CurrentBed()));
						//}, MenuOptionPriority.High), pawn, target);
						//opts.AddDistinct(option);

						// Add menu option to have sex with target, if target finds you sexy enough
						if (xxx.is_human(target.Pawn) && SexAppraiser.would_fuck(target.Pawn, pawn) > 0.1f)
						{
							option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Sex".Translate() + target.Pawn.NameShortColored, delegate ()
							{
								if (target.Pawn.InBed())
									pawn.jobs.TryTakeOrderedJob(new Job(xxx.casual_sex, target.Pawn, target.Pawn.CurrentBed()));
								else
									pawn.jobs.TryTakeOrderedJob(new Job(xxx.quick_sex, target.Pawn));
							}, MenuOptionPriority.High), pawn, target);
							opts.AddDistinct(option);
						}
						else if (xxx.is_animal(target.Pawn) && RJWSettings.bestiality_enabled && target.Pawn.Faction == pawn.Faction)
						{
							if (pawn.ownership.OwnedBed != null && target.Pawn.CanReach(pawn.ownership.OwnedBed, PathEndMode.OnCell, Danger.Some))
							{
								// Add menu option to have sex with animal(invite to bed)
								option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Sex".Translate() + target.Pawn.NameShortColored, delegate ()
								{
									pawn.jobs.TryTakeOrderedJob(new Job(xxx.bestialityForFemale, target.Pawn, pawn.ownership.OwnedBed));
									//pawn.jobs.TryTakeOrderedJob(new Job(xxx.quick_sex, target.Pawn));
								}, MenuOptionPriority.High), pawn, target);
								opts.AddDistinct(option);
							}
						//	// Add menu option to have sex with animal(service animal on spot)
						//	option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Sex".Translate() + target.Pawn.NameShortColored, delegate ()
						//	{
						//		pawn.jobs.TryTakeOrderedJob(new Job(xxx.bestialityForFemale, target.Pawn, pawn.ownership.OwnedBed));
						//		//pawn.jobs.TryTakeOrderedJob(new Job(xxx.quick_sex, target.Pawn));
						//	}, MenuOptionPriority.High), pawn, target);
						//	opts.AddDistinct(option);

						}
					}

				// Add menu options for non con sex, if not self
				if (xxx.can_rape(pawn))
				{
					//Log.Message("targets can_rape " + target.Label);
					string text = null;
					Action action = null;

					if (target.Thing is Corpse && RJWSettings.necrophilia_enabled)
					{
						text = "RJW_RMB_RapeCorpse".Translate() + ((Corpse)target.Thing).InnerPawn.NameShortColored;
						action = delegate ()
						{
							pawn.jobs.TryTakeOrderedJob(new Job(xxx.RapeCorpse, target.Thing));
						};
					}
					else if (target.Pawn != null && xxx.can_be_fucked(target.Pawn) && (xxx.is_human(target.Pawn) || (xxx.is_animal(target.Pawn) && RJWSettings.bestiality_enabled)))
					{
						//Log.Message("targets can_rape 1 " + target.Label);
						if (target.Pawn.HostileTo(pawn))
						{
							//Log.Message("targets can_rape HostileTo " + target.Label);
							if (target.Pawn.Downed)
							{
								text = "RJW_RMB_RapeEnemy".Translate() + target.Pawn.NameShortColored;
								action = delegate ()
								{
									pawn.jobs.TryTakeOrderedJob(new Job(xxx.RapeEnemy, target.Pawn));
								};
							}
						}
						else if (xxx.is_animal(target.Pawn) && xxx.can_fuck(pawn))
						{
							text = "RJW_RMB_RapeAnimal".Translate() + target.Pawn.NameShortColored;
							action = delegate ()
							{
								pawn.jobs.TryTakeOrderedJob(new Job(xxx.bestiality, target.Pawn));
							};
						}
						else if (target.Pawn.IsDesignatedComfort())
						{
							//Log.Message("targets can_rape IsDesignatedComfort " + target.Label);
							text = "RJW_RMB_RapeCP".Translate() + target.Pawn.NameShortColored;
							action = delegate ()
							{
								pawn.jobs.TryTakeOrderedJob(new Job(xxx.RapeCP, target.Pawn));
							};
						}
						else if (xxx.can_get_raped(target.Pawn) && (xxx.get_vulnerability(target.Pawn) >= xxx.get_vulnerability(pawn)))
						{
							//Log.Message("targets can_rape else " + target.Label);
							text = "RJW_RMB_Rape".Translate() + target.Pawn.NameShortColored;
							action = delegate ()
							{
								pawn.jobs.TryTakeOrderedJob(new Job(xxx.RapeRandom, target.Pawn));
							};
						}

					}

					option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption(text, action, MenuOptionPriority.High), pawn, target);
					opts.AddDistinct(option);
				}
			}
			return opts;
		}
		public static List<FloatMenuOption> GenerateSexRoleOptions(Pawn pawn, LocalTargetInfo target)
		{
			List<FloatMenuOption> opts = new List<FloatMenuOption>();
			FloatMenuOption option = null;

			return opts;
		}
		public static List<FloatMenuOption> GenerateSexPoseOptions(Pawn pawn, LocalTargetInfo target)
		{
			List<FloatMenuOption> opts = new List<FloatMenuOption>();
			FloatMenuOption option = null;

			return opts;
		}

		//this options probably can be merged into 1, but for now/testing keep it separate
		public static List<FloatMenuOption> GenerateSoloSexRoleOptions(Pawn pawn, LocalTargetInfo target)
		{
			List<FloatMenuOption> opts = new List<FloatMenuOption>();
			FloatMenuOption option = null;

			option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_Bed".Translate(), delegate ()
			{
				Find.Targeter.BeginTargeting(TargetParemetersMasturbationChairOrBed(target), (LocalTargetInfo targetThing) =>
				{
					FloatMenuUtility.MakeMenu(GenerateSoloSexPoseOptions(pawn, target).Where(x => x.action != null), (FloatMenuOption opt) => opt.Label, (FloatMenuOption opt) => opt.action);
				});
			}, MenuOptionPriority.High), pawn, target);
			opts.AddDistinct(option);

			option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_At".Translate(), delegate ()
			{
				Find.Targeter.BeginTargeting(TargetParemetersMasturbationLoc(target), (LocalTargetInfo targetThing) =>
				{
					FloatMenuUtility.MakeMenu(GenerateSoloSexPoseOptions(pawn, target).Where(x => x.action != null), (FloatMenuOption opt) => opt.Label, (FloatMenuOption opt) => opt.action);
				});
			}, MenuOptionPriority.High), pawn, target);
			opts.AddDistinct(option);

			option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_Here".Translate(), delegate ()
			{
				FloatMenuUtility.MakeMenu(GenerateSoloSexPoseOptions(pawn, target).Where(x => x.action != null), (FloatMenuOption opt) => opt.Label, (FloatMenuOption opt) => opt.action);
			}, MenuOptionPriority.High), pawn, target);
			opts.AddDistinct(option);
			return opts;
		}

		public static List<FloatMenuOption> GenerateSoloSexPoseOptions(Pawn pawn, LocalTargetInfo target)
		{
			List<FloatMenuOption> opts = new List<FloatMenuOption>();
			FloatMenuOption option = null;

			var pawnpartBPR = Genital_Helper.get_genitalsBPR(pawn);
			var pawnparts = Genital_Helper.get_PartsHediffList(pawn, pawnpartBPR);

			bool pawnHasMouth = Genital_Helper.has_mouth(pawn) && !Genital_Helper.oral_blocked(pawn);
			bool pawnHasAnus = Genital_Helper.has_anus(pawn) && !Genital_Helper.anus_blocked(pawn);
			bool pawnHasBreasts = Genital_Helper.has_breasts(pawn) && !Genital_Helper.breasts_blocked(pawn) && Genital_Helper.can_do_breastjob(pawn);
			bool pawnHasVagina = Genital_Helper.has_vagina(pawn, pawnparts) && !Genital_Helper.vagina_blocked(pawn);
			bool pawnHasPenis = (Genital_Helper.has_penis_fertile(pawn, pawnparts) || Genital_Helper.has_penis_infertile(pawn, pawnparts)) && !Genital_Helper.penis_blocked(pawn);
			bool pawnHasTail = Genital_Helper.has_tail(pawn);

			bool pawnHasHands = pawn.health.hediffSet.GetNotMissingParts().Any(part => part.IsInGroup(BodyPartGroupDefOf.RightHand) || part.IsInGroup(BodyPartGroupDefOf.LeftHand)) && !Genital_Helper.hands_blocked(pawn);
			if (pawnHasHands)
				pawnHasHands = pawn.health?.capacities?.GetLevel(PawnCapacityDefOf.Manipulation) > 0;

			if (pawnHasPenis && pawnHasHands)
			{
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_Fap".Translate(), delegate ()
				{
					pawn.jobs.TryTakeOrderedJob(new Job(xxx.Masturbate_Quick, null, null, target.Cell));
				}, MenuOptionPriority.High), pawn, target);
				opts.AddDistinct(option);
			}

			if (pawnHasPenis && pawnHasMouth && pawnparts.Where(x => x.Severity > 0.8f && x.def.defName.ToLower().Contains("penis")).Any())
			{
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_AF".Translate(), delegate ()
				{
					pawn.jobs.TryTakeOrderedJob(new Job(xxx.Masturbate_Quick, null, null, target.Cell));
				}, MenuOptionPriority.High), pawn, target);
				opts.AddDistinct(option);
			}

			if (pawnHasVagina && pawnHasHands)
			{
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_Schlic".Translate(), delegate ()
				{
					pawn.jobs.TryTakeOrderedJob(new Job(xxx.Masturbate_Quick, null, null, target.Cell));
				}, MenuOptionPriority.High), pawn, target);
				opts.AddDistinct(option);
			}

			if (pawnHasVagina && pawnHasTail)
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_TJV".Translate(), delegate ()
				{
					pawn.jobs.TryTakeOrderedJob(new Job(xxx.Masturbate_Quick, null, null, target.Cell));
				}, MenuOptionPriority.High), pawn, target);
			opts.AddDistinct(option);

			if (pawnHasBreasts)
			{
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_Breasts".Translate(), delegate ()
				{
					pawn.jobs.TryTakeOrderedJob(new Job(xxx.Masturbate_Quick, null, null, target.Cell));
				}, MenuOptionPriority.High), pawn, target);
				opts.AddDistinct(option);
			}

			if (pawnHasPenis && pawnHasBreasts && pawnparts.Where(x => x.Severity >= 0.8f && x.def.defName.ToLower().Contains("penis")).Any())
			{
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_BJ".Translate(), delegate ()
				{
					pawn.jobs.TryTakeOrderedJob(new Job(xxx.Masturbate_Quick, null, null, target.Cell));
				}, MenuOptionPriority.High), pawn, target);
				opts.AddDistinct(option);
			}

			if (pawnHasAnus && pawnHasHands)
			{
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_Anal".Translate(), delegate ()
				{
					pawn.jobs.TryTakeOrderedJob(new Job(xxx.Masturbate_Quick, null, null, target.Cell));
				}, MenuOptionPriority.High), pawn, target);
				opts.AddDistinct(option);
			}

			if (pawnHasAnus && pawnHasTail)
				option = FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("RJW_RMB_Masturbate_TJA".Translate(), delegate ()
				{
					pawn.jobs.TryTakeOrderedJob(new Job(xxx.Masturbate_Quick, null, null, target.Cell));
				}, MenuOptionPriority.High), pawn, target);
			opts.AddDistinct(option);

			return opts;
		}
	}
}