using System;
using Server.Targeting;
using Server.Mobiles;

namespace Server.Spells.Spellweaving
{
	public abstract class ArcaneSummon : ArcanistSpell
	{
		public abstract Type Type { get; }
		public abstract int Sound { get; }

		public virtual int Slots { get { return 1; } }
		public virtual bool TargetLocation { get { return false; } }
		public virtual bool Focused { get { return true; } }
		public virtual bool Controlled { get { return true; } }

		public virtual int DurationBase { get { return 0; } }
		public virtual int DurationFocusBonus { get { return 60; } }
		public virtual int DurationSkillBonus { get { return 60; } }

		public ArcaneSummon( Mobile caster, Item scroll, SpellInfo info )	: base( caster, scroll, info )
		{
		}

		public override bool CheckCast()
		{
			if( !TargetLocation && Caster.Followers + Slots > Caster.FollowersMax )
			{
				Caster.SendLocalizedMessage( 1074270 ); // You have too many followers to summon another one.
				return false;
			}

			return base.CheckCast();
		}

		public override void OnCast()
		{
			if( TargetLocation )
			{
				Caster.Target = new InternalTarget( this );
			}
			else // if( !TargetLocation )
			{
				Summon( Caster.Location );
			}
		}

		private static int[] m_Offsets = new int[]
		{
			-1, -1,
			-1,  0,
			-1,  1,
			0, -1,
			0, 0,
			0,  1,
			1, -1,
			1,  0,
			1,  1
		};

		public Point3D Fix( IPoint3D p, Map map )
		{
			SpellHelper.GetSurfaceTop( ref p );

			Point3D o = new Point3D( p );

			if( map.CanSpawnMobile( o ) )
			{
				return o;
			}

			int offset = Utility.Random( 9 ) * 2;

			for( int i = 0; i < m_Offsets.Length; i += 2 )
			{
				o = new Point3D( p );

				o.X += m_Offsets[( offset + i ) % m_Offsets.Length];
				o.Y += m_Offsets[( offset + i + 1 ) % m_Offsets.Length];

				IPoint3D x = o;

				SpellHelper.GetSurfaceTop( ref x );

				o = new Point3D( x );

				if( map.CanSpawnMobile( o ) )
				{
					return o;
				}
			}

			return Point3D.Zero;
		}

		public void Summon( IPoint3D p )
		{
			Summon( new Point3D( p ) );
		}

		public void Summon( Point3D p )
		{
			if( !Caster.CanSee( p ) )
			{
				Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
			}
			else if( Caster.Followers + Slots > Caster.FollowersMax )
			{
				Caster.SendLocalizedMessage( 1074270 ); // You have too many followers to summon another one.
			}
			else if( ( !TargetLocation || SpellHelper.CheckTown( p, Caster ) ) && CheckSequence() )
			{
				int level = GetFocusLevel();
				double skill = Caster.Skills[SkillName.Spellweaving].Value;

				int duration = DurationBase + ( DurationSkillBonus * Math.Max( 1, (int)Math.Floor( skill / 24 ) ) ) + ( DurationFocusBonus * level ); // DurationBase + ( DurationSkillBonus * Max( 1, Floor( Skill / 24 ) ) ) + ( DurationFocusBonus * FocusLevel )
								
				int maxSlots = (int)( Math.Floor( 1.0 * ( Caster.FollowersMax - Caster.Followers ) / Slots ) ); // Floor( ( F.Max - F. ) / Slots )
				int quantity = Math.Min( 1 + ( Focused ? level : 0 ), maxSlots );
				
				for( int i = 0; i < quantity; i++ )
				{
					BaseCreature bc = Activator.CreateInstance( Type ) as BaseCreature;

					if( bc != null )
					{
						if( TargetLocation )
						{
							p = Fix( p, Caster.Map );

							if( p == Point3D.Zero )
							{
								Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
								bc.Delete();
								return;
							}
						}

						bc.ControlSlots = Slots; // Sanity.
						BaseCreature.Summon( bc, Controlled, Caster, p, Sound, TimeSpan.FromSeconds( duration ) );

						OnSummoned( bc );
					}
				}
			}

			FinishSequence();
		}

		public virtual void OnSummoned( BaseCreature bc )
		{
		}

		public class InternalTarget : Target
		{
			private ArcaneSummon m_Owner;

			public InternalTarget( ArcaneSummon owner ) : base( 12, true, TargetFlags.None )
			{
				m_Owner = owner;
			}

			protected override void OnTarget( Mobile m, object o )
			{
				if( o is IPoint3D )
				{
					m_Owner.Summon( (IPoint3D)o );
				}
			}

			protected override void OnTargetFinish( Mobile m )
			{
				m_Owner.FinishSequence();
			}
		}
	}
}