using System;
using Server;
using Server.Mobiles;

namespace Server.Misc
{
	public enum Trades
	{
		Alchemy = 0,
		Blacksmith = 1,
		Carpentry = 2,
		Cartography = 3,
		Cooking = 4,
		Fishing = 5,
		Fletching = 6,
		Inscribe = 7,
		Lumberjacking = 8,
		Mining = 9,
		Tailoring = 10,
		Tinkering = 11,
		Musicianship = 12,
		Magery = 13
	}

	public class SkillCheck
	{
		private static readonly bool AntiMacroCode = Server.Misc.Settings.AllowMacroing();

		public static TimeSpan AntiMacroExpire = TimeSpan.FromMinutes( 5.0 ); //How long do we remember targets/locations?
		public const int Allowance = 3;	//How many times may we use the same location/target for gain
		private const int LocationSize = 5; //The size of eeach location, make this smaller so players dont have to move as far
		private static bool[] UseAntiMacro = new bool[]
		{
			// true if this skill uses the anti-macro code, false if it does not

			false,// Archery
			false,// Bludgeoning
			false,// Fencing
			false,// Swords
			false,// Tactics
			false,// Parry
			false,// Concentration
			true,// Discordance
			false,// Dodging
			false,// Hand-to-Hand
			true,// Healing
			true,// Hiding
			true,// Lockpicking
			true,// Magery
			true,// MagicResist
			true,// Meditation
			true,// Musicianship
			true,// Peacemaking
			true,// Poisoning
			true,// Provocation
			true,// RemoveTrap
			true,// Searching
			true,// Stealing
			true,// Stealth
			true// Tracking
		};

		public static void Initialize()
		{
			Mobile.SkillCheckLocationHandler = new SkillCheckLocationHandler( Mobile_SkillCheckLocation );
			Mobile.SkillCheckDirectLocationHandler = new SkillCheckDirectLocationHandler( Mobile_SkillCheckDirectLocation );

			Mobile.SkillCheckTargetHandler = new SkillCheckTargetHandler( Mobile_SkillCheckTarget );
			Mobile.SkillCheckDirectTargetHandler = new SkillCheckDirectTargetHandler( Mobile_SkillCheckDirectTarget );
		}

		public static string TradeName( Trades trade )
		{
			if ( trade == Trades.Alchemy ){ return "Alchemy"; }
			else if ( trade == Trades.Blacksmith ){ return "Blacksmithing"; }
			else if ( trade == Trades.Carpentry ){ return "Carpentry"; }
			else if ( trade == Trades.Cartography ){ return "Cartography"; }
			else if ( trade == Trades.Cooking ){ return "Cooking"; }
			else if ( trade == Trades.Fishing ){ return "Fishing"; }
			else if ( trade == Trades.Fletching ){ return "Fletching"; }
			else if ( trade == Trades.Inscribe ){ return "Inscription"; }
			else if ( trade == Trades.Lumberjacking ){ return "Lumberjacking"; }
			else if ( trade == Trades.Mining ){ return "Mining"; }
			else if ( trade == Trades.Tailoring ){ return "Tailoring"; }
			else if ( trade == Trades.Tinkering ){ return "Tinkering"; }
			else if ( trade == Trades.Magery ){ return "Magery"; }
			else if ( trade == Trades.Musicianship ){ return "Musicianship"; }

			return "";
		}

