<commit_before>using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;

using ACE.Common.Extensions;
using ACE.Database.Models.Shard;
using ACE.Entity.Enum;
using ACE.Entity.Enum.Properties;
using ACE.Server.Entity;
using ACE.Server.Network.GameMessages.Messages;
using ACE.Server.Network.GameEvent.Events;
using ACE.Server.Network.Structure;
using ACE.Server.Physics;
using ACE.Server.WorldObjects;
using ACE.Server.WorldObjects.Entity;

namespace ACE.Server.Managers
{
    public enum StackType
    {
        None,
        Initial,
        Surpass,
        Refresh,
        Surpassed,
    };

    public class EnchantmentManager
    {
        public WorldObject WorldObject { get; }
        public Player Player { get; }

        /// <summary>
        /// Returns TRUE if this object has any active enchantments in the registry
        /// </summary>
        public bool HasEnchantments => WorldObject.Biota.HasEnchantments(WorldObject.BiotaDatabaseLock);

        /// <summary>
        /// Returns TRUE If this object has a vitae penalty
        /// </summary>
        public bool HasVitae => WorldObject.Biota.HasEnchantment((uint)SpellId.Vitae, WorldObject.BiotaDatabaseLock);

        /// <summary>
        /// Constructs a new EnchantmentManager for a WorldObject
        /// </summary>
        public EnchantmentManager(WorldObject obj)
        {
            WorldObject = obj;
            Player = obj as Player;
        }


        /// <summary>
        /// Returns TRUE if registry contains spellId for this creature
        /// </summary>
        public bool HasSpell(uint spellId)
        {
            return WorldObject.Biota.HasEnchantment(spellId, WorldObject.BiotaDatabaseLock);
        }

        /// <summary>
        /// Returns the enchantments for a specific spell
        /// </summary>
        public BiotaPropertiesEnchantmentRegistry GetEnchantment(uint spellID, uint? casterGuid = null)
        {
            return WorldObject.Biota.GetEnchantmentBySpell((int)spellID, casterGuid, WorldObject.BiotaDatabaseLock);
        }

        /// <summary>
        /// Returns the enchantments for a specific spell from an equipment set
        /// </summary>
        public BiotaPropertiesEnchantmentRegistry GetEnchantment(uint spellID, EquipmentSet equipmentSet)
        {
            return WorldObject.Biota.GetEnchantmentBySpellSet((int)spellID, (int)equipmentSet, WorldObject.BiotaDatabaseLock);
        }

        /// <summary>
        /// Returns a list of all the active enchantments for a magic school
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments(MagicSchool magicSchool)
        {
            var spells = new List<BiotaPropertiesEnchantmentRegistry>();

            var enchantments = from e in WorldObject.Biota.GetEnchantments(WorldObject.BiotaDatabaseLock)
                group e by e.SpellCategory
                into categories
                select categories.OrderByDescending(c => c.LayerId).First();

            foreach (var enchantment in enchantments)
            {
                var spell = new Spell(enchantment.SpellId);

                if (spell.NotFound)
                {
                    Console.WriteLine($"EnchantmentManager.GetEnchantments({magicSchool}): couldn't find spell {enchantment.SpellId} for {WorldObject.Name}");
                    continue;
                }

                if (spell.School == magicSchool)
                    spells.Add(enchantment);
            }

            return spells;
        }

        /// <summary>
        /// Returns all of the enchantments for a category
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments(SpellCategory spellCategory)
        {
            return WorldObject.Biota.GetEnchantmentsByCategory((ushort)spellCategory, WorldObject.BiotaDatabaseLock);
        }

        /// <summary>
        /// Returns the top layers in each spell category
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments_TopLayer(List<BiotaPropertiesEnchantmentRegistry> enchantments)
        {
            var results = from e in enchantments
                group e by e.SpellCategory
                into categories
                //select categories.OrderByDescending(c => c.LayerId).First();
                select categories.OrderByDescending(c => c.PowerLevel).First();

            return results.ToList();
        }

        /// <summary>
        /// Returns the top layers in each spell category for a StatMod type
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments_TopLayer(EnchantmentTypeFlags statModType)
        {
            return GetEnchantments_TopLayer(WorldObject.Biota.GetEnchantmentsByStatModType((uint)statModType, WorldObject.BiotaDatabaseLock));
        }

        /// <summary>
        /// Returns the top layers in each spell category for a StatMod type + key
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments_TopLayer(EnchantmentTypeFlags statModType, uint statModKey)
        {
            return GetEnchantments_TopLayer(WorldObject.Biota.GetEnchantmentsByStatModType((uint)statModType, WorldObject.BiotaDatabaseLock).Where(e => e.StatModKey == statModKey).ToList());
        }

