using System;
using Server;
using Server.Targeting;
using Server.Mobiles;
using Server.Network;
using Server.Items;
using Server.Gumps;
using System.Collections;
using System.Collections.Generic;
using Server.ContextMenus;
using Server.Commands;
using Server.Factions;
using Server.Engines.Craft;
using System.Linq;

namespace Server.SkillHandlers
{
    public class Imbuing
    {
        public static void Initialize()
        {
            SkillInfo.Table[(int)SkillName.Imbuing].Callback = new SkillUseCallback(OnUse);

            CommandSystem.Register("GetTotalWeight", AccessLevel.GameMaster, new CommandEventHandler(GetTotalWeight_OnCommand));
            CommandSystem.Register("GetTotalMods", AccessLevel.GameMaster, new CommandEventHandler(GetTotalMods_OnCommand));
        }

        private static void OnLogin(LoginEventArgs e)
        {
            if (!e.Mobile.CanBeginAction(typeof(Imbuing)))
                e.Mobile.EndAction(typeof(Imbuing));
        }

        private static Dictionary<Mobile, ImbuingContext> m_ContextTable = new Dictionary<Mobile, ImbuingContext>();
        public static Dictionary<Mobile, ImbuingContext> ContextTable { get { return m_ContextTable; } }

        public static TimeSpan OnUse(Mobile from)
        {
            if (!from.Alive)
            {
                from.SendLocalizedMessage(500949); //You can't do that when you're dead.
            }
            else if (from is PlayerMobile)
            {
                from.CloseGump(typeof(ImbuingGump));
                BaseGump.SendGump(new ImbuingGump((PlayerMobile)from));
                from.BeginAction(typeof(Imbuing));
            }

            return TimeSpan.FromSeconds(1.0);
        }

        public static ImbuingContext GetContext(Mobile m)
        {
            if (!m_ContextTable.ContainsKey(m))
            {
                ImbuingContext context = new ImbuingContext(m);
                m_ContextTable[m] = context;
                return context;
            }

            return m_ContextTable[m];
        }

        public static void AddContext(Mobile from, ImbuingContext context)
        {
            m_ContextTable[from] = context;
        }

        public static bool CanImbueItem(Mobile from, Item item)
        {
            if (!CheckSoulForge(from, 2))
            {
                return false;
            }
            if (item == null || !item.IsChildOf(from.Backpack))
            {
                from.SendLocalizedMessage(1079575);  // The item must be in your backpack to imbue it.
            }
            else if (item.LootType == LootType.Blessed || item.LootType == LootType.Newbied)
            {
                from.SendLocalizedMessage(1080438);  // You cannot imbue a blessed item.
            }
            else if (item is BaseWeapon && Spells.Mysticism.EnchantSpell.IsUnderSpellEffects(from, (BaseWeapon)item))
            {
                from.SendLocalizedMessage(1080130);  // You cannot imbue an item that is currently enchanted.
            }
            else if (item is BaseWeapon && ((BaseWeapon)item).FocusWeilder != null)
            {
                from.SendLocalizedMessage(1080444);  //You cannot imbue an item that is under the effects of the ninjitsu focus attack ability.
            }
            else if (IsSpecialItem(item))
            {
                from.SendLocalizedMessage(1079576); // You cannot imbue this item.
            }
            else if (item is IFactionItem && ((IFactionItem)item).FactionItemState != null)
            {
                from.SendLocalizedMessage(1114312); // You cannot imbue faction items.
            }
            else if (item is BaseJewel && !(item is BaseRing) && !(item is BaseBracelet))
            {
                from.SendLocalizedMessage(1079576); // You cannot imbue this item.
            }
            else if (IsInNonImbueList(item.GetType()))
            {
                from.SendLocalizedMessage(1079576); // You cannot imbue this item.
            }
            else
            {
                return true;
            }

            return false;
        }

        public static bool OnBeforeImbue(Mobile from, Item item, int id, int value)
        {
            return OnBeforeImbue(from, item, id, value, GetTotalMods(item, id), GetMaxProps(item), GetTotalWeight(item, id, false, true), Imbuing.GetMaxWeight(item));
        }

        public static bool OnBeforeImbue(Mobile from, Item item, int id, int value, int totalprops, int maxprops, int totalitemweight, int maxweight)
        {
            if (totalprops >= maxprops || totalitemweight > maxweight)
            {
                from.SendLocalizedMessage(1079772); // You cannot imbue this item with any more item properties.
                from.CloseGump(typeof(ImbueGump));
                from.EndAction(typeof(Imbuing));
                return false;
            }

            return true;
        }

        public static bool CanUnravelItem(Mobile from, Item item, bool message = true)
        {
            if (!CheckSoulForge(from, 2, false, false))
            {
                from.SendLocalizedMessage(1080433); // You must be near a soulforge to magically unravel an item.
            }
            else if (!item.IsChildOf(from.Backpack))
            {
                if (message)
                    from.SendLocalizedMessage(1080424);  // The item must be in your backpack to magically unravel it.
            }
            else if (item.LootType == LootType.Blessed || item.LootType == LootType.Newbied)
            {
                if (message)
                    from.SendLocalizedMessage(1080421);  // You cannot unravel the magic of a blessed item.
            }
            else if (!(item is BaseWeapon || item is BaseArmor || item is BaseJewel || item is BaseHat))
            {
                if (message)
                    from.SendLocalizedMessage(1080425); // You cannot magically unravel this item.
            }
            else if (item is BaseWeapon && Spells.Mysticism.EnchantSpell.IsUnderSpellEffects(from, (BaseWeapon)item))
            {
                if (message)
                    from.SendLocalizedMessage(1080427);  // You cannot magically unravel an item that is currently enchanted.
            }
            else if (item is BaseWeapon && ((BaseWeapon)item).FocusWeilder != null)
            {
                if (message)
                    from.SendLocalizedMessage(1080445); //You cannot magically unravel an item that is under the effects of the ninjitsu focus attack ability.
            }
            else if (item is IFactionItem && ((IFactionItem)item).FactionItemState != null)
            {
                if (message)
                    from.SendLocalizedMessage(1112408); // You cannot magically unravel a faction reward item.
            }
            else
            {
                return true;
            }

            return false;
        }

        public static bool IsSpecialItem(Item item)
        {
            if (item == null)
                return true;

            if (IsSpecialImbuable(item))
                return false;

			if (item.IsArtifact)
				return true;

            if (RunicReforging.GetArtifactRarity(item) > 0)
                return true;

            if (NonCraftableImbuable(item))
                return false;

            foreach (CraftSystem system in CraftSystem.Systems)
            {
                CraftItem crItem = null;
                
                if(system != null && system.CraftItems != null)
                    crItem = system.CraftItems.SearchFor(item.GetType());

                if (crItem != null)
                    return false;
            }

            return true;
        }

        private static bool IsSpecialImbuable(Item item)
        {
            return IsSpecialImbuable(item.GetType());
        }

        private static bool IsSpecialImbuable(Type type)
        {
            if (_SpecialImbuable.Any(i => i == type))
                return true;

            if (type.IsSubclassOf(typeof(BaseGlovesOfMining)) || typeof(IFishingAttire).IsAssignableFrom(type))
                return true;

            return false;
        }

         private static Type[] _SpecialImbuable =
        {
            typeof(ClockworkLeggings), typeof(GargishClockworkLeggings), typeof(OrcishKinMask), typeof(SavageMask), typeof(VirtuososArmbands), 
            typeof(VirtuososCap), typeof(VirtuososCollar), typeof(VirtuososEarpieces), typeof(VirtuososKidGloves), typeof(VirtuososKilt), 
            typeof(VirtuososNecklace), typeof(VirtuososTunic), typeof(BestialArms), typeof(BestialEarrings), typeof(BestialGloves), typeof(BestialGorget),
            typeof(BestialHelm), typeof(BestialKilt), typeof(BestialLegs), typeof(BestialNecklace), typeof(BarbedWhip), typeof(BladedWhip),
			typeof(SpikedWhip), typeof(SkullGnarledStaff), typeof(GargishSkullGnarledStaff), typeof(SkullLongsword), typeof(GargishSkullLongsword)
        };

        private static Type[] _NonCraftables =
        {
            typeof(SilverRing), typeof(SilverBracelet)
        };

        public static bool NonCraftableImbuable(Item item)
        {
            if (item is BaseWand)
                return true;

            Type type = item.GetType();

            foreach (var t in _NonCraftables)
            {
                if (t == type)
                    return true;
            }

            return false;
        }