		public static double TradeSkill( Mobile m, Trades trade, bool raw )
		{
			double value = 10.0;

			if ( m is PlayerMobile )
			{
				PlayerMobile pm = (PlayerMobile)m;

				if ( trade == Trades.Alchemy ){ 			value += (m.Int*0.75)+(m.Dex*0.25);		if ( !raw && pm.NpcGuild == NpcGuild.AlchemistsGuild ){ value += 20; } }
				else if ( trade == Trades.Blacksmith ){ 	value += (m.Str*0.85)+(m.Dex*0.15);		if ( !raw && pm.NpcGuild == NpcGuild.BlacksmithsGuild ){ value += 20; } }
				else if ( trade == Trades.Carpentry ){ 		value += (m.Dex*0.6)+(m.Str*0.4);		if ( !raw && pm.NpcGuild == NpcGuild.CarpentryGuild ){ value += 20; } }
				else if ( trade == Trades.Cartography ){ 	value += (m.Int*0.7)+(m.Dex*0.3);		if ( !raw && pm.NpcGuild == NpcGuild.LibrariansGuild ){ value += 20; } }
				else if ( trade == Trades.Cooking ){ 		value += (m.Int*0.75)+(m.Dex*0.25);		if ( !raw ){ value += 10; } }
				else if ( trade == Trades.Fishing ){ 		value += (m.Str*0.6)+(m.Dex*0.4);		if ( !raw && pm.NpcGuild == NpcGuild.MarinersGuild ){ value += 20; } }
				else if ( trade == Trades.Fletching ){ 		value += (m.Dex*0.7)+(m.Str*0.3);		if ( !raw && pm.NpcGuild == NpcGuild.RangersGuild ){ value += 20; } }
				else if ( trade == Trades.Inscribe ){ 		value += (m.Int*0.85)+(m.Dex*0.15);		if ( !raw && pm.NpcGuild == NpcGuild.LibrariansGuild ){ value += 20; } }
				else if ( trade == Trades.Lumberjacking ){ 	value += (m.Str*0.8)+(m.Dex*0.2);		if ( !raw && pm.NpcGuild == NpcGuild.CarpentryGuild ){ value += 20; } }
				else if ( trade == Trades.Mining ){ 		value += (double)(m.Str);				if ( !raw && pm.NpcGuild == NpcGuild.BlacksmithsGuild ){ value += 20; } }
				else if ( trade == Trades.Tailoring ){ 		value += (m.Dex*0.65)+(m.Int*0.35);		if ( !raw && pm.NpcGuild == NpcGuild.TailorsGuild ){ value += 20; } }
				else if ( trade == Trades.Tinkering ){		value += (m.Dex*0.5)+(m.Int*0.5);		if ( !raw && pm.NpcGuild == NpcGuild.TinkersGuild ){ value += 20; } }
			}

			if ( value > 100 )
				value = 100;

			return value;
		}

		public static string TradeStat( Trades trade )
		{
			string stat = "STR";

			if ( trade == Trades.Alchemy ){ 			if ( Utility.RandomMinMax(1,100) <= 75 ){ stat = "INT"; } else { stat = "DEX"; } }
			else if ( trade == Trades.Blacksmith ){ 	if ( Utility.RandomMinMax(1,100) <= 85 ){ stat = "STR"; } else { stat = "DEX"; } }
			else if ( trade == Trades.Carpentry ){ 		if ( Utility.RandomMinMax(1,100) <= 60 ){ stat = "DEX"; } else { stat = "STR"; } }
			else if ( trade == Trades.Cartography ){ 	if ( Utility.RandomMinMax(1,100) <= 70 ){ stat = "INT"; } else { stat = "DEX"; } }
			else if ( trade == Trades.Cooking ){ 		if ( Utility.RandomMinMax(1,100) <= 75 ){ stat = "INT"; } else { stat = "DEX"; } }
			else if ( trade == Trades.Fishing ){ 		if ( Utility.RandomMinMax(1,100) <= 60 ){ stat = "STR"; } else { stat = "DEX"; } }
			else if ( trade == Trades.Fletching ){ 		if ( Utility.RandomMinMax(1,100) <= 70 ){ stat = "DEX"; } else { stat = "STR"; } }
			else if ( trade == Trades.Inscribe ){ 		if ( Utility.RandomMinMax(1,100) <= 85 ){ stat = "INT"; } else { stat = "DEX"; } }
			else if ( trade == Trades.Lumberjacking ){  if ( Utility.RandomMinMax(1,100) <= 80 ){ stat = "STR"; } else { stat = "DEX"; } }
			else if ( trade == Trades.Mining ){ 		stat = "STR";																	 }
			else if ( trade == Trades.Tailoring ){ 		if ( Utility.RandomMinMax(1,100) <= 65 ){ stat = "DEX"; } else { stat = "INT"; } }
			else if ( trade == Trades.Tinkering ){		if ( Utility.RandomMinMax(1,100) <= 50 ){ stat = "DEX"; } else { stat = "INT"; } }

			return stat;
		}

		public static double GetRandomNumber( double minimum, double maximum )
		{ 
			Random random = new Random();
			return random.NextDouble() * (maximum - minimum) + minimum;
		}

