using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Verse;
using RimWorld;




namespace Verse.AI{
public static class ToilFailConditions
{
	public static Toil FailOn( this Toil toil, Func<Toil, bool> condition )
	{
		toil.AddEndCondition( () => 
			{
				if( condition(toil) )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return toil;
	}

	public static T FailOn<T>( this T f, Func<bool> condition ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
		{
			if(condition())
				return JobCondition.Incompletable;
			return JobCondition.Ongoing;
		});
		return f;
	}

	public static T FailOnDestroyed<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=> 
			{
				if( f.GetActor().jobs.curJob.GetTarget( ind).Thing.Destroyed )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T FailOnDespawned<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				var t = f.GetActor().jobs.curJob.GetTarget(ind).Thing;
				if( !t.SpawnedInWorld )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T FailOnDespawnedIfNonNull<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				Thing t = f.GetActor().jobs.curJob.GetTarget(ind).Thing;

				if( t == null )
					return JobCondition.Ongoing;

				if( !t.SpawnedInWorld)
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T EndOnDespawned<T>( this T f, TargetIndex ind, JobCondition endCondition = JobCondition.Incompletable ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				if( !f.GetActor().jobs.curJob.GetTarget(ind).Thing.SpawnedInWorld)
					return endCondition;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T FailOnDowned<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				var t = f.GetActor().jobs.curJob.GetTarget(ind).Thing;
				if( ((Pawn)t).Downed )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T FailOnAsleep<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				var t = f.GetActor().jobs.curJob.GetTarget(ind).Thing;
				if( !((Pawn)t).Awake() )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T FailOnNotCasualInterruptible<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				var t = f.GetActor().jobs.curJob.GetTarget(ind).Thing;
				if( !((Pawn)t).CasualInterruptibleNow() )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T FailOnBroken<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				var p = f.GetActor().jobs.curJob.GetTarget(ind).Thing as Pawn;
				if( p != null && p.Broken )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T FailOnBrokenAggro<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				var p = f.GetActor().jobs.curJob.GetTarget(ind).Thing as Pawn;
				if( p != null && p.BrokenAggro)
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}

	public static T FailOnForbidden<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
		{
			Pawn actor = f.GetActor();
			if( actor.Faction != Faction.OfColony )
				return JobCondition.Ongoing;
			
			if( actor.jobs.curJob.ignoreForbidden )
				return JobCondition.Ongoing;

			if( actor.jobs.curJob.GetTarget(ind).Thing.IsForbidden( actor ) )
				return JobCondition.Incompletable;
			return JobCondition.Ongoing;
		});
		return f;
	}

	public static T FailOnDespawnedOrForbidden<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.FailOnDespawned(ind);
		f.FailOnForbidden(ind);
		return f;
	}

	public static T FailOnDestroyedOrForbidden<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.FailOnDestroyed(ind);
		f.FailOnForbidden(ind);
		return f;
	}

	public static T FailOnThingMissingDesignation<T>( this T f, TargetIndex ind, DesignationDef desDef ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				var job =  f.GetActor().jobs.curJob;
				if( job.ignoreDesignations )
 					return JobCondition.Ongoing;
				if( Find.DesignationManager.DesignationOn(job.GetTarget(ind).Thing, desDef ) == null )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			}
		);
		return f;
	}

	public static T FailOnCellMissingDesignation<T>( this T f, TargetIndex ind, DesignationDef desDef ) where T : IJobEndable
	{
		f.AddEndCondition( ()=>
			{
				var job =  f.GetActor().jobs.curJob;
				if( job.ignoreDesignations )
 					return JobCondition.Ongoing;
				if( Find.DesignationManager.DesignationAt(job.GetTarget(ind).Cell, desDef ) == null )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			}
		);
		return f;
	}

	public static T FailOnBurningImmobile<T>( this T f, TargetIndex ind ) where T : IJobEndable
	{
		f.AddEndCondition( ()=> 
			{
				if(f.GetActor().jobs.curJob.GetTarget( ind).IsBurning() )
					return JobCondition.Incompletable;
				return JobCondition.Ongoing;
			});
		return f;
	}



	//======================================================================================
	//================================ Toil-only conditions ================================
	//======================================================================================

	public static Toil FailOnDespawnedOrForbiddenPlacedTargets( this Toil toil )
	{
		toil.AddFailCondition( ()=>
            {
                if(toil.actor.jobs.curJob.placedTargets == null)
                    return false;
                
        		foreach( var targ in toil.actor.jobs.curJob.placedTargets )
        		{
        			if( !targ.Thing.SpawnedInWorld
						|| (!toil.actor.CurJob.ignoreForbidden && targ.Thing.IsForbidden( toil.actor )) )
        				return true;
        		}
                
        		return false;
		    }
        );
		return toil;
	}
}}

