using System;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Gumps;
using Server.Network;
using Server.Mobiles;
using Server.Targeting;
using Server.Spells.Fifth;
using Server.Spells.Seventh;

namespace Server.Spells.Ninjitsu
{
	public class AnimalForm : NinjaSpell
	{

		public static void Initialize()
		{
			EventSink.Login += new LoginEventHandler( OnLogin );
		}

		public static void OnLogin( LoginEventArgs e )
		{
			AnimalFormContext context = AnimalForm.GetContext( e.Mobile );

			if( context != null && context.SpeedBoost )
				e.Mobile.Send( SpeedControl.MountSpeed );
		}

		private static SpellInfo m_Info = new SpellInfo(
			"Animal Form", null,
			-1,
			9002
			);

		public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.0 ); } }

		public override double RequiredSkill{ get{ return 0.0; } }
		public override int RequiredMana{ get{ return (Core.ML ? 10 : 0); } }
		public override int CastRecoveryBase{ get { return (Core.ML ? 10 : base.CastRecoveryBase); } }

		public override bool BlockedByAnimalForm{ get{ return false; } }

		public AnimalForm( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
		{
		}

		public override bool CheckCast()
		{
			if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) )
			{
				Caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed.
				return false;
			}
			else if ( TransformationSpellHelper.UnderTransformation( Caster ) )
			{
				Caster.SendLocalizedMessage( 1063219 ); // You cannot mimic an animal while in that form.
				return false;
			}
			else if ( DisguiseTimers.IsDisguised( Caster ) )
			{
				Caster.SendLocalizedMessage( 1061631 ); // You can't do that while disguised.
				return false;
			}

			return base.CheckCast();
		}

		public override bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable )
		{
			return false;
		}

		public override void OnBeginCast()
		{
			base.OnBeginCast();

			Caster.FixedEffect( 0x37C4, 10, 14, 4, 3 );
		}

		public override bool CheckFizzle()
		{
			// Spell is initially always successful, and with no skill gain.
			return true;
		}

		public override void OnCast()
		{
			if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) )
			{
				Caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed.
			}
			else if( TransformationSpellHelper.UnderTransformation( Caster ) )
			{
				Caster.SendLocalizedMessage( 1063219 ); // You cannot mimic an animal while in that form.
			}
			else if ( !Caster.CanBeginAction( typeof( IncognitoSpell ) ) || (Caster.IsBodyMod && GetContext( Caster ) == null) )
			{
				DoFizzle();
			}
			else if ( CheckSequence() )
			{
				AnimalFormContext context = GetContext( Caster );

				int mana = ScaleMana( RequiredMana );
				if ( mana > Caster.Mana )
				{
					Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
				}
				else if ( context != null )
				{
						RemoveContext( Caster, context, true );
						Caster.Mana -= mana;
				}
				else if ( Caster is PlayerMobile )
				{
					if ( GetLastAnimalForm( Caster ) == -1 || DateTime.Now - Caster.LastMoveTime > Caster.ComputeMovementSpeed( Caster.Direction ) )
					{
						Caster.CloseGump( typeof( AnimalFormGump ) );
						Caster.SendGump( new AnimalFormGump( Caster, m_Entries, this ) );
					}
					else
					{
						if ( Morph( Caster, GetLastAnimalForm( Caster ) ) == MorphResult.Fail )
							DoFizzle();
						else
							Caster.Mana -= mana;
					}
				}
				else
				{
					if ( Morph( Caster, GetLastAnimalForm( Caster ) ) == MorphResult.Fail )
						DoFizzle();
					else
						Caster.Mana -= mana;
				}
			}

			FinishSequence();
		}

		private static Hashtable m_LastAnimalForms = new Hashtable();

		public int GetLastAnimalForm( Mobile m )
		{
			if ( m_LastAnimalForms.Contains( m ) )
				return (int)m_LastAnimalForms[m];

			return -1;
		}

		public enum MorphResult
		{
			Success,
			Fail,
			NoSkill
		}

		public static MorphResult Morph( Mobile m, int entryID )
		{
			if ( entryID < 0 || entryID >= m_Entries.Length )
				return MorphResult.Fail;

			AnimalFormEntry entry = m_Entries[entryID];

			m_LastAnimalForms[m] = entryID;	//On OSI, it's the last /attempted/ one not the last succeeded one

			if ( m.Skills.Ninjitsu.Value < entry.ReqSkill )
			{
				string args = String.Format( "{0}\t{1}\t ", entry.ReqSkill.ToString( "F1" ), SkillName.Ninjitsu );
				m.SendLocalizedMessage( 1063013, args ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
				return MorphResult.NoSkill;
			}

			/*
			if( !m.CheckSkill( SkillName.Ninjitsu, entry.ReqSkill, entry.ReqSkill + 37.5 ) )
				return MorphResult.Fail;
			 * 
			 * On OSI,it seems you can only gain starting at '0' using Animal form.  
			*/
			
			double ninjitsu = m.Skills.Ninjitsu.Value;

			if ( ninjitsu < entry.ReqSkill + 37.5 )
			{
				double chance = (ninjitsu - entry.ReqSkill) / 37.5;

				if ( chance < Utility.RandomDouble() )
					return MorphResult.Fail;
			}

			m.CheckSkill( SkillName.Ninjitsu, 0.0, 37.5 );

			BaseMount.Dismount( m );
			m.Flying = false;

			m.BodyMod = entry.BodyMod;

			if ( entry.HueMod > 0 )
				m.HueMod = entry.HueMod;

			if ( entry.SpeedBoost )
				m.Send( SpeedControl.MountSpeed );

			SkillMod mod = null;

			if ( entry.StealthBonus )
			{
				mod = new DefaultSkillMod( SkillName.Stealth, true, 20.0 );
				mod.ObeyCap = true;
				m.AddSkillMod( mod );
			}

			#region Heritage Items			
			else if ( entry.StealingBonus )
			{
				mod = new DefaultSkillMod( SkillName.Stealing, true, 10.0 );
				mod.ObeyCap = true;
				m.AddSkillMod( mod );
			}

			m.Target = null;
			#endregion

			Timer timer = new AnimalFormTimer( m, entry.BodyMod, entry.HueMod );
			timer.Start();

			AddContext( m, new AnimalFormContext( timer, mod, entry.SpeedBoost, entry.Type, entry.StealingBonus ) );
			m.CheckStatTimers();
			return MorphResult.Success;
		}


		private static Hashtable m_Table = new Hashtable();

		public static void AddContext( Mobile m, AnimalFormContext context )
		{
			m_Table[m] = context;

			if ( context.Type == typeof( BakeKitsune ) || context.Type == typeof( GreyWolf ) )
				m.CheckStatTimers();
		}

		public static void RemoveContext( Mobile m, bool resetGraphics )
		{
			AnimalFormContext context = GetContext( m );

			if ( context != null )
				RemoveContext( m, context, resetGraphics );
		}

		public static void RemoveContext( Mobile m, AnimalFormContext context, bool resetGraphics )
		{
			m_Table.Remove( m );

			#region Mondain's Legacy
			if ( context.SpeedBoost )
			{
				if ( m.Region is Server.Regions.TwistedWealdDesert )
					m.Send( SpeedControl.WalkSpeed );
				else
					m.Send( SpeedControl.Disable );
			}
			#endregion

			SkillMod mod = context.Mod;

			if ( mod != null )
				m.RemoveSkillMod( mod );

			if ( resetGraphics )
			{
				m.HueMod = -1;
				m.BodyMod = 0;
			}

			m.FixedParticles( 0x3728, 10, 13, 2023, EffectLayer.Waist );

			context.Timer.Stop();
		}

		public static AnimalFormContext GetContext( Mobile m )
		{
			return ( m_Table[m] as AnimalFormContext );
		}

		public static bool UnderTransformation( Mobile m )
		{
			return ( GetContext( m ) != null );
		}

		public static bool UnderTransformation( Mobile m, Type type )
		{
			AnimalFormContext context = GetContext( m );

			return ( context != null && context.Type == type );
		}
/*
		private delegate void AnimalFormCallback( Mobile from );
		private delegate bool AnimalFormRequirementCallback( Mobile from );
*/
 		public class AnimalFormEntry
		{
			private Type m_Type;
			private TextDefinition m_Name;
			private int m_ItemID;
			private int m_Hue;
			private int m_Tooltip;
			private double m_ReqSkill;
			private int m_BodyMod;
			private int m_HueMod;
			private bool m_StealthBonus;
			private bool m_SpeedBoost;

			public Type Type{ get{ return m_Type; } }
			public TextDefinition Name{ get{ return m_Name; } }
			public int ItemID{ get{ return m_ItemID; } }
			public int Hue{ get{ return m_Hue; } }
			public int Tooltip{ get{ return m_Tooltip; } }
			public double ReqSkill{ get{ return m_ReqSkill; } }
			public int BodyMod{ get{ return m_BodyMod; } }
			public int HueMod{ get{ return m_HueMod; } }
			public bool StealthBonus{ get{ return m_StealthBonus; } }
			public bool SpeedBoost{ get{ return m_SpeedBoost; } }

			#region Heritage Items
			private int m_X, m_Y;
			private bool m_StealingBonus;

			public int X{ get{ return m_X; } }
			public int Y{ get{ return m_Y; } }
			public bool StealingBonus { get{ return m_StealingBonus; } }
			#endregion

			/*
			private AnimalFormCallback m_TransformCallback;
			private AnimalFormCallback m_UntransformCallback;
			private AnimalFormRequirementCallback m_RequirementCallback;
			*/

			public AnimalFormEntry( Type type, TextDefinition name, int itemID, int hue, int x, int y, int tooltip, double reqSkill, int bodyMod, bool stealthBonus, bool speedBoost, bool stealingBonus )
				: this( type, name, itemID, hue, x, y, tooltip, reqSkill, bodyMod, 0, stealthBonus, speedBoost, stealingBonus )
			{
			}

			public AnimalFormEntry( Type type, TextDefinition name, int itemID, int hue, int x, int y, int tooltip, double reqSkill, int bodyMod, int hueMod, bool stealthBonus, bool speedBoost, bool stealingBonus )
			{
				m_Type = type;
				m_Name = name;
				m_ItemID = itemID;
				m_X = x; m_Y = y;
				m_Hue = hue;
				m_Tooltip = tooltip;
				m_ReqSkill = reqSkill;
				m_BodyMod = bodyMod;
				m_HueMod = hueMod;
				m_StealthBonus = stealthBonus;
				m_SpeedBoost = speedBoost;
				m_StealingBonus = stealingBonus;
			}
		}

		private static AnimalFormEntry[] m_Entries = new AnimalFormEntry[]
			{
				#region Mondain's Legacy
				new AnimalFormEntry( typeof( Kirin ),			1029632,	9632,    0,  6, 10, 1070811, 100.0, 0x84, false,  true, false ),
				new AnimalFormEntry( typeof( Unicorn ),			1018214,	9678,    0, 20, 10, 1070812, 100.0, 0x7A, false,  true, false ),
				new AnimalFormEntry( typeof( BakeKitsune ),		1030083,   10083,    0, 15, 15, 1070810,	 82.5, 0xF6, false,  true, false ),
				new AnimalFormEntry( typeof( GreyWolf ),		1028482,	9681, 2309, 25, 10, 1070810,  82.5, 0x19, false,  true, false ),
				new AnimalFormEntry( typeof( Llama ),			1028438,	8438,    0, 15,  8, 1070809,  70.0, 0xDC, false,  true, false ),
				new AnimalFormEntry( typeof( ForestOstard ),	1018273,	8503, 2212, 12, 10, 1070809,  70.0, 0xDA, false,  true, false ),
				new AnimalFormEntry( typeof( BullFrog ),		1028496,	8496, 2003, 15, 20, 1070807,  50.0, 0x51, /*0x5A3,*/ false, false, false ),
				new AnimalFormEntry( typeof( GiantSerpent ),	1018114,	9663, 2009,  8,  7, 1070808,  50.0, 0x15, false, false, false ),
				new AnimalFormEntry( typeof( Dog ),				1018280,	8476, 2309, 16, 17, 1070806,  40.0, 0xD9, false, false, false ),
				new AnimalFormEntry( typeof( Cat ),				1018264,	8475, 2309, 18, 17, 1070806,  40.0, 0xC9, false, false, false ),
				new AnimalFormEntry( typeof( Rat ),				1018294,	8483, 2309, 15, 20, 1070805,  20.0, 0xEE,  true, false, false ),
				new AnimalFormEntry( typeof( Rabbit ),			1028485,	8485, 2309, 19, 20, 1070805,  20.0, 0xCD,  true, false, false ),
				#region Heritage Items
				new AnimalFormEntry( typeof( SquirrelFormTalisman ),	1031671,  0x2D97,   0, 15, 15,        0,  20.0, 0x116,  false, false, false ), // squirrel
				new AnimalFormEntry( typeof( FerretFormTalisman ),		1031672,  0x2D98,   0, 21, 21,	1075220,  40.0, 0x117,  false, false, true ), // ferret
				new AnimalFormEntry( typeof( CuSidheFormTalisman ),		1031670,  0x2D96,   0, 19,  8,  1075221,  60.0, 0x115,  false, false, false ), // cu sidhe
				new AnimalFormEntry( typeof( ReptalonFormTalisman ),	1075202,  0x2D95,   0, -2,  0,  1075222,  90.0, 0x114,  false, false, false ), // reptalon
				#endregion
				#endregion
			};

		public static AnimalFormEntry[] Entries{ get{ return m_Entries; } }

		public class AnimalFormGump : Gump
		{

			//TODO: Convert this for ML to the BaseImageTileButtonsgump
			private Mobile m_Caster;
			private AnimalForm m_Spell;

			public AnimalFormGump( Mobile caster, AnimalFormEntry[] entries, AnimalForm spell ) : base( 50, 50 )
			{
				m_Caster = caster;
				m_Spell = spell;

				#region Heritage Items
				AddPage( 0 );

				AddBackground( 0, 0, 520, 404, 0x13BE );
				AddImageTiled( 10, 10, 500, 20, 0xA40 );
				AddImageTiled( 10, 40, 500, 324, 0xA40 );
				AddImageTiled( 10, 374, 500, 20, 0xA40 );
				AddAlphaRegion( 10, 10, 500, 384 );
				AddButton( 10, 374, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 );
				AddHtmlLocalized( 45, 376, 450, 20, 1060051, 0x7FFF, false, false ); // CANCEL
				AddHtmlLocalized( 14, 12, 500, 20, 1063394, 0x7FFF, false, false ); // <center>Animal Form Selection Menu</center>
				
				double ninjitsu = caster.Skills.Ninjitsu.Value;
				int count = 0;
				int y = 44;

				for ( int i = 0; i < entries.Length; i++ )
				{
					bool enabled = ( ninjitsu >= entries[ i ].ReqSkill );

					if ( i >= 12 && i <= 15 && ( caster.Talisman == null || caster.Talisman.GetType() != entries[ i ].Type ) )
						enabled = false;

					if ( enabled )
					{
						if ( count % 10 == 0 )
						{
							AddPage( count / 10 + 1 );

							y = 44;
						}
						else if ( count > 10 && count % 10 == 1 )
						{
							AddButton( 300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, count / 10 );
							AddHtmlLocalized( 340, 376, 60, 20, 1011393, 0x7FFF, false, false ); // Back
						}
						else if ( count % 10 == 9 )
						{
							AddButton( 400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, count / 10 + 2 );
							AddHtmlLocalized( 440, 376, 60, 20, 1043353, 0x7FFF, false, false ); // Next
						}

						int x = count % 2 == 0 ? 14 : 264;

						AddImageTiledButton( x, y, 0x918, 0x919, 0x64 + i, GumpButtonType.Reply, 0, entries[ i ].ItemID, 0x0, entries[ i ].X, entries[ i ].Y );
						
						if ( entries[ i ].Tooltip > 0 )
							AddTooltip( entries[ i ].Tooltip );
						
						TextDefinition.AddHtmlText( this, x + 84, y, 250, 60, entries[ i ].Name, false, false, 0x7FFF, 0x7FFF );
						
						if ( count % 2 == 1 )
							y += 64;

						count += 1;
					}
				}
				#endregion
			}

			public override void OnResponse( NetState sender, RelayInfo info )
			{
                if (info.ButtonID >= 100 && info.ButtonID <= 100 + m_Entries.Length)
                {
                    int mana = m_Spell.ScaleMana(m_Spell.RequiredMana);

                    if (mana > m_Caster.Mana)
                    {
                        m_Caster.SendLocalizedMessage(1060174, mana.ToString()); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
                    }
                    else
                    {
                        int entryID = info.ButtonID - 100;

                        if (AnimalForm.Morph(m_Caster, entryID) == MorphResult.Fail)
                        {
                            m_Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502632); // The spell fizzles.
                            m_Caster.FixedParticles(0x3735, 1, 30, 9503, EffectLayer.Waist);
                            m_Caster.PlaySound(0x5C);
                        }
                        else
                        {
                            m_Caster.Mana -= mana;
                        }
                    }
                }
			}
		}
	}

	public class AnimalFormContext
	{
		private Timer m_Timer;
		private SkillMod m_Mod;
		private bool m_SpeedBoost;
		private Type m_Type;

		public Timer Timer{ get{ return m_Timer; } }
		public SkillMod Mod{ get{ return m_Mod; } }
		public bool SpeedBoost{ get{ return m_SpeedBoost; } }
		public Type Type{ get{ return m_Type; } }

		#region Mondain's Legacy
		private bool m_StealingBonus;

		public bool StealingBonus{ get{ return m_StealingBonus; } }
		#endregion

		public AnimalFormContext( Timer timer, SkillMod mod, bool speedBoost, Type type, bool stealingBonus )
		{
			m_Timer = timer;
			m_Mod = mod;
			m_SpeedBoost = speedBoost;
			m_Type = type;

			#region Mondain's Legacy
			m_StealingBonus = stealingBonus;
			#endregion
		}
	}

	public class AnimalFormTimer : Timer
	{
		private Mobile m_Mobile;
		private int m_Body;
		private int m_Hue;

		#region Heritage Items
		private int m_Delay = 10;
		private Mobile m_Target;
		#endregion

		public AnimalFormTimer( Mobile from, int body, int hue ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) )
		{
			m_Mobile = from;
			m_Body = body;
			m_Hue = hue;

			Priority = TimerPriority.FiftyMS;
		}

		protected override void OnTick()
		{
			#region Heritage Items
			if ( !m_Mobile.Deleted && m_Mobile.Alive ) 
			{
				if ( m_Body == 0x115 ) // Cu Sidhe
				{
					if ( m_Mobile.Hits < m_Mobile.HitsMax || m_Delay < 9 )
					{
						if ( m_Delay-- <= 0 )
						{
							m_Mobile.Hits += Utility.RandomMinMax( 28, 32 );
							m_Delay = 10;
						}
					}
				}
				else if ( m_Body == 0x114 ) // Reptalon
				{
					if ( m_Mobile.Combatant != null && m_Mobile.Combatant != m_Target )
					{
						m_Delay = 0;
						m_Target = m_Mobile.Combatant;
					}

					if ( m_Target != null && m_Mobile.InRange( m_Target.Location, 1 ) )
						m_Delay -= 8;

					if ( m_Target != null && m_Delay-- <= 0 )
					{
						if ( m_Target.Alive && !m_Target.IsDeadBondedPet && m_Mobile.CanBeHarmful( m_Target ) && m_Target.Map == m_Mobile.Map && m_Target.InRange( m_Mobile.Location, BaseCreature.DefaultRangePerception ) && m_Mobile.InLOS( m_Target ) )
						{
							m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Target );
							m_Mobile.Freeze( TimeSpan.FromSeconds( 1 ) );
							m_Mobile.PlaySound( 0x16A );
							m_Mobile.Animate( 12, 5, 1, true, false, 0 );

							Timer.DelayCall( TimeSpan.FromSeconds( 1.3 ), new TimerStateCallback( BreathEffect_Callback ), m_Target );
						}

						m_Delay = 10;
					}
				}
			}
			#endregion

			if ( m_Mobile.Deleted || !m_Mobile.Alive || m_Mobile.Body != m_Body || (m_Hue != 0 && m_Mobile.Hue != m_Hue) )
			{
				AnimalForm.RemoveContext( m_Mobile, true );
				Stop();
			}
		}

		#region Heritage Items
		public virtual void BreathEffect_Callback( object state )
		{
			Mobile target = (Mobile) state;

			if ( target.Alive && m_Mobile.CanBeHarmful( target ) )
			{			
				m_Mobile.PlaySound( 0x227 );
				Effects.SendMovingEffect( m_Mobile, target, 0x36D4, 5, 0, false, false, 0, 0 );

				Timer.DelayCall( TimeSpan.FromSeconds( 1 ), new TimerStateCallback( BreathDamage_Callback ), target );
			}
		}

		public virtual void BreathDamage_Callback( object state )
		{
			Mobile target = (Mobile) state;

			if ( target.Alive && m_Mobile.CanBeHarmful( target ) )
				AOS.Damage( target, m_Mobile, 20, 0, 100, 0, 0, 0 );
		}
		#endregion
	}
}