		public static bool TestTrade( Mobile m, Trades trade, double min, double max )
		{
			if ( trade == Trades.Magery )
			{
				if ( m.Skills[SkillName.Magery].Value >= GetRandomNumber( min, max ) )
					return true;
				else
					return false;
			}
			else if ( trade == Trades.Musicianship )
			{
				if ( m.Skills[SkillName.Musicianship].Value >= GetRandomNumber( min, max ) )
					return true;
				else
					return false;
			}
			else if ( TradeSkill( m, trade, false ) >= GetRandomNumber( min, max ) )
			{
				double gc = (double)(m.Skills.Cap - m.Skills.Total) / m.Skills.Cap;
				gc += ( 100 - TradeSkill( m, trade, true ) ) / 100;
				gc /= 2;

				gc += 0.5;
				gc /= 2;

				gc *= 1.0;

				if ( gc < 0.01 )
					gc = 0.01;

				if ( gc >= Utility.RandomDouble() )
					TradeStatGain( m, TradeStat( trade ) );

				return true;
			}

			return false;
		}

		public static void TradeStatGain( Mobile from, string stat )
		{
			if ( stat == "STR" && from.StrLock == StatLockType.Up && (0.5 / 33.3) > Utility.RandomDouble() )
				GainStat( from, Stat.Str );
			else if ( stat == "DEX" && from.DexLock == StatLockType.Up && (0.5 / 33.3) > Utility.RandomDouble() )
				GainStat( from, Stat.Dex );
			else if ( stat == "INT" && from.IntLock == StatLockType.Up && (0.5 / 33.3) > Utility.RandomDouble() )
				GainStat( from, Stat.Int );
		}

		public static bool IsGuildSkill( Mobile from, SkillName skillName )
		{
			if ( from is PlayerMobile )
			{
				PlayerMobile pm = (PlayerMobile)from;

				if ( pm.NpcGuild == NpcGuild.MagesGuild )
				{
					if ( skillName == SkillName.Concentration ){ return true; }
					else if ( skillName == SkillName.Magery ){ return true; }
					else if ( skillName == SkillName.Meditation ){ return true; }
				}
				else if ( pm.NpcGuild == NpcGuild.WarriorsGuild )
				{
					if ( skillName == SkillName.Fencing ){ return true; }
					else if ( skillName == SkillName.Bludgeoning ){ return true; }
					else if ( skillName == SkillName.Parry ){ return true; }
					else if ( skillName == SkillName.Swords ){ return true; }
					else if ( skillName == SkillName.Tactics ){ return true; }
				}
				else if ( pm.NpcGuild == NpcGuild.ThievesGuild )
				{
					if ( skillName == SkillName.Hiding ){ return true; }
					else if ( skillName == SkillName.Lockpicking ){ return true; }
					else if ( skillName == SkillName.Stealing ){ return true; }
					else if ( skillName == SkillName.Stealth ){ return true; }
				}
				else if ( pm.NpcGuild == NpcGuild.RangersGuild )
				{
					if ( skillName == SkillName.Archery ){ return true; }
					else if ( skillName == SkillName.Tracking ){ return true; }
					else if ( skillName == SkillName.Tactics ){ return true; }
				}
				else if ( pm.NpcGuild == NpcGuild.HealersGuild )
				{
					if ( skillName == SkillName.Healing ){ return true; }
				}
				else if ( pm.NpcGuild == NpcGuild.BardsGuild )
				{
					if ( skillName == SkillName.Discordance ){ return true; }
					else if ( skillName == SkillName.Musicianship ){ return true; }
					else if ( skillName == SkillName.Peacemaking ){ return true; }
					else if ( skillName == SkillName.Provocation ){ return true; }
				}
				else if ( pm.NpcGuild == NpcGuild.AssassinsGuild )
				{
					if ( skillName == SkillName.Fencing ){ return true; }
					else if ( skillName == SkillName.Hiding ){ return true; }
					else if ( skillName == SkillName.Poisoning ){ return true; }
					else if ( skillName == SkillName.Stealth ){ return true; }
				}
			}
			return false;
		}

		public static bool IsHealSkill( Mobile from, SkillName skillName )
		{
			if ( from is PlayerMobile )
			{
				PlayerMobile pm = (PlayerMobile)from;

				if ( pm.NpcGuild == NpcGuild.HealersGuild )
					if ( skillName == SkillName.Healing ){ return true; }
			}
			return false;
		}

		public static bool Mobile_SkillCheckLocation( Mobile from, SkillName skillName, double minSkill, double maxSkill )
		{
			Skill skill = from.Skills[skillName];

			if ( skill == null )
				return false;

			double value = skill.Value;

			if ( value < minSkill )
				return false; // Too difficult
			else if ( value >= maxSkill )
				return true; // No challenge

			double chance = (value - minSkill) / (maxSkill - minSkill);

			Point2D loc = new Point2D( from.Location.X / LocationSize, from.Location.Y / LocationSize );
			return CheckSkill( from, skill, loc, chance );
		}

