/***************************************************************************
 *                                Target.cs
 *                            -------------------
 *   begin                : May 1, 2002
 *   copyright            : (C) The RunUO Software Team
 *   email                : info@runuo.com
 *
 *   $Id$
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

using System;
using Server.Network;

namespace Server.Targeting
{
	public abstract class Target
	{
		private static int m_NextTargetID;

		private int m_TargetID;
		private int m_Range;
		private bool m_AllowGround;
		private bool m_CheckLOS;
		private bool m_AllowNonlocal;
		private bool m_DisallowMultis;
		private TargetFlags m_Flags;
		private DateTime m_TimeoutTime;

		public DateTime TimeoutTime{ get{ return m_TimeoutTime; } }

		protected Target( int range, bool allowGround, TargetFlags flags )
		{
			m_TargetID = ++m_NextTargetID;
			m_Range = range;
			m_AllowGround = allowGround;
			m_Flags = flags;

			m_CheckLOS = true;
		}

		public static void Cancel( Mobile m )
		{
			NetState ns = m.NetState;

			if ( ns != null )
				ns.Send( CancelTarget.Instance );

			Target targ = m.Target;

			if ( targ != null )
				targ.OnTargetCancel( m, TargetCancelType.Canceled );
		}

		private Timer m_TimeoutTimer;

		public void BeginTimeout( Mobile from, TimeSpan delay )
		{
			m_TimeoutTime = DateTime.Now + delay;

			if ( m_TimeoutTimer != null )
				m_TimeoutTimer.Stop();

			m_TimeoutTimer = new TimeoutTimer( this, from, delay );
			m_TimeoutTimer.Start();
		}

		public void CancelTimeout()
		{
			if ( m_TimeoutTimer != null )
				m_TimeoutTimer.Stop();

			m_TimeoutTimer = null;
		}

		public void Timeout( Mobile from )
		{
			CancelTimeout();
			from.ClearTarget();

			Cancel( from );

			OnTargetCancel( from, TargetCancelType.Timeout );
			OnTargetFinish( from );
		}

		private class TimeoutTimer : Timer
		{
			private Target m_Target;
			private Mobile m_Mobile;

			private static TimeSpan ThirtySeconds = TimeSpan.FromSeconds( 30.0 );
			private static TimeSpan TenSeconds = TimeSpan.FromSeconds( 10.0 );
			private static TimeSpan OneSecond = TimeSpan.FromSeconds( 1.0 );

			public TimeoutTimer( Target target, Mobile m, TimeSpan delay ) : base( delay )
			{
				m_Target = target;
				m_Mobile = m;

				if ( delay >= ThirtySeconds )
					Priority = TimerPriority.FiveSeconds;
				else if ( delay >= TenSeconds )
					Priority = TimerPriority.OneSecond;
				else if ( delay >= OneSecond )
					Priority = TimerPriority.TwoFiftyMS;
				else
					Priority = TimerPriority.TwentyFiveMS;
			}

			protected override void OnTick()
			{
				if ( m_Mobile.Target == m_Target )
					m_Target.Timeout( m_Mobile );
			}
		}

		public bool CheckLOS
		{
			get
			{
				return m_CheckLOS;
			}
			set
			{
				m_CheckLOS = value;
			}
		}

		public bool DisallowMultis
		{
			get
			{
				return m_DisallowMultis;
			}
			set
			{
				m_DisallowMultis = value;
			}
		}

		public bool AllowNonlocal
		{
			get
			{
				return m_AllowNonlocal;
			}
			set
			{
				m_AllowNonlocal = value;
			}
		}

		public int TargetID
		{
			get
			{
				return m_TargetID;
			}
		}

		public virtual Packet GetPacketFor( NetState ns )
		{
			return new TargetReq( this );
		}

		public void Cancel( Mobile from, TargetCancelType type )
		{
			CancelTimeout();
			from.ClearTarget();

			OnTargetCancel( from, type );
			OnTargetFinish( from );
		}

		public void Invoke( Mobile from, object targeted )
		{
			CancelTimeout();
			from.ClearTarget();

			if ( from.Deleted )
			{
				OnTargetCancel( from, TargetCancelType.Canceled );
				OnTargetFinish( from );
				return;
			}

			Point3D loc;
			Map map;

			if ( targeted is LandTarget )
			{
				loc = ((LandTarget)targeted).Location;
				map = from.Map;
			}
			else if ( targeted is StaticTarget )
			{
				loc = ((StaticTarget)targeted).Location;
				map = from.Map;
			}
			else if ( targeted is Mobile )
			{
				if ( ((Mobile)targeted).Deleted )
				{
					OnTargetDeleted( from, targeted );
					OnTargetFinish( from );
					return;
				}
				else if ( !((Mobile)targeted).CanTarget )
				{
					OnTargetUntargetable( from, targeted );
					OnTargetFinish( from );
					return;
				}

				loc = ((Mobile)targeted).Location;
				map = ((Mobile)targeted).Map;
			}
			else if ( targeted is Item )
			{
				Item item = (Item)targeted;

				if ( item.Deleted )
				{
					OnTargetDeleted( from, targeted );
					OnTargetFinish( from );
					return;
				}
				else if ( !item.CanTarget )
				{
					OnTargetUntargetable( from, targeted );
					OnTargetFinish( from );
					return;
				}

				object root = item.RootParent;

				if ( !m_AllowNonlocal && root is Mobile && root != from && from.AccessLevel == AccessLevel.Player )
				{
					OnNonlocalTarget( from, targeted );
					OnTargetFinish( from );
					return;
				}

				loc = item.GetWorldLocation();
				map = item.Map;
			}
			else
			{
				OnTargetCancel( from, TargetCancelType.Canceled );
				OnTargetFinish( from );
				return;
			}

			if ( map == null || map != from.Map || ( m_Range != -1 && !from.InRange( loc, m_Range ) ) )
			{
				OnTargetOutOfRange( from, targeted );
			}
			else
			{
				if ( !from.CanSee( targeted ) )
					OnCantSeeTarget( from, targeted );
				else if ( m_CheckLOS && !from.InLOS( targeted ) )
					OnTargetOutOfLOS( from, targeted );
				else if ( targeted is Item && ((Item)targeted).InSecureTrade )
					OnTargetInSecureTrade( from, targeted );
				else if ( targeted is Item && !((Item)targeted).IsAccessibleTo( from ) )
					OnTargetNotAccessible( from, targeted );
				else if ( targeted is Item && !((Item)targeted).CheckTarget( from, this, targeted ) )
					OnTargetUntargetable( from, targeted );
				else if ( targeted is Mobile && !((Mobile)targeted).CheckTarget( from, this, targeted ) )
					OnTargetUntargetable( from, targeted );
				else if ( from.Region.OnTarget( from, this, targeted ) )
					OnTarget( from, targeted );
			}

			OnTargetFinish( from );
		}

		protected virtual void OnTarget( Mobile from, object targeted )
		{
		}

		protected virtual void OnTargetNotAccessible( Mobile from, object targeted )
		{
			from.SendLocalizedMessage( 500447 ); // That is not accessible.
		}

		protected virtual void OnTargetInSecureTrade( Mobile from, object targeted )
		{
			from.SendLocalizedMessage( 500447 ); // That is not accessible.
		}

		protected virtual void OnNonlocalTarget( Mobile from, object targeted )
		{
			from.SendLocalizedMessage( 500447 ); // That is not accessible.
		}

		protected virtual void OnCantSeeTarget( Mobile from, object targeted )
		{
			from.SendLocalizedMessage( 500237 ); // Target can not be seen.
		}

		protected virtual void OnTargetOutOfLOS( Mobile from, object targeted )
		{
			from.SendLocalizedMessage( 500237 ); // Target can not be seen.
		}

		protected virtual void OnTargetOutOfRange( Mobile from, object targeted )
		{
			from.SendLocalizedMessage( 500446 ); // That is too far away.
		}

		protected virtual void OnTargetDeleted( Mobile from, object targeted )
		{
		}

		protected virtual void OnTargetUntargetable( Mobile from, object targeted )
		{
			from.SendLocalizedMessage( 500447 ); // That is not accessible.
		}

		protected virtual void OnTargetCancel( Mobile from, TargetCancelType cancelType )
		{
		}

		protected virtual void OnTargetFinish( Mobile from )
		{
		}

		public int Range
		{
			get
			{
				return m_Range;
			}
			set
			{
				m_Range = value;
			}
		}

		public bool AllowGround
		{
			get
			{
				return m_AllowGround;
			}
			set
			{
				m_AllowGround = value;
			}
		}

		public TargetFlags Flags
		{
			get
			{
				return m_Flags;
			}
			set
			{
				m_Flags = value;
			}
		}
	}
}