        /// <summary>
        /// Add/update an enchantment in this object's registry
        /// </summary>
        public virtual AddEnchantmentResult Add(Spell spell, WorldObject caster)
        {
            var result = new AddEnchantmentResult();

            // check for existing spell in this category
            var entries = GetEnchantments(spell.Category);

            // if none, add new record
            if (entries.Count == 0)
            {
                var newEntry = BuildEntry(spell, caster);
                newEntry.LayerId = 1;
                WorldObject.Biota.AddEnchantment(newEntry, WorldObject.BiotaDatabaseLock);
                WorldObject.ChangesDetected = true;

                result.Enchantment = newEntry;
                result.StackType = StackType.Initial;
                return result;
            }

            result.BuildStack(entries, spell, caster);

            // handle cases:
            // surpassing: new spell is written to next layer
            // refreshing: - key by caster guid
            // surpassed:  - underpowered spell is written to next layer?

            // note that these cases are not exclusive,
            // consider case: strength 3 -> strength 6 -> strength 3
            // for the 2nd cast of strength 3, it would have 1 refresh and 1 surpassed
            // would 2nd cast of strength 3 refresh the 1st, but still be surpassed by 6?

            var refreshSpell = result.Refresh.Count > 0 ? result.RefreshCaster : null;

            if (refreshSpell == null)
            {
                var newEntry = BuildEntry(spell, caster);
                newEntry.LayerId = result.NextLayerId;
                WorldObject.Biota.AddEnchantment(newEntry, WorldObject.BiotaDatabaseLock);

                result.Enchantment = newEntry;
            }
            else
            {
                refreshSpell.StartTime = 0;
                result.Enchantment = refreshSpell;
            }
            WorldObject.ChangesDetected = true;

            // output message is from StackType,
            // which is the largest of the combined StackTypes

            return result;
        }

        /// <summary><commit_msg>handling edge cases for refreshing spells w/ augs (#1560)<commit_after>using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;

using ACE.Common.Extensions;
using ACE.Database.Models.Shard;
using ACE.Entity.Enum;
using ACE.Entity.Enum.Properties;
using ACE.Server.Entity;
using ACE.Server.Network.GameMessages.Messages;
using ACE.Server.Network.GameEvent.Events;
using ACE.Server.Network.Structure;
using ACE.Server.Physics;
using ACE.Server.WorldObjects;
using ACE.Server.WorldObjects.Entity;

namespace ACE.Server.Managers
{
    public enum StackType
    {
        None,
        Initial,
        Surpass,
        Refresh,
        Surpassed,
    };

    public class EnchantmentManager
    {
        public WorldObject WorldObject { get; }
        public Player Player { get; }

        /// <summary>
        /// Returns TRUE if this object has any active enchantments in the registry
        /// </summary>
        public bool HasEnchantments => WorldObject.Biota.HasEnchantments(WorldObject.BiotaDatabaseLock);

        /// <summary>
        /// Returns TRUE If this object has a vitae penalty
        /// </summary>
        public bool HasVitae => WorldObject.Biota.HasEnchantment((uint)SpellId.Vitae, WorldObject.BiotaDatabaseLock);

        /// <summary>
        /// Constructs a new EnchantmentManager for a WorldObject
        /// </summary>
        public EnchantmentManager(WorldObject obj)
        {
            WorldObject = obj;
            Player = obj as Player;
        }


        /// <summary>
        /// Returns TRUE if registry contains spellId for this creature
        /// </summary>
        public bool HasSpell(uint spellId)
        {
            return WorldObject.Biota.HasEnchantment(spellId, WorldObject.BiotaDatabaseLock);
        }

        /// <summary>
        /// Returns the enchantments for a specific spell
        /// </summary>
        public BiotaPropertiesEnchantmentRegistry GetEnchantment(uint spellID, uint? casterGuid = null)
        {
            return WorldObject.Biota.GetEnchantmentBySpell((int)spellID, casterGuid, WorldObject.BiotaDatabaseLock);
        }

        /// <summary>
        /// Returns the enchantments for a specific spell from an equipment set
        /// </summary>
        public BiotaPropertiesEnchantmentRegistry GetEnchantment(uint spellID, EquipmentSet equipmentSet)
        {
            return WorldObject.Biota.GetEnchantmentBySpellSet((int)spellID, (int)equipmentSet, WorldObject.BiotaDatabaseLock);
        }