		public static bool Mobile_SkillCheckDirectLocation( Mobile from, SkillName skillName, double chance )
		{
			Skill skill = from.Skills[skillName];

			if ( skill == null )
				return false;

			if ( chance < 0.0 )
				return false; // Too difficult
			else if ( chance >= 1.0 )
				return true; // No challenge

			Point2D loc = new Point2D( from.Location.X / LocationSize, from.Location.Y / LocationSize );
			return CheckSkill( from, skill, loc, chance );
		}

		public static bool CheckSkill( Mobile from, Skill skill, object amObj, double chance )
		{
			SkillName skillName = skill.SkillName;

			if ( from.Skills.Cap == 0 )
				return false;

			double gainer = 2.0;

			if ( from is PlayerMobile )
			{
				if ( IsGuildSkill( from, skillName ) )
				{
					switch( Utility.RandomMinMax( 0, 5 ) )
					{
						case 0: gainer = 1.5; break;
						case 1: gainer = 1.4; break;
						case 2: gainer = 1.3; break;
						case 3: gainer = 1.2; break;
						case 4: gainer = 1.1; break;
						case 5: gainer = 1.0; break;
					}
				}
			}

			if ( IsHealSkill( from, skillName ) )
				gainer = 1.0;

			bool success = ( chance >= Utility.RandomDouble() );
			double gc = (double)(from.Skills.Cap - from.Skills.Total) / from.Skills.Cap;
			gc += ( skill.Cap - skill.Base ) / skill.Cap;
			gc /= gainer;

			gc += ( 1.0 - chance ) * ( success ? 0.5 : 0.2 );
			gc /= gainer;

			gc *= skill.Info.GainFactor;

			if ( gc < 0.01 )
				gc = 0.01;

			if ( from is BaseCreature && ((BaseCreature)from).Controlled )
				gc *= 2;

			if ( from.Alive && ( ( gc >= Utility.RandomDouble() && AllowGain( from, skill, amObj ) ) || skill.Base < 10.0 ) )
				Gain( from, skill );

			return success;
		}

		public static bool Mobile_SkillCheckTarget( Mobile from, SkillName skillName, object target, double minSkill, double maxSkill )
		{
			Skill skill = from.Skills[skillName];

			if ( skill == null )
				return false;

			double value = skill.Value;

			if ( value < minSkill )
				return false; // Too difficult
			else if ( value >= maxSkill )
				return true; // No challenge

			double chance = (value - minSkill) / (maxSkill - minSkill);

			return CheckSkill( from, skill, target, chance );
		}

		public static bool Mobile_SkillCheckDirectTarget( Mobile from, SkillName skillName, object target, double chance )
		{
			Skill skill = from.Skills[skillName];

			if ( skill == null )
				return false;

			if ( chance < 0.0 )
				return false; // Too difficult
			else if ( chance >= 1.0 )
				return true; // No challenge

			return CheckSkill( from, skill, target, chance );
		}

		private static bool AllowGain( Mobile from, Skill skill, object obj )
		{
			if ( AntiMacroCode && from is PlayerMobile && UseAntiMacro[skill.Info.SkillID] )
				return ((PlayerMobile)from).AntiMacroCheck( skill, obj );
			else
				return true;
		}

		public enum Stat { Str, Dex, Int }

		public static void Gain( Mobile from, Skill skill )
		{
			if ( from is BaseCreature && ((BaseCreature)from).IsDeadPet )
				return;

			if ( skill.Base < skill.Cap && skill.Lock == SkillLock.Up )
			{
				int toGain = 1;

				if ( skill.Base <= 10.0 )
					toGain = Utility.Random( 4 ) + 1;

				Skills skills = from.Skills;

				if ( from.Player && ( skills.Total / skills.Cap ) >= Utility.RandomDouble() )//( skills.Total >= skills.Cap )
				{
					for ( int i = 0; i < skills.Length; ++i )
					{
						Skill toLower = skills[i];

						if ( toLower != skill && toLower.Lock == SkillLock.Down && toLower.BaseFixedPoint >= toGain )
						{
							toLower.BaseFixedPoint -= toGain;
							break;
						}
					}
				}

				if ( !from.Player || (skills.Total + toGain) <= skills.Cap )
				{
					skill.BaseFixedPoint += toGain;
				}
			}

			if ( skill.Lock == SkillLock.Up )
			{
				SkillInfo info = skill.Info;

				if ( from.StrLock == StatLockType.Up && (info.StrGain / 33.3) > Utility.RandomDouble() )
					GainStat( from, Stat.Str );
				else if ( from.DexLock == StatLockType.Up && (info.DexGain / 33.3) > Utility.RandomDouble() )
					GainStat( from, Stat.Dex );
				else if ( from.IntLock == StatLockType.Up && (info.IntGain / 33.3) > Utility.RandomDouble() )
					GainStat( from, Stat.Int );
			}
		}