        public static double GetSuccessChance(Mobile from, Item item, int totalItemIntensity, int propintensity, double bonus)
        {
            double skill = from.Skills[SkillName.Imbuing].Value;
            double resultWeight = totalItemIntensity + propintensity;

            double e = Math.E;
            double a, b, c, w;
            double i = item is BaseJewel ? 0.9162 : 1.0;

            // - Racial Bonus - SA ONLY -
            if (from.Race == Race.Gargoyle)
            {
                a = 1362.281555;
                b = 66.32801518;
                c = 235.2223147;
                w = -1481.037561;
            }
            else
            {
                a = 1554.96118;
                b = 53.81743328;
                c = 230.0038452;
                w = -1664.857794;
            }

            // Royal City Bonus, Fluctuation - Removed as EA doesn't seem to fluctuate despite stratics posts
            /*if (bonus == 0.02)
            {
                if (totalItemIntensity < 295)
                {
                    bonus = (double)Utility.RandomMinMax(190, 210) / 100.0;
                }
            }*/

            return Math.Max(0, Math.Round(Math.Floor(20 * skill + 10 * a * Math.Pow(e, (b / (resultWeight + c))) + 10 * w - 2400) / 1000 * (i) + bonus, 3) * 100);
        }

        public static int GetQualityBonus(Item item)
        {
            IQuality quality = item as IQuality;

            if (quality != null)
            {
                if (quality.Quality == ItemQuality.Exceptional)
                    return 20;

                if (quality.PlayerConstructed)
                    return 10;
            }

            return 0;
        }

        /// <summary>
        /// Imbues Item with selected id
        /// </summary>
        /// <param name="from">Player Imbuing</param>
        /// <param name="i">Item to be imbued</param>
        /// <param name="id">id to be imbued, see m_Table</param>
        /// <param name="value">value for id</param>
        public static void TryImbueItem(Mobile from, Item i, int id, int value)
        {
            double bonus = 0.0;

            if (!CheckSoulForge(from, 2, out bonus))
                return;

            ImbuingContext context = Imbuing.GetContext(from);

            context.LastImbued = i;
            context.Imbue_Mod = id;
            context.Imbue_ModInt = value;

            var def = ItemPropertyInfo.GetInfo(id);

            if (def == null)
                return;

            var gem = def.GemRes;
            var primary = def.PrimaryRes;
            var special = def.SpecialRes;

            context.Imbue_ModVal = def.Weight;

            var gemAmount = GetGemAmount(i, id, value);
            var primResAmount = GetPrimaryAmount(i, id, value);
            var specResAmount = GetSpecialAmount(i, id, value);

            if (from.AccessLevel == AccessLevel.Player && 
                (from.Backpack == null || from.Backpack.GetAmount(gem) < gemAmount || 
                from.Backpack.GetAmount(primary) < primResAmount || 
                from.Backpack.GetAmount(special) < specResAmount))
                from.SendLocalizedMessage(1079773); //You do not have enough resources to imbue this item.     
            else
            {
                var maxWeight = GetMaxWeight(i);
                context.Imbue_IWmax = maxWeight;

                var trueWeight = GetTotalWeight(i, id, true, true);
                var imbuingWeight = GetTotalWeight(i, id, false, true);
                var totalItemMods = GetTotalMods(i, id);
                var maxint = ItemPropertyInfo.GetMaxIntensity(i, id, true);

                var propImbuingweight = (int)(((double)def.Weight / (double)maxint) * value);
                var propTrueWeight = (int)(((double)propImbuingweight / (double)def.Weight) * 100);

                if ((imbuingWeight + propImbuingweight) > maxWeight)
                {
                    from.SendLocalizedMessage(1079772); // You cannot imbue this item with any more item properties.
                    from.CloseGump(typeof(ImbueGump));
                    return;
                }

                double skill = from.Skills[SkillName.Imbuing].Value;
                double success = GetSuccessChance(from, i, trueWeight, propTrueWeight, bonus);

                if (TimesImbued(i) < 20 && skill < from.Skills[SkillName.Imbuing].Cap)
                {
                    double s = Math.Min(100, success);
                    double mins = 120 - (s * 1.2);
                    double maxs = Math.Max(120 / (s / 100), skill);

                    from.CheckSkill(SkillName.Imbuing, mins, maxs);
                }

                success /= 100;

                Effects.SendPacket(from, from.Map, new GraphicalEffect(EffectType.FixedFrom, from.Serial, Server.Serial.Zero, 0x375A, from.Location, from.Location, 1, 17, true, false));
                Effects.SendTargetParticles(from, 0, 1, 0, 0x1593, EffectLayer.Waist);

                if (success >= Utility.RandomDouble() || id < 0 || id > 180)
                {
                    if (from.AccessLevel == AccessLevel.Player)
                    {
                        from.Backpack.ConsumeTotal(gem, gemAmount);
                        from.Backpack.ConsumeTotal(primary, primResAmount);

                        if (specResAmount > 0)
                            from.Backpack.ConsumeTotal(special, specResAmount);
                    }


                    ImbueItem(from, i, id, value);
                } 
                else
                {
                    // This is consumed regardless of success/fail
                    if (from.AccessLevel == AccessLevel.Player)
                    {
                        from.Backpack.ConsumeTotal(primary, primResAmount);
                    }

                    from.SendLocalizedMessage(1079774); // You attempt to imbue the item, but fail.
                    from.PlaySound(0x1E4);
                }
            }

            from.EndAction(typeof(Imbuing));
        }

        public static void ImbueItem(Mobile from, Item item, int id, int value)
        {
            from.SendLocalizedMessage(1079775); // You successfully imbue the item!
            from.PlaySound(0x1EB);

            if (item is BaseWeapon)
            {
                var wep = (BaseWeapon)item;

                // New property replaces the old one, so lets set them all to 0
                if (id >= 30 && id <= 34)
                {
                    wep.WeaponAttributes.HitPhysicalArea = 0;
                    wep.WeaponAttributes.HitFireArea = 0;
                    wep.WeaponAttributes.HitColdArea = 0;
                    wep.WeaponAttributes.HitPoisonArea = 0;
                    wep.WeaponAttributes.HitEnergyArea = 0;
                }
                else if (id >= 35 && id <= 39)
                {
                    wep.WeaponAttributes.HitMagicArrow = 0;
                    wep.WeaponAttributes.HitHarm = 0;
                    wep.WeaponAttributes.HitFireball = 0;
                    wep.WeaponAttributes.HitLightning = 0;
                    wep.WeaponAttributes.HitDispel = 0;
                }
            }

            if (item is BaseJewel && id >= 151 && id <= 183)
            {
                var jewel = (BaseJewel)item;
                var group = GetSkillGroup((SkillName)ItemPropertyInfo.GetAttribute(id));

                //Removes skill bonus if that group already exists on the item
                for (int j = 0; j < 5; j++)
                {
                    if (jewel.SkillBonuses.GetBonus(j) > 0 && group.Any(sk => sk == jewel.SkillBonuses.GetSkill(j)))
                    {
                        jewel.SkillBonuses.SetBonus(j, 0.0);
                        jewel.SkillBonuses.SetSkill(j, SkillName.Alchemy);
                    }
                }
            }

            SetProperty(item, id, value);

            // Sets DImodded, which is used in BaseWeapon
            if (item is BaseWeapon && id == 12 && !((BaseWeapon)item).DImodded)
            {
                ((BaseWeapon)item).DImodded = true;
            }

            // removes nom-imbued Imbuing value, which changes the way the items total weight is calculated
            if (id >= 51 && id <= 55)
            {
                if (item is BaseArmor)
                {
                    var arm = (BaseArmor)item;

                    switch (id)
                    {
                        case 51: arm.PhysNonImbuing = 0; break;
                        case 52: arm.FireNonImbuing = 0; break;
                        case 53: arm.ColdNonImbuing = 0; break;
                        case 54: arm.PoisonNonImbuing = 0; break;
                        case 55: arm.EnergyNonImbuing = 0; break;
                    }
                }
                else if (item is BaseClothing)
                {
                    var hat = (BaseClothing)item;

                    switch (id)
                    {
                        case 51: hat.PhysNonImbuing = 0; break;
                        case 52: hat.FireNonImbuing = 0; break;
                        case 53: hat.ColdNonImbuing = 0; break;
                        case 54: hat.PoisonNonImbuing = 0; break;
                        case 55: hat.EnergyNonImbuing = 0; break;
                    }
                }
            }

            if (item is IImbuableEquipement)
            {
                var imbuable = (IImbuableEquipement)item;

                imbuable.OnAfterImbued(from, id, value);
                imbuable.TimesImbued++;
            }

            // jewels get hits set to 255
            if (item is BaseJewel && ((BaseJewel)item).MaxHitPoints <= 0)
            {
                ((BaseJewel)item).MaxHitPoints = 255;
                ((BaseJewel)item).HitPoints = 255;
            }

            // Removes self repair
            var armorAttrs = RunicReforging.GetAosArmorAttributes(item);

            if (armorAttrs != null)
            {
                armorAttrs.SelfRepair = 0;
            }
            else
            {
                var wepAttrs = RunicReforging.GetAosWeaponAttributes(item);

                if (wepAttrs != null)
                {
                    wepAttrs.SelfRepair = 0;
                }
            }
        }

