using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.AI.Group;


namespace RimWorld{
public class CompForbiddable : ThingComp
{
	//Working vars
	private bool forbiddenInt = false;

	public bool Forbidden
	{
		get
		{
			return forbiddenInt;
		}
		set
		{
			if( value == forbiddenInt )
				return;
			
			forbiddenInt = value;

			if( forbiddenInt )
				ListerHaulables.Notify_Forbidden(parent);
			else
				ListerHaulables.Notify_Unforbidden(parent);

			if( parent is Building_Door )
				Reachability.ClearCache();
		}
	}



	public override void PostExposeData()
	{
		Scribe_Values.LookValue(ref forbiddenInt, "forbidden", false);	
	}
	
	
	public override void PostDraw()
	{	
		if( forbiddenInt )
		{
			if( parent is Blueprint || parent is Frame )
			{
				// for blueprints and frames we draw big overlay only if size is greater than 1x1
				if( parent.def.size.x > 1 || parent.def.size.z > 1 )
					OverlayDrawer.DrawOverlay(parent, OverlayTypes.ForbiddenBig);
				else
					OverlayDrawer.DrawOverlay(parent, OverlayTypes.Forbidden);
			}
			else if( parent.def.category == ThingCategory.Building )
				OverlayDrawer.DrawOverlay(parent, OverlayTypes.ForbiddenBig);
			else
				OverlayDrawer.DrawOverlay(parent, OverlayTypes.Forbidden);
		}
	}
	
	public override void PostSplitOff( Thing piece )
	{
		piece.SetForbidden( forbiddenInt );
	}

	public override IEnumerable<Command> CompGetGizmosExtra()
	{
		Command_Toggle com = new Command_Toggle();
		com.hotKey = KeyBindingDefOf.CommandItemForbid;
		com.icon = TexCommand.Forbidden;
		com.isActive = ()=>!forbiddenInt;
		com.toggleAction = ()=>
			{
				Forbidden = !Forbidden;
				ConceptDatabase.KnowledgeDemonstrated(ConceptDefOf.Forbidding, KnowledgeAmount.SpecificInteraction);
			};

		if( forbiddenInt )
			com.defaultDesc = "CommandForbiddenDesc".Translate();
		else
			com.defaultDesc = "CommandNotForbiddenDesc".Translate();

		com.tutorHighlightTag = "ToggleForbidden";

		yield return com;
	}
}


public static class ForbidUtility
{
	public static void SetForbidden(this Thing t, bool value, bool warnOnFail = true)
	{
		if( t == null )
		{
			if( warnOnFail )
				Log.Error("Tried to SetForbidden on null Thing." );
			return;
		}

		ThingWithComps twc = t as ThingWithComps;
		if( twc == null )
		{
			if( warnOnFail )
				Log.Error("Tried to SetForbidden on non-ThingWithComps Thing " + t );
			return;
		}
		
		CompForbiddable f = twc.GetComp<CompForbiddable>();
		if( f == null )
		{
			if( warnOnFail )
				Log.Error("Tried to SetForbidden on non-Forbiddable Thing " + t );
			
			return;
		}
		
		f.Forbidden = value;
	}

	public static void SetForbiddenIfOutsideHomeArea(this Thing t)
	{
		if( !t.Spawned )
			Log.Error("SetForbiddenIfOutsideHomeArea unspawned thing " + t);

		if( t.Position.InBounds() && !Find.AreaHome[t.Position] )
			SetForbidden(t, true, false);
	}

	//===========================================================================

	private static bool CaresAboutForbidden(Pawn pawn, bool cellTarget)
	{
		if( pawn.InMentalState )
			return false;
		
		if( pawn.HostFaction != null )
			return false;

		//Animals following drafted masters don't care about forbidden cells (only things, like doors)
		if( cellTarget
			&& pawn.playerSettings != null
			&& pawn.playerSettings.master != null
			&& pawn.playerSettings.master.Drafted )
			return false;

		return true;
	}

	private static Area GetApplicableAllowedArea(Pawn pawn)
	{
		if( pawn.playerSettings != null
			&& pawn.playerSettings.AreaRestriction != null
			&& pawn.playerSettings.AreaRestriction.TrueCount > 0 )
			return pawn.playerSettings.AreaRestriction;
		return null;
	}

	public static bool InAllowedArea(this IntVec3 c, Pawn forPawn)
	{
		var aa = GetApplicableAllowedArea(forPawn);

		if( aa != null && !aa[c] )
			return false;

		return true;
	}

	/// <summary>
	/// Returns true if t is forbidden as a destination for pawn in the pawn's current state.
	/// </summary>
	public static bool IsForbidden(this Thing t, Pawn pawn)
	{
		if( !CaresAboutForbidden(pawn, false) )
			return false;

		//Is t's position forbidden?
		if( t.Spawned && t.Position.IsForbidden(pawn) )
			return true;

		//Is t forbidden directly?
		if( IsForbidden(t, pawn.Faction) )
			return true;

		//Is it one of lord's extra forbidden things?
		var lord = pawn.GetLord();
		if( lord != null && lord.extraForbiddenThings.Contains(t) )
			return true;

		return false;
	}

	/// <summary>
	/// Returns true if t is forbidden for pawn in the pawn's current state.
	/// Except in the case of doors. We pass those even if their position is forbidden.
	/// </summary>
	public static bool IsForbiddenToPass(this Thing t, Pawn pawn)
	{
		if( !CaresAboutForbidden(pawn, false) )
			return false;

		//We don't want to intepret doors as forbidden just because their cell is
		//Since door forbidding affects reachability, and doors are never destinations
		//Allowed areas reflect allowed destinations and preferred paths - not allowed paths
		//
		//However, doors can be destinations if they're being repaired or something.
		//
		if( t.Spawned && t.Position.IsForbidden(pawn) && !(t is Building_Door) )
			return true;

		//Is thing forbidden directly?
		if( IsForbidden(t, pawn.Faction) )
			return true;

		return false;
	}

	/// <summary>
	/// Returns true if the cell is forbidden for pawn in the pawn's current state.
	/// </summary>
	public static bool IsForbidden(this IntVec3 c, Pawn pawn)
	{
		if( !CaresAboutForbidden(pawn, true) )
			return false;

		//Forbidden because outside allowed area?
		if( !c.InAllowedArea(pawn) )
			return true;

		return false;
	}

	/// <summary>
	/// Returns true if the region is entirely outside pawn's allowed area.
	/// </summary>
	public static bool IsForbiddenEntirely(this Region r, Pawn pawn)
	{
		if( !CaresAboutForbidden(pawn, true) )
			return false;

		//Forbidden because outside allowed area?
		var aa = GetApplicableAllowedArea(pawn);
		if( aa != null && r.OverlapWith(aa) == AreaOverlap.None )
			return true;

		return false;
	}

	/// <summary>
	/// Returns true if the thing is marked forbidden for the faction (e.g. by direct per-thing forbid marking).
	/// If you're checking in relation to a pawn, use IsForbidden with the pawn directly.
	/// </summary>
	public static bool IsForbidden(this Thing t, Faction faction)
	{
		if( faction == null )
			return false;

		if( faction != Faction.OfPlayer )
			return false;

		ThingWithComps twc = t as ThingWithComps;
		if( twc == null )
			return false;
		
		CompForbiddable f = twc.GetComp<CompForbiddable>();
		if( f == null )
			return false;
		
		return f.Forbidden;
	}

}}