		public static bool CanLower( Mobile from, Stat stat )
		{
			switch ( stat )
			{
				case Stat.Str: return ( from.StrLock == StatLockType.Down && from.RawStr > 10 );
				case Stat.Dex: return ( from.DexLock == StatLockType.Down && from.RawDex > 10 );
				case Stat.Int: return ( from.IntLock == StatLockType.Down && from.RawInt > 10 );
			}

			return false;
		}

		public static bool CanRaise( Mobile from, Stat stat )
		{
			if ( !(from is BaseCreature && ((BaseCreature)from).Controlled) )
			{
				if ( from.RawStatTotal >= from.StatCap )
					return false;
			}

			switch ( stat )
			{
				case Stat.Str: return ( from.StrLock == StatLockType.Up && from.RawStr < 125 );
				case Stat.Dex: return ( from.DexLock == StatLockType.Up && from.RawDex < 125 );
				case Stat.Int: return ( from.IntLock == StatLockType.Up && from.RawInt < 125 );
			}

			return false;
		}

		public static void IncreaseStat( Mobile from, Stat stat, bool atrophy )
		{
			atrophy = atrophy || (from.RawStatTotal >= from.StatCap);

			switch ( stat )
			{
				case Stat.Str:
				{
					if ( atrophy )
					{
						if ( CanLower( from, Stat.Dex ) && (from.RawDex < from.RawInt || !CanLower( from, Stat.Int )) )
							--from.RawDex;
						else if ( CanLower( from, Stat.Int ) )
							--from.RawInt;
					}

					if ( CanRaise( from, Stat.Str ) )
						++from.RawStr;

					break;
				}
				case Stat.Dex:
				{
					if ( atrophy )
					{
						if ( CanLower( from, Stat.Str ) && (from.RawStr < from.RawInt || !CanLower( from, Stat.Int )) )
							--from.RawStr;
						else if ( CanLower( from, Stat.Int ) )
							--from.RawInt;
					}

					if ( CanRaise( from, Stat.Dex ) )
						++from.RawDex;

					break;
				}
				case Stat.Int:
				{
					if ( atrophy )
					{
						if ( CanLower( from, Stat.Str ) && (from.RawStr < from.RawDex || !CanLower( from, Stat.Dex )) )
							--from.RawStr;
						else if ( CanLower( from, Stat.Dex ) )
							--from.RawDex;
					}

					if ( CanRaise( from, Stat.Int ) )
						++from.RawInt;

					break;
				}
			}
		}

		private static TimeSpan m_StatGainDelay = TimeSpan.FromMinutes( 15.0 );
		private static TimeSpan m_PetStatGainDelay = TimeSpan.FromMinutes( 5.0 );

		public static void GainStat( Mobile from, Stat stat )
		{
			switch( stat )
			{
				case Stat.Str:
				{
					if ( from is BaseCreature && ((BaseCreature)from).Controlled ) {
						if ( (from.LastStrGain + m_PetStatGainDelay) >= DateTime.Now )
							return;
					}
					else if( (from.LastStrGain + m_StatGainDelay) >= DateTime.Now )
						return;

					from.LastStrGain = DateTime.Now;
					break;
				}
				case Stat.Dex:
				{
					if ( from is BaseCreature && ((BaseCreature)from).Controlled ) {
						if ( (from.LastDexGain + m_PetStatGainDelay) >= DateTime.Now )
							return;
					}
					else if( (from.LastDexGain + m_StatGainDelay) >= DateTime.Now )
						return;

					from.LastDexGain = DateTime.Now;
					break;
				}
				case Stat.Int:
				{
					if ( from is BaseCreature && ((BaseCreature)from).Controlled ) {
						if ( (from.LastIntGain + m_PetStatGainDelay) >= DateTime.Now )
							return;
					}

					else if( (from.LastIntGain + m_StatGainDelay) >= DateTime.Now )
						return;

					from.LastIntGain = DateTime.Now;
					break;
				}
			}

			bool atrophy = ( (from.RawStatTotal / (double)from.StatCap) >= Utility.RandomDouble() );

			IncreaseStat( from, stat, atrophy );
		}
	}
}