        public static void SetProperty(Item item, int id, int value)
        {
            object prop = ItemPropertyInfo.GetAttribute(id);

            if (item is BaseWeapon)
            {
                BaseWeapon wep = item as BaseWeapon;

                if (prop is AosAttribute)
                {
                    AosAttribute attr = (AosAttribute)prop;

                    if (attr == AosAttribute.SpellChanneling)
                    {
                        wep.Attributes.SpellChanneling = value;

                        if (value > 0 && wep.Attributes.CastSpeed >= 0)
                            wep.Attributes.CastSpeed -= 1;
                    }
                    else if (attr == AosAttribute.CastSpeed)
                    {
                        wep.Attributes.CastSpeed += value;
                    }
                    else
                    {
                        wep.Attributes[attr] = value;
                    }
                }
                else if (prop is AosWeaponAttribute)
                {
                    wep.WeaponAttributes[(AosWeaponAttribute)prop] = value;
                }

                else if (prop is SlayerName)
                {
                    wep.Slayer = (SlayerName)prop;
                }
                else if (prop is SAAbsorptionAttribute)
                {
                    wep.AbsorptionAttributes[(SAAbsorptionAttribute)prop] = value;
                }
                else if (prop is AosElementAttribute)
                {
                    int fire, phys, cold, nrgy, pois, chaos, direct;

                    wep.GetDamageTypes(null, out phys, out fire, out cold, out pois, out nrgy, out chaos, out direct);

                    value = Math.Min(phys, value);

                    wep.AosElementDamages[(AosElementAttribute)prop] = value;
                    wep.Hue = wep.GetElementalDamageHue();
                }
                else if (prop is string && wep is BaseRanged && (string)prop == "WeaponVelocity")
                {
                    ((BaseRanged)wep).Velocity = value;
                }
            }
            else if (item is BaseShield)
            {
                BaseShield shield = item as BaseShield;

                if (prop is AosWeaponAttribute && (AosWeaponAttribute)prop == AosWeaponAttribute.DurabilityBonus)
                {
                    prop = AosArmorAttribute.DurabilityBonus;
                }

                if (prop is AosAttribute)
                {
                    AosAttribute attr = (AosAttribute)prop;

                    if (attr == AosAttribute.SpellChanneling)
                    {
                        shield.Attributes.SpellChanneling = value;

                        if (value > 0 && shield.Attributes.CastSpeed >= 0)
                            shield.Attributes.CastSpeed -= 1;
                    }
                    else if (attr == AosAttribute.CastSpeed)
                    {
                        shield.Attributes.CastSpeed += value;
                    }
                    else
                    {
                        shield.Attributes[attr] = value;
                    }
                }
                else if (prop is AosElementAttribute)
                {
                    AosElementAttribute attr = (AosElementAttribute)prop;

                    switch (attr)
                    {
                        case AosElementAttribute.Physical: shield.PhysicalBonus = value; break;
                        case AosElementAttribute.Fire: shield.FireBonus = value; break;
                        case AosElementAttribute.Cold: shield.ColdBonus = value; break;
                        case AosElementAttribute.Poison: shield.PoisonBonus = value; break;
                        case AosElementAttribute.Energy: shield.EnergyBonus = value; break;
                    }
                }
                else if (prop is SAAbsorptionAttribute)
                {
                    shield.AbsorptionAttributes[(SAAbsorptionAttribute)prop] = value;
                }
                else if (prop is AosArmorAttribute)
                {
                    shield.ArmorAttributes[(AosArmorAttribute)prop] = value;
                }
            }
            else if (item is BaseArmor)
            {
                BaseArmor arm = item as BaseArmor;

                if (prop is AosWeaponAttribute && (AosWeaponAttribute)prop == AosWeaponAttribute.DurabilityBonus)
                {
                    prop = AosArmorAttribute.DurabilityBonus;
                }

                if (prop is AosAttribute)
                {
                    arm.Attributes[(AosAttribute)prop] = value;
                }
                else if (prop is AosElementAttribute)
                {
                    AosElementAttribute attr = (AosElementAttribute)prop;

                    switch (attr)
                    {
                        case AosElementAttribute.Physical: arm.PhysicalBonus = value; break;
                        case AosElementAttribute.Fire: arm.FireBonus = value; break;
                        case AosElementAttribute.Cold: arm.ColdBonus = value; break;
                        case AosElementAttribute.Poison: arm.PoisonBonus = value; break;
                        case AosElementAttribute.Energy: arm.EnergyBonus = value; break;
                    }
                }
                else if (prop is SAAbsorptionAttribute)
                {
                    arm.AbsorptionAttributes[(SAAbsorptionAttribute)prop] = value;
                }
                else if (prop is AosArmorAttribute)
                {
                    arm.ArmorAttributes[(AosArmorAttribute)prop] = value;
                }
            }
            else if (item is BaseClothing)
            {
                BaseClothing clothing = item as BaseClothing;

                if (prop is AosAttribute)
                {
                    clothing.Attributes[(AosAttribute)prop] = value;
                }
                else if (prop is SAAbsorptionAttribute)
                {
                    clothing.SAAbsorptionAttributes[(SAAbsorptionAttribute)prop] = value;
                }
                else if (prop is AosElementAttribute)
                {
                    AosElementAttribute attr = (AosElementAttribute)prop;

                    switch (attr)
                    {
                        case AosElementAttribute.Physical: clothing.Resistances.Physical = value; break;
                        case AosElementAttribute.Fire: clothing.Resistances.Fire = value; break;
                        case AosElementAttribute.Cold: clothing.Resistances.Cold = value; break;
                        case AosElementAttribute.Poison: clothing.Resistances.Poison = value; break;
                        case AosElementAttribute.Energy: clothing.Resistances.Energy = value; break;
                    }
                }
            }
            else if (item is BaseJewel)
            {
                BaseJewel jewel = item as BaseJewel;

                if (prop is AosAttribute)
                {
                    jewel.Attributes[(AosAttribute)prop] = value;
                }
                else if (prop is SAAbsorptionAttribute)
                {
                    jewel.AbsorptionAttributes[(SAAbsorptionAttribute)prop] = value;
                }
                else if (prop is AosElementAttribute)
                {
                    AosElementAttribute attr = (AosElementAttribute)prop;

                    switch (attr)
                    {
                        case AosElementAttribute.Physical: jewel.Resistances.Physical = value; break;
                        case AosElementAttribute.Fire: jewel.Resistances.Fire = value; break;
                        case AosElementAttribute.Cold: jewel.Resistances.Cold = value; break;
                        case AosElementAttribute.Poison: jewel.Resistances.Poison = value; break;
                        case AosElementAttribute.Energy: jewel.Resistances.Energy = value; break;
                    }
                }
                else if (prop is SkillName)
                {
                    SkillName skill = (SkillName)prop;
                    var bonuses = jewel.SkillBonuses;

                    var index = GetAvailableSkillIndex(bonuses);

                    if (index >= 0 && index <= 4)
                    {
                        bonuses.SetValues(index, skill, value);
                    }
                }
            }

            item.InvalidateProperties();
        }

