using System;
using Server;
using Server.Items;
using Server.Spells;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
using Server.Mobiles;

namespace Server.Misc
{
	public class RegenRates
	{
		[CallPriority(10)]
		public static void Configure()
		{
			Mobile.DefaultHitsRate = TimeSpan.FromSeconds(11.0);
			Mobile.DefaultStamRate = TimeSpan.FromSeconds(7.0);
			Mobile.DefaultManaRate = TimeSpan.FromSeconds(7.0);

			Mobile.ManaRegenRateHandler = new RegenRateHandler(Mobile_ManaRegenRate);

			if (Core.AOS)
			{
				Mobile.StamRegenRateHandler = new RegenRateHandler(Mobile_StamRegenRate);
				Mobile.HitsRegenRateHandler = new RegenRateHandler(Mobile_HitsRegenRate);
			}
		}

		private static void CheckBonusSkill(Mobile m, int cur, int max, SkillName skill)
		{
			if (!m.Alive)
				return;

			double n = (double)cur / max;
			double v = Math.Sqrt(m.Skills[skill].Value * 0.005);

			n *= (1.0 - v);
			n += v;

			m.CheckSkill(skill, n);
		}

		private static bool CheckTransform(Mobile m, Type type)
		{
			return TransformationSpell.UnderTransformation(m, type);
		}

		private static bool CheckAnimal(Mobile m, Type type)
		{
			return AnimalForm.UnderTransformation(m, type);
		}

		private static TimeSpan Mobile_HitsRegenRate(Mobile from)
		{
			if (from.Player && (from.Hunger < 1 || Ability.UnderColdBlood(from)) && from.AccessLevel == AccessLevel.Player && !((PlayerMobile)from).Young)
				return Mobile.DefaultHitsRate;

			int points = AosAttributes.GetValue(from, AosAttribute.RegenHits);

			if (points > 60)
				points = 60;

			if (from.Map == Map.Underworld && points > 4)
				points = 4;

			if (from is BaseCreature && !((BaseCreature)from).IsAnimatedDead)
				points += 4;

			if ((from is BaseCreature && (((BaseCreature)from).IsParagon || ((BaseCreature)from).IsTeragon)) || from is Leviathan)
				points += 40;

			if (CheckTransform(from, typeof(HorrificBeastSpell)))
				points += 20;

			if (CheckAnimal(from, typeof(Dog)) || CheckAnimal(from, typeof(Cat)))
				points += from.Skills[SkillName.Ninjitsu].Fixed / 300;

			if(from.Map != Map.Underworld && from.Race == Race.Human)
				points += 2;

			if (points < 0)
				points = 0;

			return TimeSpan.FromSeconds(1.0 / (0.1 * (1 + points)));
		}

		private static TimeSpan Mobile_StamRegenRate(Mobile from)
		{
			if (from.Skills == null || (from.Player && (from.Hunger < 1 || Ability.UnderColdBlood(from)) && from.AccessLevel == AccessLevel.Player && !((PlayerMobile)from).Young))
				return Mobile.DefaultStamRate;

				// 			if ( from.Player && from.Hunger < 1 )
				// 				return TimeSpan.FromSeconds( 15.0 );

			CheckBonusSkill(from, from.Stam, from.StamMax, SkillName.Focus);

			int points = (from.Map == Map.Underworld) ? Math.Min(AosAttributes.GetValue(from, AosAttribute.RegenStam), 4) : Math.Min(AosAttributes.GetValue(from, AosAttribute.RegenStam), 30) +
				(int)(from.Skills[SkillName.Focus].Value * 0.1);

			if (CheckTransform(from, typeof(VampiricEmbraceSpell)))
				points += 15;

			if (CheckAnimal(from, typeof(Kirin)))
				points += 20;

			if ((from is BaseCreature && (((BaseCreature)from).IsParagon || ((BaseCreature)from).IsTeragon)) || from is Leviathan)
				points += 40;

			if (points < -1)
				points = -1;

			return TimeSpan.FromSeconds(1.0 / (0.1 * (2 + points)));
		}

