﻿using RimWorld;
using System.Collections.Generic;
using System.Linq;
using Verse;
using rjw.Modules.Interactions;
using rjw.Modules.Interactions.Implementation;

namespace rjw.RMB
{
	/// <summary>
	/// Generator of RMB categories for masturbation.
	///
	/// Unlike the two-pawn options, masturbation menu is two has 3 levels:
	/// Masturbate -> Masturbate on bed
	///               Masturbate at (tile) -> Interaction 1
	///               Masturbate here         Interaction 2
	///                                       etc.
	/// </summary>
	static class RMB_Masturbate
	{
		/// <summary>
		/// Add masturbation option to <paramref name="opts"/>
		/// </summary>
		/// <param name="pawn">Selected pawn</param>
		/// <param name="opts">List of RMB options</param>
		/// <param name="target">Target of the right click</param>
		public static void AddFloatMenuOptions(Pawn pawn, List<FloatMenuOption> opts, LocalTargetInfo target)
		{
			FloatMenuOption opt = GenerateCategoryOption(pawn, target);
			if (opt != null)
				opts.Add(opt);
		}

		/// <summary>
		/// Generate one FloatMenuOption
		/// </summary>
		/// <param name="pawn"></param>
		/// <param name="target"></param>
		/// <param name="reverse"></param>
		/// <returns>Category-level item that opens a sub-menu on click</returns>
		private static FloatMenuOption GenerateCategoryOption(Pawn pawn, LocalTargetInfo target)
		{
			return 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);
		}

		/// <summary>
		/// Generates sub-menu options for a solo pawn
		/// </summary>
		/// <param name="pawn">Initiator pawn</param>
		/// <param name="target">Place to masturbate</param>
		/// <returns>A list of menu options, where each item represents a possible masturbation type</returns>
		private static List<FloatMenuOption> GenerateSoloSexRoleOptions(Pawn pawn, LocalTargetInfo target)
		{
			List<FloatMenuOption> opts = new();
			FloatMenuOption option = null;

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

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

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

		private static TargetingParameters TargetParemetersMasturbationChairOrBed(LocalTargetInfo target)
		{
			return new TargetingParameters()
			{
				canTargetBuildings = true,
				mapObjectTargetsMustBeAutoAttackable = false,
				validator = (TargetInfo target) =>
				{
					if (!target.HasThing)
						return false;
					if (target.Thing is not Building building)
						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;
				}
			};
		}

		/// <summary>
		/// Generates sub-sub-menu options for a solo pawn
		/// </summary>
		/// <param name="pawn">Initiator pawn</param>
		/// <param name="target">Place to masturbate</param>
		/// <returns>A list of menu options, where each item represents a possible masturbation interaction</returns>
		public static List<FloatMenuOption> GenerateSoloSexPoseOptions(Pawn pawn, LocalTargetInfo target)
		{
			List<FloatMenuOption> opts = new();
			FloatMenuOption option = null;
			ILewdInteractionValidatorService service = LewdInteractionValidatorService.Instance;

			foreach (InteractionDef d in SexUtility.SexInterractions)
			{
				var interaction = Modules.Interactions.Helpers.InteractionHelper.GetWithExtension(d);
				if (interaction.Extension.rjwSextype != xxx.rjwSextype.Masturbation.ToStringSafe())
					continue;

				if (!service.IsValid(d, pawn, pawn))
					continue;

				string label = interaction.Extension.RMBLabel.CapitalizeFirst();
				if (RJWSettings.DevMode)
					label += $" ( defName: {d.defName} )";

				option = new FloatMenuOption(label, delegate ()
				{
					RMB_Menu.HaveSex(pawn, xxx.Masturbate, target, d);
				}, MenuOptionPriority.High);
				opts.Add(FloatMenuUtility.DecoratePrioritizedTask(option, pawn, target));
			}

			if (RJWSettings.DevMode && opts.NullOrEmpty())
			{
				opts.Add(new FloatMenuOption("No interactions found", null));
			}

			return opts;
		}
	}
}