	    public static bool UnravelItem(Mobile from, Item item, bool message = true)
	    {
            double bonus = 0.0;

            if (!CheckSoulForge(from, 2, out bonus))
                return false;

		    int weight = GetTotalWeight(item, -1, false, true);
			
		    if (weight <= 0)
			{
				if (message)
				{
					// You cannot magically unravel this item. It appears to possess little or no magic.
					from.SendLocalizedMessage(1080437);
				}

				return false;
		    }

		    ImbuingContext context = GetContext(from);
			
		    Type resType = null;
		    var resAmount = Math.Max(1, weight / 100);

			var success = false;

		    if (weight >= 480 - bonus)
			{
				if (from.Skills[SkillName.Imbuing].Value < 95.0)
				{
					if (message)
					{
						// Your Imbuing skill is not high enough to magically unravel this item.
						from.SendLocalizedMessage(1080434);
					}

					return false;
				}
				
				if (from.CheckSkill(SkillName.Imbuing, 90.1, 120.0))
				{
					success = true;
					resType = typeof(RelicFragment);
					resAmount = 1;
				}
				else if (from.CheckSkill(SkillName.Imbuing, 45.0, 95.0))
				{
					success = true;
					resType = typeof(EnchantedEssence);
					resAmount = Math.Max(1, resAmount - Utility.Random(3));
				}
			}
		    else if (weight > 200 - bonus && weight < 480 - bonus)
			{
				if (from.Skills[SkillName.Imbuing].Value < 45.0)
				{
					if (message)
					{
						// Your Imbuing skill is not high enough to magically unravel this item.
						from.SendLocalizedMessage(1080434);
					}

					return false;
				}
				
				if (from.CheckSkill(SkillName.Imbuing, 45.0, 95.0))
				{
					success = true;
					resType = typeof(EnchantedEssence);
					resAmount = Math.Max(1, resAmount);
				}
				else if (from.CheckSkill(SkillName.Imbuing, 0.0, 45.0))
				{
					success = true;
					resType = typeof(MagicalResidue);
					resAmount = Math.Max(1, resAmount + Utility.Random(2));
				}
			}
			else if (weight <= 200 - bonus)
			{
				if (from.CheckSkill(SkillName.Imbuing, 0.0, 45.0))
				{
					success = true;
					resType = typeof(MagicalResidue);
					resAmount = Math.Max(1, resAmount + Utility.Random(2));
				}
			}
			else
			{
				if (message)
				{
					// You cannot magically unravel this item. It appears to possess little or no magic.
					from.SendLocalizedMessage(1080437);
				}

				return false;
			}

		    if (!success)
		    {
			    return false;
		    }

		    Item res;

		    while (resAmount > 0)
		    {
			    res = Activator.CreateInstance(resType) as Item;

			    if (res == null)
			    {
				    break;
			    }

			    if (res.Stackable)
			    {
				    res.Amount = Math.Max(1, Math.Min(60000, resAmount));
			    }

			    resAmount -= res.Amount;

			    from.AddToBackpack(res);
		    }

		    item.Delete();

		    return true;
	    }

        public static int GetMaxWeight(object item)
        {
            int maxWeight = 450;

            IQuality quality = item as IQuality;

            if (quality != null && quality.Quality == ItemQuality.Exceptional)
                maxWeight += 50;

            if (item is BaseWeapon)
            {
                BaseWeapon itemToImbue = item as BaseWeapon;

                if (itemToImbue is BaseThrown)
                    maxWeight += 0;
                else if (itemToImbue is BaseRanged)
                    maxWeight += 50;
                else if (itemToImbue.Layer == Layer.TwoHanded)
                    maxWeight += 100;
            }
            else if (item is BaseJewel)
            {
                maxWeight = 500;
            }

            return maxWeight;
        }

        public static int GetMaxProps(Item item)
        {
            return 5;
        }

        public static int GetGemAmount(Item item, int id, int value)
        {
            int max = ItemPropertyInfo.GetMaxIntensity(item, id, true);
            int inc = ItemPropertyInfo.GetScale(item, id);

            if (max == 1 && inc == 0)
                return 10;

            double v = Math.Floor(value / ((double)max / 10));

            if (v > 10) v = 10;
            if (v < 1) v = 1;

            return (int)v;
        }

        public static int GetPrimaryAmount(Item item, int id, int value)
        {
            int max = ItemPropertyInfo.GetMaxIntensity(item, id, true);
            int inc = ItemPropertyInfo.GetScale(item, id);

            //if (item is BaseJewel && id == 12)
            //    max /= 2;

            if (max == 1 && inc == 0)
                return 5;

            double v = Math.Floor(value / ((double)max / 5.0));

            if (v > 5) v = 5;
            if (v < 1) v = 1;

            return (int)v;
        }

        public static int GetSpecialAmount(Item item, int id, int value)
        {
            int max = ItemPropertyInfo.GetMaxIntensity(item, id, true);

            int intensity = (int)(((double)value / (double)max) * 100);

            if (intensity >= 100)
            {
                return 10;
            }

            else if (intensity >= 1 && intensity > 90)
            {
                return intensity - 90;
            }

            return 0;
        }

        [Usage("GetTotalMods")]
        [Description("Displays the total mods, ie AOS attributes for the targeted item.")]
        public static void GetTotalMods_OnCommand(CommandEventArgs e)
        {
            e.Mobile.BeginTarget(12, false, TargetFlags.None, new TargetCallback(GetTotalMods_OnTarget));
            e.Mobile.SendMessage("Target the item to get total AOS Attributes.");
        }

        public static void GetTotalMods_OnTarget(Mobile from, object targeted)
        {
            if (targeted is Item)
            {
                int ids = GetTotalMods((Item)targeted);

                ((Item)targeted).LabelTo(from, String.Format("Total Mods: {0}", ids.ToString()));
            }
            else
                from.SendMessage("That is not an item!");
        }