		private static TimeSpan Mobile_ManaRegenRate(Mobile from)
		{
			if (from.Skills == null || (Ability.UnderColdBlood(from) && !(from.FindItemOnLayer(Layer.Bracelet) is LevelOrnamentOfTheMagician)))
				return Mobile.DefaultManaRate;

			if (!from.Meditating)
				CheckBonusSkill(from, from.Mana, from.ManaMax, SkillName.Meditation);

			double rate;
			double armorPenalty = GetArmorOffset(from);

			if (from.Map != Map.Underworld)
			{
				double medPoints = from.Int + (from.Skills[SkillName.Meditation].Value * 3);

				medPoints *= (from.Skills[SkillName.Meditation].Value < 100.0) ? 0.025 : 0.0275;

				CheckBonusSkill(from, from.Mana, from.ManaMax, SkillName.Focus);

				double focusPoints = (int)(from.Skills[SkillName.Focus].Value * 0.05);

				if (armorPenalty > 0)
					medPoints = 0; // In AOS, wearing any meditation-blocking armor completely removes meditation bonus

				double totalPoints = Math.Min(AosAttributes.GetValue(from, AosAttribute.RegenMana), 60) +
					focusPoints + medPoints + (from.Meditating ? (medPoints > 13.0 ? 13.0 : medPoints) : 0.0);

				if (CheckTransform(from, typeof(VampiricEmbraceSpell)))
					totalPoints += 3;
				else if (CheckTransform(from, typeof(LichFormSpell)))
					totalPoints += 13;

				if ((from is BaseCreature && (((BaseCreature)from).IsParagon || ((BaseCreature)from).IsTeragon)) || from is Leviathan)
					totalPoints += 40;

				if (totalPoints < -1)
					totalPoints = -1;

				rate = 1.0 / (0.1 * (2 + (int)totalPoints));
			}
			else
			{
				double medPoints = (from.Int + from.Skills[SkillName.Meditation].Value) * 0.5;

				if (medPoints <= 0)
					rate = 7.0;
				else if (medPoints <= 100)
					rate = 7.0 - (239*medPoints/2400) + (19*medPoints*medPoints/48000);
				else if (medPoints < 120)
					rate = 1.0;
				else
					rate = 0.75;
				
				rate -= (double) Math.Min(AosAttributes.GetValue(from, AosAttribute.RegenMana), 4) * 0.1;

				rate += armorPenalty;

				if (from.Meditating)
					rate *= 0.5;

				if (rate < 0.5)
					rate = 0.5;
				else if (rate > 7.0)
					rate = 7.0;
			}

			return TimeSpan.FromSeconds(rate);
		}

		public static double GetArmorOffset(Mobile from)
		{
			double rating = 0.0;

			if (from.Map == Map.Underworld)
				rating += GetArmorMeditationValue(from.ShieldArmor as BaseArmor);

			rating += GetArmorMeditationValue(from.NeckArmor as BaseArmor);
			rating += GetArmorMeditationValue(from.HandArmor as BaseArmor);
			rating += GetArmorMeditationValue(from.HeadArmor as BaseArmor);
			rating += GetArmorMeditationValue(from.ArmsArmor as BaseArmor);
			rating += GetArmorMeditationValue(from.LegsArmor as BaseArmor);
			rating += GetArmorMeditationValue(from.ChestArmor as BaseArmor);

			return rating / 4;
		}

		private static double GetArmorMeditationValue(BaseArmor ar)
		{
			if (ar == null || ar.ArmorAttributes.MageArmor != 0 || ar.Attributes.SpellChanneling != 0)
				return 0.0;

			switch (ar.MeditationAllowance)
			{
				default:
				case ArmorMeditationAllowance.None: return ar.BaseArmorRatingScaled;
				case ArmorMeditationAllowance.Half: return ar.BaseArmorRatingScaled / 2.0;
				case ArmorMeditationAllowance.All: return 0.0;
			}
		}
	}
}