        /// <summary>
        /// Returns a list of all the active enchantments for a magic school
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments(MagicSchool magicSchool)
        {
            var spells = new List<BiotaPropertiesEnchantmentRegistry>();

            var enchantments = from e in WorldObject.Biota.GetEnchantments(WorldObject.BiotaDatabaseLock)
                group e by e.SpellCategory
                into categories
                select categories.OrderByDescending(c => c.LayerId).First();

            foreach (var enchantment in enchantments)
            {
                var spell = new Spell(enchantment.SpellId);

                if (spell.NotFound)
                {
                    Console.WriteLine($"EnchantmentManager.GetEnchantments({magicSchool}): couldn't find spell {enchantment.SpellId} for {WorldObject.Name}");
                    continue;
                }

                if (spell.School == magicSchool)
                    spells.Add(enchantment);
            }

            return spells;
        }

        /// <summary>
        /// Returns all of the enchantments for a category
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments(SpellCategory spellCategory)
        {
            return WorldObject.Biota.GetEnchantmentsByCategory((ushort)spellCategory, WorldObject.BiotaDatabaseLock);
        }

        /// <summary>
        /// Returns the top layers in each spell category
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments_TopLayer(List<BiotaPropertiesEnchantmentRegistry> enchantments)
        {
            var results = from e in enchantments
                group e by e.SpellCategory
                into categories
                //select categories.OrderByDescending(c => c.LayerId).First();
                select categories.OrderByDescending(c => c.PowerLevel).First();

            return results.ToList();
        }

        /// <summary>
        /// Returns the top layers in each spell category for a StatMod type
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments_TopLayer(EnchantmentTypeFlags statModType)
        {
            return GetEnchantments_TopLayer(WorldObject.Biota.GetEnchantmentsByStatModType((uint)statModType, WorldObject.BiotaDatabaseLock));
        }

        /// <summary>
        /// Returns the top layers in each spell category for a StatMod type + key
        /// </summary>
        public List<BiotaPropertiesEnchantmentRegistry> GetEnchantments_TopLayer(EnchantmentTypeFlags statModType, uint statModKey)
        {
            return GetEnchantments_TopLayer(WorldObject.Biota.GetEnchantmentsByStatModType((uint)statModType, WorldObject.BiotaDatabaseLock).Where(e => e.StatModKey == statModKey).ToList());
        }

        /// <summary>
        /// Add/update an enchantment in this object's registry
        /// </summary>
        public virtual AddEnchantmentResult Add(Spell spell, WorldObject caster)
        {
            var result = new AddEnchantmentResult();

            // check for existing spell in this category
            var entries = GetEnchantments(spell.Category);

            // if none, add new record
            if (entries.Count == 0)
            {
                var newEntry = BuildEntry(spell, caster);
                newEntry.LayerId = 1;
                WorldObject.Biota.AddEnchantment(newEntry, WorldObject.BiotaDatabaseLock);
                WorldObject.ChangesDetected = true;

                result.Enchantment = newEntry;
                result.StackType = StackType.Initial;
                return result;
            }

            result.BuildStack(entries, spell, caster);

            // handle cases:
            // surpassing: new spell is written to next layer
            // refreshing: - key by caster guid
            // surpassed:  - underpowered spell is written to next layer?

            // note that these cases are not exclusive,
            // consider case: strength 3 -> strength 6 -> strength 3
            // for the 2nd cast of strength 3, it would have 1 refresh and 1 surpassed
            // would 2nd cast of strength 3 refresh the 1st, but still be surpassed by 6?

            var refreshSpell = result.Refresh.Count > 0 ? result.RefreshCaster : null;

            if (refreshSpell == null)
            {
                var newEntry = BuildEntry(spell, caster);
                newEntry.LayerId = result.NextLayerId;
                WorldObject.Biota.AddEnchantment(newEntry, WorldObject.BiotaDatabaseLock);

                result.Enchantment = newEntry;
            }
            else
            {
                var duration = spell.Duration;
                if (caster is Player player && player.AugmentationIncreasedSpellDuration > 0 && spell.DotDuration == 0)
                    duration *= 1.0f + player.AugmentationIncreasedSpellDuration * 0.2f;

                var timeRemaining = refreshSpell.Duration + refreshSpell.StartTime;

                if (duration > timeRemaining)
                {
                    refreshSpell.StartTime = 0;
                    refreshSpell.Duration = duration;
                }

                result.Enchantment = refreshSpell;
            }
            WorldObject.ChangesDetected = true;

            // output message is from StackType,
            // which is the largest of the combined StackTypes

            return result;
        }

        /// <summary>
{'fraction_non_alphanumeric': 0.08303111280778774, 'fraction_numerical': 0.0025450149519628427, 'mean_word_length': 1.9956165427863541, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5317977', 'n_tokens_mistral': 4095, 'n_tokens_neox': 3898, 'n_words': 1018}