        public static int GetTotalMods(Item item, int id = -1)
        {
            int total = 0;
            object prop = ItemPropertyInfo.GetAttribute(id);

            if (item is BaseWeapon)
            {
                BaseWeapon wep = item as BaseWeapon;

                foreach (int i in Enum.GetValues(typeof(AosAttribute)))
                {
                    AosAttribute attr = (AosAttribute)i;

                    if(!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (wep.Attributes[attr] > 0)
                    {
                        if (!(prop is AosAttribute) || ((AosAttribute)prop) != attr)
                            total++;
                    }
                    else if (wep.Attributes[attr] == 0 && attr == AosAttribute.CastSpeed && wep.Attributes[AosAttribute.SpellChanneling] > 0)
                    {
                        if(!(prop is AosAttribute) || (AosAttribute)prop != attr)
                            total++;
                    }
                }

                total += GetSkillBonuses(wep.SkillBonuses, prop);

                foreach (long i in Enum.GetValues(typeof(AosWeaponAttribute)))
                {
                    AosWeaponAttribute attr = (AosWeaponAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (wep.WeaponAttributes[attr] > 0)
                    {
                        if (IsHitAreaOrSpell(attr, id))
                            continue;

                        if (!(prop is AosWeaponAttribute) || ((AosWeaponAttribute)prop) != attr)
                            total++;
                    }
                }

                foreach (int i in Enum.GetValues(typeof(ExtendedWeaponAttribute)))
                {
                    ExtendedWeaponAttribute attr = (ExtendedWeaponAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (wep.ExtendedWeaponAttributes[attr] > 0)
                    {
                        if (!(prop is ExtendedWeaponAttribute) || ((ExtendedWeaponAttribute)prop) != attr)
                            total++;
                    }
                }

                if (wep.Slayer != SlayerName.None && (!(prop is SlayerName) || ((SlayerName)prop) != wep.Slayer))
                    total++;

                if (wep.Slayer2 != SlayerName.None)
                    total++;

                if (wep.Slayer3 != TalismanSlayerName.None)
                    total++;

                foreach (int i in Enum.GetValues(typeof(SAAbsorptionAttribute)))
                {
                    SAAbsorptionAttribute attr = (SAAbsorptionAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (wep.AbsorptionAttributes[attr] > 0)
                    {
                        if (!(prop is SAAbsorptionAttribute) || ((SAAbsorptionAttribute)prop) != attr)
                            total++;
                    }
                }

                if (wep is BaseRanged && !(prop is string))
                {
                    BaseRanged ranged = wep as BaseRanged;

                    if (ranged.Velocity > 0 && id != 60)
                        total++;
                }

                if (wep.SearingWeapon)
                    total++;
            }
            else if (item is BaseArmor)
            {
                BaseArmor armor = item as BaseArmor;

                foreach (int i in Enum.GetValues(typeof(AosAttribute)))
                {
                    AosAttribute attr = (AosAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (armor.Attributes[attr] > 0)
                    {
                        if (!(prop is AosAttribute) || ((AosAttribute)prop) != attr)
                            total++;
                    }
                    else if (armor.Attributes[attr] == 0 && attr == AosAttribute.CastSpeed && armor.Attributes[AosAttribute.SpellChanneling] > 0)
                    {
                        if (!(prop is AosAttribute) || (AosAttribute)prop == attr)
                            total++;
                    }
                }

                total += GetSkillBonuses(armor.SkillBonuses, prop);

                if (armor.PhysicalBonus > armor.PhysNonImbuing && id != 51) { total++; }
                if (armor.FireBonus > armor.FireNonImbuing && id != 52) { total++; }
                if (armor.ColdBonus > armor.ColdNonImbuing && id != 53) { total++; }
                if (armor.PoisonBonus > armor.PoisonNonImbuing && id != 54) { total++; }
                if (armor.EnergyBonus > armor.EnergyNonImbuing && id != 55) { total++; }

                foreach (int i in Enum.GetValues(typeof(AosArmorAttribute)))
                {
                    AosArmorAttribute attr = (AosArmorAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (armor.ArmorAttributes[attr] > 0)
                    {
                        if (!(prop is AosArmorAttribute) || ((AosArmorAttribute)prop) != attr)
                            total++;
                    }
                }


                foreach (int i in Enum.GetValues(typeof(SAAbsorptionAttribute)))
                {
                    SAAbsorptionAttribute attr = (SAAbsorptionAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (armor.AbsorptionAttributes[attr] > 0)
                    {
                        if (!(prop is SAAbsorptionAttribute) || ((SAAbsorptionAttribute)prop) != attr)
                            total++;
                    }
                }
            }
            else if (item is BaseJewel)
            {
                BaseJewel j = item as BaseJewel;

                foreach (int i in Enum.GetValues(typeof(AosAttribute)))
                {
                    AosAttribute attr = (AosAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (j.Attributes[attr] > 0)
                    {
                        if (!(prop is AosAttribute) || ((AosAttribute)prop) != attr)
                            total++;
                    }
                }

                foreach (int i in Enum.GetValues(typeof(SAAbsorptionAttribute)))
                {
                    SAAbsorptionAttribute attr = (SAAbsorptionAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (j.AbsorptionAttributes[attr] > 0)
                    {
                        if (!(prop is SAAbsorptionAttribute) || ((SAAbsorptionAttribute)prop) != attr)
                            total++;
                    }
                }

                total += GetSkillBonuses(j.SkillBonuses, prop);

                if (j.Resistances.Physical > 0 && id != 51) { total++; }
                if (j.Resistances.Fire > 0 && id != 52) { total++; }
                if (j.Resistances.Cold > 0 && id != 53) { total++; }
                if (j.Resistances.Poison > 0 && id != 54) { total++; }
                if (j.Resistances.Energy > 0 && id != 55) { total++; }
            }
            else if (item is BaseClothing)
            {
                BaseClothing clothing = item as BaseClothing;

                foreach (int i in Enum.GetValues(typeof(AosAttribute)))
                {
                    AosAttribute attr = (AosAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (clothing.Attributes[attr] > 0)
                    {
                        if (!(prop is AosAttribute) || ((AosAttribute)prop) != attr)
                            total++;
                    }
                }

                foreach (int i in Enum.GetValues(typeof(SAAbsorptionAttribute)))
                {
                    SAAbsorptionAttribute attr = (SAAbsorptionAttribute)i;

                    if (!ItemPropertyInfo.ValidateProperty(attr))
                        continue;

                    if (clothing.SAAbsorptionAttributes[attr] > 0)
                    {
                        if (!(prop is SAAbsorptionAttribute) || ((SAAbsorptionAttribute)prop) != attr)
                            total++;
                    }
                }

                total += GetSkillBonuses(clothing.SkillBonuses, prop);

                if (clothing.Resistances.Physical > clothing.PhysNonImbuing && id != 51) { total++; }
                if (clothing.Resistances.Fire > clothing.FireNonImbuing && id != 52) { total++; }
                if (clothing.Resistances.Cold > clothing.ColdNonImbuing && id != 53) { total++; }
                if (clothing.Resistances.Poison > clothing.PoisonNonImbuing && id != 54) { total++; }
                if (clothing.Resistances.Energy > clothing.EnergyNonImbuing && id != 55) { total++; }
            }

            var type = item.GetType();

            if (IsDerivedArmorOrClothing(type))
            {
                int[] resists = null;

                if (ResistBuffer != null && ResistBuffer.ContainsKey(type))
                {
                    resists = ResistBuffer[type];
                }
                else
                {
                    var baseType = type.BaseType;

                    if (IsDerivedArmorOrClothing(baseType))
                    {
                        var temp = Loot.Construct(baseType);

                        if (temp != null)
                        {
                            resists = new int[5];

                            resists[0] = GetBaseResistBonus(item, AosElementAttribute.Physical) - GetBaseResistBonus(temp, AosElementAttribute.Physical);
                            resists[1] = GetBaseResistBonus(item, AosElementAttribute.Fire) - GetBaseResistBonus(temp, AosElementAttribute.Fire);
                            resists[2] = GetBaseResistBonus(item, AosElementAttribute.Cold) - GetBaseResistBonus(temp, AosElementAttribute.Cold);
                            resists[3] = GetBaseResistBonus(item, AosElementAttribute.Poison) - GetBaseResistBonus(temp, AosElementAttribute.Poison);
                            resists[4] = GetBaseResistBonus(item, AosElementAttribute.Energy) - GetBaseResistBonus(temp, AosElementAttribute.Energy);

                            if (ResistBuffer == null)
                                ResistBuffer = new Dictionary<Type, int[]>();

                            ResistBuffer[type] = resists;
                            temp.Delete();
                        }
                    }
                }

                if (resists != null)
                {
                    for (int i = 0; i < resists.Length; i++)
                    {
                        if (id != 51 + i && resists[i] > 0)
                        {
                            total++;
                        }
                    }
                }
            }

            return total;
        }

        private static bool IsHitAreaOrSpell(AosWeaponAttribute attr, int id)
        {
            if (attr >= AosWeaponAttribute.HitMagicArrow && attr <= AosWeaponAttribute.HitDispel)
                return id >= 35 && id <= 39;
            else if (attr >= AosWeaponAttribute.HitColdArea && attr <= AosWeaponAttribute.HitPhysicalArea)
                return id >= 30 && id <= 34;
            return false;
        }

        private static bool IsInSkillGroup(SkillName skill, int index)
        {
            if (index < 0 || index >= m_SkillGroups.Length)
                return false;

            foreach (SkillName name in m_SkillGroups[index])
            {
                if (name == skill)
                    return true;
            }
            return false;
        }

        private static int GetSkillBonuses(AosSkillBonuses bonus, object prop)
        {
            int id = 0;

            for (int j = 0; j < 5; j++)
            {
                if (bonus.GetBonus(j) > 0)
                {
                    if (!(prop is SkillName) || !IsInSkillGroup((SkillName)prop, j))
                        id += 1;
                }
            }

            return id;
        }

        [Usage("GetTotalWeight")]
        [Description("Displays the total imbuing weight of the targeted item.")]
        public static void GetTotalWeight_OnCommand(CommandEventArgs e)
        {
            e.Mobile.BeginTarget(12, false, TargetFlags.None, new TargetCallback(GetTotalWeight_OnTarget));
            e.Mobile.SendMessage("Target the item to get total imbuing weight.");
        }

        public static void GetTotalWeight_OnTarget(Mobile from, object targeted)
        {
            if (targeted is Item)
            {
                int w = GetTotalWeight((Item)targeted, -1, false, true);
                ((Item)targeted).LabelTo(from, String.Format("Imbuing Weight: {0}", w.ToString()));
                w = GetTotalWeight((Item)targeted, -1, false, false);
                ((Item)targeted).LabelTo(from, String.Format("Loot Weight: {0}", w.ToString()));
                w = GetTotalWeight((Item)targeted, -1, true, true);
                ((Item)targeted).LabelTo(from, String.Format("True Weight: {0}", w.ToString()));
            }
            else
                from.SendMessage("That is not an item!");
        }

        public static Dictionary<Type, int[]> ResistBuffer { get; private set; }

        public static int GetTotalWeight(Item item, int id, bool trueWeight, bool imbuing)
        {
            double weight = 0;

            AosAttributes aosAttrs = RunicReforging.GetAosAttributes(item);
            AosWeaponAttributes wepAttrs = RunicReforging.GetAosWeaponAttributes(item);
            SAAbsorptionAttributes saAttrs = RunicReforging.GetSAAbsorptionAttributes(item);
            AosArmorAttributes armorAttrs = RunicReforging.GetAosArmorAttributes(item);
            AosElementAttributes resistAttrs = RunicReforging.GetElementalAttributes(item);
            ExtendedWeaponAttributes extattrs = RunicReforging.GetExtendedWeaponAttributes(item);

            if (item is BaseWeapon)
            {
                if(((BaseWeapon)item).Slayer != SlayerName.None)
                    weight += GetIntensityForAttribute(item, ((BaseWeapon)item).Slayer, id, 1, trueWeight, imbuing);

                if (((BaseWeapon)item).Slayer2 != SlayerName.None)
                    weight += GetIntensityForAttribute(item, ((BaseWeapon)item).Slayer2, id, 1, trueWeight, imbuing);

                if (((BaseWeapon)item).Slayer3 != TalismanSlayerName.None)
                    weight += GetIntensityForAttribute(item, ((BaseWeapon)item).Slayer3, id, 1, trueWeight, imbuing);

                if(((BaseWeapon)item).SearingWeapon)
                    weight += GetIntensityForAttribute(item, "SearingWeapon", id, 1, trueWeight, imbuing);

                if (item is BaseRanged)
                {
                    BaseRanged ranged = item as BaseRanged;

                    if(ranged.Velocity > 0)
                        weight += GetIntensityForAttribute(item, "WeaponVelocity", id, ranged.Velocity, trueWeight, imbuing);
                }
            }
            else if (item is BaseArmor)
            {
                var arm = (BaseArmor)item;

                if (arm.PhysicalBonus > arm.PhysNonImbuing) { if (id != 51) { weight += ((double)(100.0 / 15) * (double)(arm.PhysicalBonus - arm.PhysNonImbuing)); } }
                if (arm.FireBonus > arm.FireNonImbuing) { if (id != 52) { weight += ((double)(100.0 / 15) * (double)(arm.FireBonus - arm.FireNonImbuing)); } }
                if (arm.ColdBonus > arm.ColdNonImbuing) { if (id != 53) { weight += ((double)(100.0 / 15) * (double)(arm.ColdBonus - arm.ColdNonImbuing)); } }
                if (arm.PoisonBonus > arm.PoisonNonImbuing) { if (id != 54) { weight += ((double)(100.0 / 15) * (double)(arm.PoisonBonus - arm.PoisonNonImbuing)); } }
                if (arm.EnergyBonus > arm.EnergyNonImbuing) { if (id != 55) { weight += ((double)(100.0 / 15) * (double)(arm.EnergyBonus - arm.EnergyNonImbuing)); } }
            }

            var type = item.GetType();

            if (IsDerivedArmorOrClothing(type))
            {
                int[] resists = null;

                if (ResistBuffer != null && ResistBuffer.ContainsKey(type))
                {
                    resists = ResistBuffer[type];
                }
                else
                {
                    var baseType = type.BaseType;

                    if (IsDerivedArmorOrClothing(baseType))
                    {
                        var temp = Loot.Construct(baseType);

                        if (temp != null)
                        {
                            resists = new int[5];

                            resists[0] = GetBaseResistBonus(item, AosElementAttribute.Physical) - GetBaseResistBonus(temp, AosElementAttribute.Physical);
                            resists[1] = GetBaseResistBonus(item, AosElementAttribute.Fire) - GetBaseResistBonus(temp, AosElementAttribute.Fire);
                            resists[2] = GetBaseResistBonus(item, AosElementAttribute.Cold) - GetBaseResistBonus(temp, AosElementAttribute.Cold);
                            resists[3] = GetBaseResistBonus(item, AosElementAttribute.Poison) - GetBaseResistBonus(temp, AosElementAttribute.Poison);
                            resists[4] = GetBaseResistBonus(item, AosElementAttribute.Energy) - GetBaseResistBonus(temp, AosElementAttribute.Energy);

                            if (ResistBuffer == null)
                                ResistBuffer = new Dictionary<Type, int[]>();

                            ResistBuffer[type] = resists;
                            temp.Delete();
                        }
                    }
                }

                if (resists != null)
                {
                    for (int i = 0; i < resists.Length; i++)
                    {
                        if(id != 51 + i && resists[i] > 0)
                        {
                            weight += ((double)(100.0 / 15) * (double)resists[i]);
                        }
                    }
                }
            }

            if (aosAttrs != null)
                foreach (int i in Enum.GetValues(typeof(AosAttribute)))
                    weight += GetIntensityForAttribute(item, (AosAttribute)i, id, aosAttrs[(AosAttribute)i], trueWeight, imbuing);

            if (wepAttrs != null)
                foreach (long i in Enum.GetValues(typeof(AosWeaponAttribute)))
                    weight += GetIntensityForAttribute(item, (AosWeaponAttribute)i, id, wepAttrs[(AosWeaponAttribute)i], trueWeight, imbuing);

            if (saAttrs != null)
                foreach (int i in Enum.GetValues(typeof(SAAbsorptionAttribute)))
                    weight += GetIntensityForAttribute(item, (SAAbsorptionAttribute)i, id, saAttrs[(SAAbsorptionAttribute)i], trueWeight, imbuing);

            if (armorAttrs != null)
                foreach (int i in Enum.GetValues(typeof(AosArmorAttribute)))
                    weight += GetIntensityForAttribute(item, (AosArmorAttribute)i, id, armorAttrs[(AosArmorAttribute)i], trueWeight, imbuing);

            if (resistAttrs != null && !(item is BaseWeapon))
                foreach (int i in Enum.GetValues(typeof(AosElementAttribute)))
                    weight += GetIntensityForAttribute(item, (AosElementAttribute)i, id, resistAttrs[(AosElementAttribute)i], trueWeight, imbuing);

            if(extattrs != null)
                foreach (int i in Enum.GetValues(typeof(ExtendedWeaponAttribute)))
                    weight += GetIntensityForAttribute(item, (ExtendedWeaponAttribute)i, id, extattrs[(ExtendedWeaponAttribute)i], trueWeight, imbuing);

            weight += CheckSkillBonuses(item, id, trueWeight, imbuing);

            return (int)weight;
        }

        public static int[] GetBaseResists(Item item)
        {
            int[] resists;

            // Special items base resist don't count as a property or weight. Once that resist is imbued, 
            // it then uses the base class resistance as the base resistance. EA is stupid.
            if (item is IImbuableEquipement && IsSpecialImbuable(item))
            {
                resists = ((IImbuableEquipement)item).BaseResists;
            }
            else
            {
                resists = new int[5];

                resists[0] = GetBaseResistBonus(item, AosElementAttribute.Physical);
                resists[1] = GetBaseResistBonus(item, AosElementAttribute.Fire);
                resists[2] = GetBaseResistBonus(item, AosElementAttribute.Cold);
                resists[3] = GetBaseResistBonus(item, AosElementAttribute.Poison);
                resists[4] = GetBaseResistBonus(item, AosElementAttribute.Energy);
            }

            return resists;
        }

        private static int GetBaseResistBonus(Item item, AosElementAttribute resist)
        {
            switch (resist)
            {
                case AosElementAttribute.Physical:
                    {
                        if (item is BaseArmor)
                            return ((BaseArmor)item).BasePhysicalResistance;

                        if (item is BaseClothing)
                            return ((BaseClothing)item).BasePhysicalResistance;

                        break;
                    }
                case AosElementAttribute.Fire:
                    {
                        if (item is BaseArmor)
                            return ((BaseArmor)item).BaseFireResistance;

                        if (item is BaseClothing)
                            return ((BaseClothing)item).BaseFireResistance;

                        break;
                    }
                case AosElementAttribute.Cold:
                    {
                        if (item is BaseArmor)
                            return ((BaseArmor)item).BaseColdResistance;

                        if (item is BaseClothing)
                            return ((BaseClothing)item).BaseColdResistance;

                        break;
                    }
                case AosElementAttribute.Poison:
                    {
                        if (item is BaseArmor)
                            return ((BaseArmor)item).BasePoisonResistance;

                        if (item is BaseClothing)
                            return ((BaseClothing)item).BasePoisonResistance;

                        break;
                    }
                case AosElementAttribute.Energy:
                    {
                        if (item is BaseArmor)
                            return ((BaseArmor)item).BaseEnergyResistance;

                        if (item is BaseClothing)
                            return ((BaseClothing)item).BaseEnergyResistance;

                        break;
                    }
            }

            return 0;
        }

        /// <summary>
        /// This is for special items such as artifacts, if you ever so chose to imbue them on your server. Without
        /// massive edits, this should never come back as true.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static bool IsDerivedArmorOrClothing(Type type)
        {
            if (IsSpecialImbuable(type))
            {
                return false;
            }

            return (type.IsSubclassOf(typeof(BaseClothing)) || type.IsSubclassOf(typeof(BaseArmor))) &&
                type != typeof(BaseHat) &&
                type != typeof(BaseShield) &&
                type != typeof(Item) &&
                type != typeof(BaseOuterTorso) &&
                type != typeof(BaseMiddleTorso) &&
                type != typeof(BaseOuterLegs) &&
                type != typeof(BasePants) &&
                type != typeof(BaseShirt) &&
                type != typeof(BaseWaist) &&
                type != typeof(BaseShoes) &&
                type != typeof(BaseCloak);
        }

        private static int CheckSkillBonuses(Item item, int check, bool trueWeight, bool imbuing)
        {
            double weight = 0;
            int id = -1;

            AosSkillBonuses skills = RunicReforging.GetAosSkillBonuses(item);

            if (item is BaseJewel)
            {
                id = check;
            }

            // Place Holder. THis is in case the skill weight/max intensity every changes
            int totalWeight = trueWeight ? 100 : ItemPropertyInfo.GetWeight(151);
            int maxInt = ItemPropertyInfo.GetMaxIntensity(item, 151, imbuing);

            if (skills != null)
            {
                if (skills.GetBonus(0) > 0) { if (id < 151 || id > 155) { weight += ((double)(totalWeight / maxInt) * (double)skills.GetBonus(0)); } }
                if (skills.GetBonus(1) > 0) { if (id < 156 || id > 160) { weight += ((double)(totalWeight / maxInt) * (double)skills.GetBonus(1)); } }
                if (skills.GetBonus(2) > 0) { if (id < 161 || id > 166) { weight += ((double)(totalWeight / maxInt) * (double)skills.GetBonus(2)); } }
                if (skills.GetBonus(3) > 0) { if (id < 167 || id > 173) { weight += ((double)(totalWeight / maxInt) * (double)skills.GetBonus(3)); } }
                if (skills.GetBonus(4) > 0) { if (id < 174 || id > 180) { weight += ((double)(totalWeight / maxInt) * (double)skills.GetBonus(4)); } }
            }

            return (int)weight;
        }

        public static SkillName[] PossibleSkills { get { return m_PossibleSkills; } }
        private static SkillName[] m_PossibleSkills = new SkillName[]
			{
				SkillName.Swords,
				SkillName.Fencing,
				SkillName.Macing,
				SkillName.Archery,
				SkillName.Wrestling,
				SkillName.Parry,
				SkillName.Tactics,
				SkillName.Anatomy,
				SkillName.Healing,
				SkillName.Magery,
				SkillName.Meditation,
				SkillName.EvalInt,
				SkillName.MagicResist,
				SkillName.AnimalTaming,
				SkillName.AnimalLore,
				SkillName.Veterinary,
				SkillName.Musicianship,
				SkillName.Provocation,
				SkillName.Discordance,
				SkillName.Peacemaking,
				SkillName.Chivalry,
				SkillName.Focus,
				SkillName.Necromancy,
				SkillName.Stealing,
				SkillName.Stealth,
				SkillName.SpiritSpeak,
				SkillName.Bushido,
				SkillName.Ninjitsu,
                SkillName.Throwing,
                SkillName.Mysticism
			};

        private static SkillName[][] m_SkillGroups = new SkillName[][]
        {
            new SkillName[] { SkillName.Fencing, SkillName.Macing, SkillName.Swords, SkillName.Musicianship, SkillName.Magery },
            new SkillName[] { SkillName.Wrestling, SkillName.AnimalTaming, SkillName.SpiritSpeak, SkillName.Tactics, SkillName.Provocation },
            new SkillName[] { SkillName.Focus, SkillName.Parry, SkillName.Stealth, SkillName.Meditation, SkillName.AnimalLore, SkillName.Discordance },
            new SkillName[] { SkillName.Mysticism, SkillName.Bushido, SkillName.Necromancy, SkillName.Veterinary, SkillName.Stealing, SkillName.EvalInt, SkillName.Anatomy },
            new SkillName[] { SkillName.Peacemaking, SkillName.Ninjitsu, SkillName.Chivalry, SkillName.Archery, SkillName.MagicResist, SkillName.Healing, SkillName.Throwing }
        };

        public static SkillName[] GetSkillGroup(SkillName skill)
        {
            return m_SkillGroups.FirstOrDefault(list => list.Any(sk => sk == skill));
        }

        public static int GetAvailableSkillIndex(AosSkillBonuses skills)
        {
            for (int i = 0; i < 5; i++)
            {
                if (skills.GetBonus(i) == 0)
                {
                    return i;
                }
            }

            return 01;
        }
        
        public static bool CheckSoulForge(Mobile from, int range)
        {
            return CheckSoulForge(from, range, true, true);
        }

        public static bool CheckSoulForge(Mobile from, int range, out double bonus)
        {
            return CheckSoulForge(from, range, true, true, out bonus);
        }

        public static bool CheckSoulForge(Mobile from, int range, bool message)
        {
            double bonus;
            return CheckSoulForge(from, range, message, false, out bonus);
        }

        public static bool CheckSoulForge(Mobile from, int range, bool message, bool checkqueen)
        {
            double bonus;
            return CheckSoulForge(from, range, message, checkqueen, out bonus);
        }

        public static bool CheckSoulForge(Mobile from, int range, bool message, bool checkqueen, out double bonus)
        {
            PlayerMobile m = from as PlayerMobile;
            bonus = 0.0;

            ImbuingContext context = Imbuing.GetContext(m);
            Map map = from.Map;

            if (map == null)
                return false;

            bool isForge = false;

            IPooledEnumerable eable = map.GetItemsInRange(from.Location, range);

            foreach (Item item in eable)
            {
                if ((item.ItemID >= 0x4277 && item.ItemID <= 0x4286) || (item.ItemID >= 0x4263 && item.ItemID <= 0x4272) || (item.ItemID >= 17607 && item.ItemID <= 17610))
				{
					isForge = true;
					break;
				}
            }

            eable.Free();

			if (!isForge)
			{
				if (message)
					from.SendLocalizedMessage(1079787); // You must be near a soulforge to imbue an item.

				return false;
			}

            if (checkqueen)
            {
                if (from.Region != null && from.Region.IsPartOf("Queen's Palace"))
                {
                    if (!Server.Engines.Points.PointsSystem.QueensLoyalty.IsNoble(from))
                    {
                        if (message)
                        {
                            from.SendLocalizedMessage(1113736); // You must rise to the rank of noble in the eyes of the Gargoyle Queen before her majesty will allow you to use this soulforge.
                        }

                        return false;
                    }
                    else
                    {
                        bonus = 0.06;
                    }
                }
                else if (from.Region != null && from.Region.IsPartOf("Royal City"))
                {
                    bonus = 0.02;
                }
            }

            return true;
        }

        public static Type[] IngredTypes { get { return m_IngredTypes; } }
        private static Type[] m_IngredTypes = new Type[]
		{
			typeof(MagicalResidue), 	typeof(EnchantedEssence), 		typeof(RelicFragment),
			
			typeof(SeedOfRenewal), 		typeof(ChagaMushroom), 			typeof(CrystalShards),
			typeof(BottleIchor), 		typeof(ReflectiveWolfEye), 		typeof(FaeryDust),
			typeof(BouraPelt), 			typeof(SilverSnakeSkin), 		typeof(ArcanicRuneStone),
			typeof(SlithTongue), 		typeof(VoidOrb), 				typeof(RaptorTeeth),
			typeof(SpiderCarapace), 	typeof(DaemonClaw), 			typeof(VialOfVitriol),
			typeof(GoblinBlood), 		typeof(LavaSerpentCrust), 		typeof(UndyingFlesh),
			typeof(CrushedGlass), 		typeof(CrystallineBlackrock), 	typeof(PowderedIron),
			typeof(ElvenFletching),     typeof(DelicateScales),
			
			typeof(EssenceSingularity), typeof(EssenceBalance), 		typeof(EssencePassion),
			typeof(EssenceDirection), 	typeof(EssencePrecision), 		typeof(EssenceControl),
			typeof(EssenceDiligence), 	typeof(EssenceAchievement), 	typeof(EssenceFeeling), 
			typeof(EssenceOrder),
			
			typeof(ParasiticPlant), 	typeof(LuminescentFungi),
			typeof(FireRuby), 			typeof(WhitePearl), 			typeof(BlueDiamond), 
			typeof(Turquoise)
		};


        public static bool IsInNonImbueList(Type itemType)
        {
            foreach (Type type in m_CannotImbue)
            {
                if (type == itemType)
                    return true;
            }

            return false;
        }

        private static Type[] m_CannotImbue = new Type[]
        {
            typeof(GargishLeatherWingArmor), typeof(GargishClothWingArmor)
        };

        public static int GetValueForID(Item item, int id)
        {
            object attr = ItemPropertyInfo.GetAttribute(id);

            if (item is BaseWeapon)
            {
                BaseWeapon w = (BaseWeapon)item;

                if (id == 16 && w.Attributes.SpellChanneling > 0)
                    return w.Attributes[AosAttribute.CastSpeed] + 1;

                if (attr is AosAttribute)
                    return w.Attributes[(AosAttribute)attr];

                else if (attr is AosWeaponAttribute)
                    return w.WeaponAttributes[(AosWeaponAttribute)attr];

                else if (attr is ExtendedWeaponAttribute)
                    return w.ExtendedWeaponAttributes[(ExtendedWeaponAttribute)attr];

                else if (attr is SAAbsorptionAttribute)
                    return w.AbsorptionAttributes[(SAAbsorptionAttribute)attr];

                else if (attr is SlayerName && w.Slayer == (SlayerName)attr)
                    return 1;

                else if (id == 60 && item is BaseRanged)
                    return ((BaseRanged)item).Velocity;

                else if (id == 62)
                    return w.SearingWeapon ? 1 : 0;

                else if (attr is AosElementAttribute)
                {
                    AosElementAttribute ele = (AosElementAttribute)attr;

                    switch (ele)
                    {
                        case AosElementAttribute.Physical: return w.WeaponAttributes.ResistPhysicalBonus;
                        case AosElementAttribute.Fire: return w.WeaponAttributes.ResistFireBonus;
                        case AosElementAttribute.Cold: return w.WeaponAttributes.ResistColdBonus;
                        case AosElementAttribute.Poison: return w.WeaponAttributes.ResistPoisonBonus;
                        case AosElementAttribute.Energy: return w.WeaponAttributes.ResistEnergyBonus;
                    }
                }
            }
            else if (item is BaseArmor)
            {
                BaseArmor a = (BaseArmor)item;

                if (a is BaseShield && id == 16 && a.Attributes.SpellChanneling > 0)
                    return a.Attributes[AosAttribute.CastSpeed] + 1;

                if (attr is AosAttribute)
                    return a.Attributes[(AosAttribute)attr];

                else if (attr is AosArmorAttribute)
                    return a.ArmorAttributes[(AosArmorAttribute)attr];

                else if (attr is SAAbsorptionAttribute)
                    return a.AbsorptionAttributes[(SAAbsorptionAttribute)attr];

                else if (attr is AosElementAttribute)
                {
                    AosElementAttribute ele = (AosElementAttribute)attr;
                    var value = 0;

                    switch (ele)
                    {
                        case AosElementAttribute.Physical: value = a.PhysicalBonus; break;
                        case AosElementAttribute.Fire: value = a.FireBonus; break;
                        case AosElementAttribute.Cold: value = a.ColdBonus; break;
                        case AosElementAttribute.Poison: value = a.PoisonBonus; break;
                        case AosElementAttribute.Energy: value = a.EnergyBonus; break;
                    }

                    if (value > 0)
                    {
                        return value;
                    }
                }
            }
            else if (item is BaseClothing)
            {
                BaseClothing c = (BaseClothing)item;

                if (attr is AosAttribute)
                    return c.Attributes[(AosAttribute)attr];

                else if (attr is AosElementAttribute)
                {
                    var value = c.Resistances[(AosElementAttribute)attr];

                    if (value > 0)
                    {
                        return value;
                    }
                }

                else if (attr is AosArmorAttribute)
                    return c.ClothingAttributes[(AosArmorAttribute)attr];

                else if (attr is SAAbsorptionAttribute)
                    return c.SAAbsorptionAttributes[(SAAbsorptionAttribute)attr];
            }
            else if (item is BaseJewel)
            {
                BaseJewel j = (BaseJewel)item;

                if (attr is AosAttribute)
                    return j.Attributes[(AosAttribute)attr];

                else if (attr is AosElementAttribute)
                    return j.Resistances[(AosElementAttribute)attr];

                else if (attr is SAAbsorptionAttribute)
                    return j.AbsorptionAttributes[(SAAbsorptionAttribute)attr];

                else if (attr is SkillName)
                {
                    SkillName sk = (SkillName)attr;

                    if (j.SkillBonuses.Skill_1_Name == sk)
                        return (int)j.SkillBonuses.Skill_1_Value;

                    if (j.SkillBonuses.Skill_2_Name == sk)
                        return (int)j.SkillBonuses.Skill_2_Value;

                    if (j.SkillBonuses.Skill_3_Name == sk)
                        return (int)j.SkillBonuses.Skill_3_Value;

                    if (j.SkillBonuses.Skill_4_Name == sk)
                        return (int)j.SkillBonuses.Skill_4_Value;

                    if (j.SkillBonuses.Skill_5_Name == sk)
                        return (int)j.SkillBonuses.Skill_5_Value;
                }
            }

            var type = item.GetType();

            if (id >= 51 && id <= 55 && IsDerivedArmorOrClothing(type))
            {
                int[] resists = null;

                if (ResistBuffer != null && ResistBuffer.ContainsKey(type))
                {
                    resists = ResistBuffer[type];
                }
                else
                {
                    var baseType = type.BaseType;

                    if (IsDerivedArmorOrClothing(baseType))
                    {
                        var temp = Loot.Construct(baseType);

                        if (temp != null)
                        {
                            resists = new int[5];

                            resists[0] = GetBaseResistBonus(item, AosElementAttribute.Physical) - GetBaseResistBonus(temp, AosElementAttribute.Physical);
                            resists[1] = GetBaseResistBonus(item, AosElementAttribute.Fire) - GetBaseResistBonus(temp, AosElementAttribute.Fire);
                            resists[2] = GetBaseResistBonus(item, AosElementAttribute.Cold) - GetBaseResistBonus(temp, AosElementAttribute.Cold);
                            resists[3] = GetBaseResistBonus(item, AosElementAttribute.Poison) - GetBaseResistBonus(temp, AosElementAttribute.Poison);
                            resists[4] = GetBaseResistBonus(item, AosElementAttribute.Energy) - GetBaseResistBonus(temp, AosElementAttribute.Energy);

                            if (ResistBuffer == null)
                                ResistBuffer = new Dictionary<Type, int[]>();

                            ResistBuffer[type] = resists;
                            temp.Delete();
                        }
                    }
                }

                if (resists != null && resists.Length == 5)
                {
                    return resists[id - 51];
                }
            }

            return 0;
        }

        public static int GetIntensityForAttribute(Item item, object attr, int checkID, int value)
        {
            return GetIntensityForAttribute(item, attr, checkID, value, false);
        }

        public static int GetIntensityForAttribute(Item item, object attr, int checkID, int value, bool trueWeight)
        {
            return GetIntensityForID(item, ItemPropertyInfo.GetID(attr), checkID, value, trueWeight, true);
        }

        public static int GetIntensityForAttribute(Item item, object attr, int checkID, int value, bool trueWeight, bool imbuing)
        {
            return GetIntensityForID(item, ItemPropertyInfo.GetID(attr), checkID, value, trueWeight, imbuing);
        }

        public static int GetIntensityForID(Item item, int id, int checkID, int value)
        {
            return GetIntensityForID(item, id, checkID, value, false);
        }

        public static int GetIntensityForID(Item item, int id, int checkID, int value, bool trueWeight)
        {
            return GetIntensityForID(item, id, checkID, value, trueWeight, true);
        }

        public static int GetIntensityForID(Item item, int id, int checkID, int value, bool trueWeight, bool imbuing)
        {
            // This is terribly clunky, however we're accomidating 1 out of 50+ attributes that acts differently
            if (value <= 0 && id != 16)
            {
                return 0;
            }

            if ((item is BaseWeapon || item is BaseShield) && id == 16)
            {
                AosAttributes attrs = RunicReforging.GetAosAttributes(item);

                if (attrs != null && attrs.SpellChanneling > 0)
                    value++;
            }
            
            if (value <= 0)
                return 0;

            if (id != checkID)
            {
                var weight = trueWeight ? 100 : ItemPropertyInfo.GetWeight(id);

                if (weight == 0)
                {
                    return 0;
                }

                int max = ItemPropertyInfo.GetMaxIntensity(item, id, imbuing);

                return (int)(((double)weight / max) * (double)value);
            }

            return 0;
        }

        public static bool CanImbueProperty(Mobile from, Item item, int id)
        {
            ItemPropertyInfo info = ItemPropertyInfo.GetInfo(id);
            bool canImbue = false;

            if (info != null)
            {
                if (item is BaseWeapon)
                {
                    if (item is BaseRanged && info.CanImbue(ItemType.Ranged))
                    {
                        canImbue = true;
                    }
                    else if (info.CanImbue(ItemType.Melee))
                    {
                        canImbue = true;
                    }
                }
                else if (item is BaseArmor)
                {
                    if (item is BaseShield && info.CanImbue(ItemType.Shield))
                    {
                        canImbue = true;
                    }
                    else if (info.CanImbue(ItemType.Armor))
                    {
                        canImbue = true;
                    }
                }
                else if (item is BaseJewel && info.CanImbue(ItemType.Jewel))
                {
                    canImbue = true;
                }

            }

            if (!canImbue)
            {
                from.CloseGump(typeof(ImbueGump));
                from.SendLocalizedMessage(1114291); // You cannot imbue the last property on that item.
            }

            return canImbue;
        }

        public static int TimesImbued(Item item)
        {
            if (item is IImbuableEquipement)
            {
                return ((IImbuableEquipement)item).TimesImbued;
            }

            return 0;
        }
    }
}        
