using System.Collections.Generic;
using WingsEmu.DTOs.Items;
using WingsEmu.Game._enum;
using WingsEmu.Game.Algorithm;
using WingsEmu.Game.Buffs;
using WingsEmu.Game.Characters;
using WingsEmu.Game.Entities;
using WingsEmu.Game.EntityStatistics;
using WingsEmu.Game.Inventory;
using WingsEmu.Game.Items;
using WingsEmu.Game.Maps;
using WingsEmu.Game.Mates;
using WingsEmu.Packets.Enums;

namespace WingsEmu.Game.Extensions;

public static class AlgorithmExtension
{
    private static readonly HashSet<EquipmentType> EquipmentTypes = new()
    {
        EquipmentType.Sp,
        EquipmentType.MainWeapon,
        EquipmentType.SecondaryWeapon,
        EquipmentType.Armor
    };

    private static readonly HashSet<StatisticType> StatsTypes = new()
    {
        StatisticType.HITRATE_MAGIC,
        StatisticType.FIRE,
        StatisticType.WATER,
        StatisticType.LIGHT,
        StatisticType.DARK
    };

    public static byte GetSpeed(this IBattleEntity entity, int speed)
    {
        (int firstData, int secondData) increaseSpeedBCard =
            entity.BCardComponent.GetAllBCardsInformation(BCardType.Move, (byte)AdditionalTypes.Move.MovementSpeedIncreased, entity.Level);
        (int firstData, int secondData) decreaseSpeedBCard =
            entity.BCardComponent.GetAllBCardsInformation(BCardType.Move, (byte)AdditionalTypes.Move.MovementSpeedDecreased, entity.Level);

        int increaseSpeed = increaseSpeedBCard.firstData;
        increaseSpeed -= decreaseSpeedBCard.firstData;

        speed += increaseSpeed;

        switch (entity)
        {
            case IPlayerEntity character:
                switch (character.IsOnVehicle)
                {
                    case true:
                        return (byte)(character.VehicleSpeed + character.VehicleMapSpeed);
                    case false when character.Specialist != null && character.UseSp:
                        speed += character.Specialist.GameItem.Speed;
                        break;
                }

                if (character.IsInvisible())
                {
                    speed += character.BCardComponent.GetAllBCardsInformation(BCardType.Move, (byte)AdditionalTypes.Move.InvisibleMovement, entity.Level).firstData;
                    speed -= character.BCardComponent.GetAllBCardsInformation(BCardType.Move, (byte)AdditionalTypes.Move.InvisibleMovementNegated, entity.Level).firstData;
                }

                speed += IncreaseByMap(character);
                break;
            case IMateEntity mateEntity:
                if (mateEntity.CanPickUp)
                {
                    speed += 2;
                }

                if (mateEntity.Specialist != null && mateEntity.IsUsingSp)
                {
                    speed += mateEntity.Specialist.GameItem.Speed;
                }

                if (mateEntity.IsTeamMember)
                {
                    speed += 2;
                }

                if (mateEntity.MateType == MateType.Partner && mateEntity.Skin != 0)
                {
                    speed += 2;
                }

                if (mateEntity.Owner.HasBuff(BuffVnums.GUARDIAN_BLESS))
                {
                    speed += 2;
                }

                break;
        }

        double multiplier = 1 + (entity.BCardComponent.GetAllBCardsInformation(BCardType.Move, (byte)AdditionalTypes.Move.MoveSpeedIncreasedPercentage, entity.Level).firstData * 0.01
            - entity.BCardComponent.GetAllBCardsInformation(BCardType.Move, (byte)AdditionalTypes.Move.MoveSpeedDecreasedPercentage, entity.Level).firstData * 0.01);

        speed = (int)(speed * multiplier);

        if (speed < 0)
        {
            speed = 0;
        }

        return speed > 59 ? (byte)59 : (byte)speed;
    }

    private static int IncreaseByMap(IPlayerEntity character)
    {
        int increaseSpeed = 0;
        IMapInstance mapInstance = character.MapInstance;
        if (mapInstance == null)
        {
            return increaseSpeed;
        }

        if (mapInstance.MapInstanceType == MapInstanceType.RainbowBattle)
        {
            increaseSpeed += character.BCardComponent
                .GetAllBCardsInformation(BCardType.IncreaseDamageVersus, (byte)AdditionalTypes.IncreaseDamageVersus.PvpDamageAndSpeedRainbowBattleIncrease, character.Level).secondData;
        }

        return increaseSpeed;
    }

    public static int GetMaxHp(this IBattleEntity battleEntity, int baseHp)
    {
        int hp = baseHp;
        if (battleEntity is IPlayerEntity character)
        {
            hp += character.StatisticsComponent.Passives.GetValueOrDefault(PassiveType.HP);
            if (character.UseSp)
            {
                double multiplier = 1;
                int point = character.SpecialistComponent.GetSlHp();

                if (point <= 50)
                {
                    multiplier += point * 0.01;
                }
                else
                {
                    multiplier += 0.5 + (point * 0.01 - 0.5) * 2;
                }

                hp = (int)(hp * multiplier);

                hp += character.SpecialistComponent.Hp + character.SpecialistComponent.SpHP * 100;
            }
        }

        int increaseHp = 0;
        double hpMultiplier = 1;

        bool bearSpirit = battleEntity.BCardComponent.HasBCard(BCardType.BearSpirit, (byte)AdditionalTypes.BearSpirit.IncreaseMaximumHP);
        if (bearSpirit)
        {
            double bearMultiplier =
                battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.BearSpirit, (byte)AdditionalTypes.BearSpirit.IncreaseMaximumHP, battleEntity.Level).firstData * 0.01;
            int bearIncrease = (int)(hp * bearMultiplier);
            if (bearIncrease > 5000)
            {
                bearIncrease = 5000;
            }

            hp += bearIncrease;
        }


        hpMultiplier += battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.IncreasesMaximumHP, battleEntity.Level).firstData * 0.01;
        hpMultiplier -= battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.DecreasesMaximumHP, battleEntity.Level).firstData * 0.01;

        increaseHp += battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.MaximumHPIncreased, battleEntity.Level).firstData;
        increaseHp += battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.MaximumHPMPIncreased, battleEntity.Level).firstData;

        increaseHp -= battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.MaximumHPDecreased, battleEntity.Level).firstData;
        increaseHp -= battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.MaximumHPMPDecreased, battleEntity.Level).firstData;

        int increaseFinalHp = (int)(hp * hpMultiplier);
        hp = increaseFinalHp + increaseHp;

        if (battleEntity is IPlayerEntity playerEntity)
        {
            hp += playerEntity.GetJewelsCellonsValue(CellonType.Hp);
        }

        return hp;
    }

    public static int GetMaxMp(this IBattleEntity battleEntity, int baseMp)
    {
        int mp = baseMp;

        if (battleEntity is IPlayerEntity character)
        {
            mp += character.StatisticsComponent.Passives.GetValueOrDefault(PassiveType.MP);

            if (character.UseSp)
            {
                double multiplier = 1;
                int point = character.SpecialistComponent.GetSlHp();

                if (point <= 50)
                {
                    multiplier += point * 0.01;
                }
                else
                {
                    multiplier += 0.5 + (point * 0.01 - 0.5) * 2;
                }

                mp = (int)(mp * multiplier);

                mp += character.SpecialistComponent.Mp + character.SpecialistComponent.SpHP * 100;
            }
        }

        int increaseMp = 0;
        double mpMultiplier = 1;

        bool bearSpirit = battleEntity.BCardComponent.HasBCard(BCardType.BearSpirit, (byte)AdditionalTypes.BearSpirit.IncreaseMaximumMP);
        double bearMultiplier = battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.BearSpirit, (byte)AdditionalTypes.BearSpirit.IncreaseMaximumMP, battleEntity.Level).Item1 * 0.01;
        if (bearSpirit)
        {
            int bearIncrease = (int)(mp * bearMultiplier);
            if (bearIncrease > 5000)
            {
                bearIncrease = 5000;
            }

            mp += bearIncrease;
        }

        mpMultiplier += battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.IncreasesMaximumMP, battleEntity.Level).firstData * 0.01;
        mpMultiplier -= battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.DecreasesMaximumMP, battleEntity.Level).firstData * 0.01;

        increaseMp += battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.MaximumMPIncreased, battleEntity.Level).firstData;
        increaseMp += battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.MaximumHPMPIncreased, battleEntity.Level).firstData;

        increaseMp -= battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.MaximumMPDecreased, battleEntity.Level).firstData;
        increaseMp -= battleEntity.BCardComponent.GetAllBCardsInformation(BCardType.MaxHPMP, (byte)AdditionalTypes.MaxHPMP.MaximumHPMPDecreased, battleEntity.Level).firstData;

        int increaseFinalMp = (int)(mp * mpMultiplier);
        mp = increaseFinalMp + increaseMp;

        if (battleEntity is IPlayerEntity playerEntity)
        {
            mp += playerEntity.GetJewelsCellonsValue(CellonType.Mp);
        }

        return mp;
    }


    public static void RefreshMaxHpMp(this IBattleEntity entity, IBattleEntityAlgorithmService algorithm)
    {
        switch (entity)
        {
            case IPlayerEntity character:
                character.MaxHp = algorithm.GetBasicHpByClass(character.Class, character.Level);
                character.MaxMp = algorithm.GetBasicMpByClass(character.Class, character.Level);
                break;
            case IMateEntity mateEntity:
                mateEntity.MaxHp = algorithm.GetBasicHp((int)mateEntity.MonsterRaceType, mateEntity.Level, mateEntity.MeleeHpFactor, mateEntity.CleanHp, false);
                mateEntity.MaxMp = algorithm.GetBasicMp((int)mateEntity.MonsterRaceType, mateEntity.Level, mateEntity.MagicMpFactor, mateEntity.CleanMp, false);
                break;
        }
    }

    public static long GetLevelXp(this IPlayerEntity character, ICharacterAlgorithm characterAlgorithm) => characterAlgorithm.GetLevelXp(character.Level);
    public static int GetJobXp(this IPlayerEntity character, ICharacterAlgorithm characterAlgorithm, bool isAdventurer = false) => characterAlgorithm.GetJobXp(character.JobLevel, isAdventurer);
    public static int GetHeroXp(this IPlayerEntity character, ICharacterAlgorithm characterAlgorithm) => characterAlgorithm.GetHeroLevelXp(character.HeroLevel);

    public static int GetSpJobXp(this IPlayerEntity character, ICharacterAlgorithm characterAlgorithm, bool isFunSpecialist) =>
        characterAlgorithm.GetSpecialistJobXp(character.Specialist.SpLevel, isFunSpecialist);

    public static int GetResistance(this IPlayerEntity character, StatisticType type)
    {
        int resistance = 0;

        GameItemInstance specialistInstance = character.Specialist;
        if (character.UseSp && specialistInstance != null)
        {
            resistance += type switch
            {
                StatisticType.FIRE => character.SpecialistComponent.FireResistance + specialistInstance.GameItem.FireResistance + character.SpecialistComponent.SpFire,
                StatisticType.WATER => character.SpecialistComponent.WaterResistance + specialistInstance.GameItem.WaterResistance + character.SpecialistComponent.SpWater,
                StatisticType.LIGHT => character.SpecialistComponent.LightResistance + specialistInstance.GameItem.LightResistance + character.SpecialistComponent.SpLight,
                StatisticType.DARK => character.SpecialistComponent.DarkResistance + specialistInstance.GameItem.DarkResistance + character.SpecialistComponent.SpDark
            };
        }

        resistance += character.FindMoreStats(type);

        return resistance;
    }

    private static int FindMoreStats(this IPlayerEntity character, StatisticType type)
    {
        int stats = 0;

        IReadOnlyDictionary<PassiveType, int> passives = character.StatisticsComponent.Passives;

        stats += type switch
        {
            StatisticType.ATTACK_MELEE => passives.GetValueOrDefault(PassiveType.MELEE_ATTACK),
            StatisticType.ATTACK_RANGED => passives.GetValueOrDefault(PassiveType.RANGED_ATTACK),
            StatisticType.ATTACK_MAGIC => passives.GetValueOrDefault(PassiveType.MAGIC_ATTACK),
            StatisticType.HITRATE_MELEE => passives.GetValueOrDefault(PassiveType.MELEE_HIRATE),
            StatisticType.HITRATE_RANGED => passives.GetValueOrDefault(PassiveType.RANGED_HIRATE),
            StatisticType.DEFENSE_MELEE => passives.GetValueOrDefault(PassiveType.MELEE_DEFENCE),
            StatisticType.DEFENSE_RANGED => passives.GetValueOrDefault(PassiveType.RANGED_DEFENCE),
            StatisticType.DEFENSE_MAGIC => passives.GetValueOrDefault(PassiveType.MAGIC_DEFENCE),
            StatisticType.DODGE_MELEE => passives.GetValueOrDefault(PassiveType.MELEE_DODGE),
            StatisticType.DODGE_RANGED => passives.GetValueOrDefault(PassiveType.RANGED_DODGE),
            _ => 0
        };

        foreach (InventoryItem inventoryItem in character.EquippedItems)
        {
            GameItemInstance item = inventoryItem?.ItemInstance;

            if (item == null)
            {
                continue;
            }

            if (EquipmentTypes.Contains(item.GameItem.EquipmentSlot) && !StatsTypes.Contains(type))
            {
                continue;
            }

            if (item.Type == ItemInstanceType.SpecialistInstance)
            {
                continue;
            }

            stats += type switch
            {
                StatisticType.HITRATE_MELEE => item.HitRate + item.GameItem.HitRate,
                StatisticType.HITRATE_RANGED => item.HitRate + item.GameItem.HitRate,
                StatisticType.DEFENSE_MELEE => item.CloseDefence + item.GameItem.CloseDefence,
                StatisticType.DEFENSE_RANGED => item.DistanceDefence + item.GameItem.DistanceDefence,
                StatisticType.DEFENSE_MAGIC => item.MagicDefence + item.GameItem.MagicDefence,
                StatisticType.DODGE_MELEE => item.DefenceDodge + item.GameItem.DefenceDodge,
                StatisticType.DODGE_RANGED => item.DistanceDefenceDodge + item.GameItem.DistanceDefenceDodge,
                StatisticType.FIRE => item.FireResistance + item.GameItem.FireResistance,
                StatisticType.WATER => item.WaterResistance + item.GameItem.WaterResistance,
                StatisticType.LIGHT => item.LightResistance + item.GameItem.LightResistance,
                StatisticType.DARK => item.DarkResistance + item.GameItem.DarkResistance,
                _ => 0
            };
        }

        return stats;
    }

    public static int FindMoreStats(this IMateEntity mateEntity, StatisticType type)
    {
        int stats = 0;

        if (mateEntity.MateType == MateType.Pet)
        {
            return stats;
        }

        foreach (PartnerInventoryItem inventoryItem in mateEntity.Owner.PartnerGetEquippedItems(mateEntity.PetSlot))
        {
            GameItemInstance item = inventoryItem?.ItemInstance;

            if (item == null)
            {
                continue;
            }

            if (item.GameItem.EquipmentSlot == EquipmentType.Sp && !mateEntity.IsUsingSp)
            {
                continue;
            }

            stats += type switch
            {
                StatisticType.HITRATE_MELEE => item.HitRate + item.GameItem.HitRate,
                StatisticType.HITRATE_RANGED => item.HitRate + item.GameItem.HitRate,
                StatisticType.DEFENSE_MELEE => item.CloseDefence + item.GameItem.CloseDefence,
                StatisticType.DEFENSE_RANGED => item.DistanceDefence + item.GameItem.DistanceDefence,
                StatisticType.DEFENSE_MAGIC => item.MagicDefence + item.GameItem.MagicDefence,
                StatisticType.DODGE_MELEE => item.DefenceDodge + item.GameItem.DefenceDodge,
                StatisticType.DODGE_RANGED => item.DistanceDefenceDodge + item.GameItem.DistanceDefenceDodge,
                StatisticType.FIRE => item.FireResistance + item.GameItem.FireResistance,
                StatisticType.WATER => item.WaterResistance + item.GameItem.WaterResistance,
                StatisticType.LIGHT => item.LightResistance + item.GameItem.LightResistance,
                StatisticType.DARK => item.DarkResistance + item.GameItem.DarkResistance,
                _ => 0
            };
        }

        return stats;
    }

    public static int GetHitRate(this IPlayerEntity character, int basic, bool isMainWeapon, StatisticType type)
    {
        int hitRate = basic;
        GameItemInstance mainWeapon = character.MainWeapon;
        GameItemInstance secondaryWeapon = character.SecondaryWeapon;

        if (character.UseSp && type != StatisticType.HITRATE_MAGIC)
        {
            hitRate += character.SpecialistComponent.HitRate;
        }

        hitRate += character.FindMoreStats(type);

        if (isMainWeapon)
        {
            if (mainWeapon == null)
            {
                return hitRate;
            }

            return hitRate + mainWeapon.HitRate + mainWeapon.GameItem.HitRate;
        }

        if (secondaryWeapon == null)
        {
            return hitRate;
        }

        return hitRate + secondaryWeapon.HitRate + secondaryWeapon.GameItem.HitRate;
    }

    public static int GetDodge(this IPlayerEntity character, int basic, StatisticType type)
    {
        int dodge = basic;
        GameItemInstance armorInstance = character.Armor;

        if (character.UseSp)
        {
            dodge += type switch
            {
                StatisticType.DODGE_MELEE => character.SpecialistComponent.DefenceDodge,
                StatisticType.DODGE_RANGED => character.SpecialistComponent.DistanceDefenceDodge
            };
        }

        dodge += character.FindMoreStats(type);

        if (armorInstance == null)
        {
            return dodge;
        }

        dodge += type switch
        {
            StatisticType.DODGE_MELEE => armorInstance.DefenceDodge + armorInstance.GameItem.DefenceDodge,
            StatisticType.DODGE_RANGED => armorInstance.DistanceDefenceDodge + armorInstance.GameItem.DistanceDefenceDodge
        };

        return dodge;
    }

    public static int GetCriticalChance(this IPlayerEntity character, int basic, bool isMainWeapon)
    {
        int criticalChance = basic;
        GameItemInstance mainWeapon = character.MainWeapon;
        GameItemInstance secondaryWeapon = character.SecondaryWeapon;

        if (character.UseSp)
        {
            criticalChance += character.SpecialistComponent.CriticalLuckRate;
        }

        if (isMainWeapon)
        {
            if (mainWeapon == null)
            {
                return criticalChance;
            }

            return criticalChance + mainWeapon.GameItem.CriticalLuckRate;
        }

        if (secondaryWeapon == null)
        {
            return criticalChance;
        }

        return criticalChance + secondaryWeapon.GameItem.CriticalLuckRate;
    }

    public static int GetCriticalDamage(this IPlayerEntity character, int basic, bool isMainWeapon)
    {
        int criticalDamage = basic;
        GameItemInstance mainWeapon = character.MainWeapon;
        GameItemInstance secondaryWeapon = character.SecondaryWeapon;

        if (character.UseSp)
        {
            criticalDamage += character.SpecialistComponent.CriticalRate;
        }

        if (isMainWeapon)
        {
            if (mainWeapon == null)
            {
                return criticalDamage;
            }

            return criticalDamage + mainWeapon.GameItem.CriticalRate;
        }

        if (secondaryWeapon == null)
        {
            return criticalDamage;
        }

        return criticalDamage + secondaryWeapon.GameItem.CriticalRate;
    }

    public static int GetElement(this IPlayerEntity character, bool isSpecialistRate)
    {
        int element = 0;
        GameItemInstance specialist = character.Specialist;
        if (!isSpecialistRate)
        {
            bool isBuffed = character.HasBuff(BuffVnums.FAIRY_BOOSTER);

            GameItemInstance fairy = character.Fairy;
            if (fairy == null)
            {
                return element;
            }

            element += character.BCardComponent.GetAllBCardsInformation(BCardType.IncreaseElementFairy, (byte)AdditionalTypes.IncreaseElementFairy.FairyElementIncrease, character.Level).firstData;
            element -= character.BCardComponent.GetAllBCardsInformation(BCardType.IncreaseElementFairy, (byte)AdditionalTypes.IncreaseElementFairy.FairyElementDecrease, character.Level).firstData;

            int finalElement = element + fairy.ElementRate + fairy.GameItem.ElementRate + (isBuffed ? 30 : 0);

            return finalElement < 0 ? 0 : finalElement;
        }

        if (!character.UseSp || specialist == null)
        {
            return element;
        }

        int point = character.SpecialistComponent.GetSlElement();
        int p;

        if (point <= 50)
        {
            p = point;
        }
        else
        {
            p = 50 + (point - 50) * 2;
        }

        return p + character.SpecialistComponent.SpElement;
    }

    public static int GetDefence(this IPlayerEntity character, int basic, StatisticType statisticType)
    {
        int armor = basic;
        GameItemInstance armorInstance = character.Armor;
        GameItemInstance specialist = character.Specialist;

        if (character.UseSp)
        {
            int point = character.SpecialistComponent.GetSlDefense();

            int p = point switch
            {
                <= 10 => point,
                <= 20 => 10 + (point - 10) * 2,
                <= 30 => 30 + (point - 20) * 3,
                <= 40 => 60 + (point - 30) * 4,
                <= 50 => 100 + (point - 40) * 5,
                <= 60 => 150 + (point - 50) * 6,
                <= 70 => 210 + (point - 60) * 7,
                <= 80 => 280 + (point - 70) * 8,
                <= 90 => 360 + (point - 80) * 9,
                <= 100 => 450 + (point - 90) * 10,
                _ => 0
            };

            armor += character.SpecialistComponent.SpDefence * 10;
            switch (statisticType)
            {
                case StatisticType.DEFENSE_MELEE:
                    armor += character.SpecialistComponent.CloseDefence;
                    break;
                case StatisticType.DEFENSE_RANGED:
                    armor += character.SpecialistComponent.DistanceDefence;
                    break;
                case StatisticType.DEFENSE_MAGIC:
                    armor += character.SpecialistComponent.MagicDefence;
                    break;
            }

            armor += p;
        }

        armor += character.FindMoreStats(statisticType);

        if (armorInstance == null)
        {
            return armor;
        }

        switch (statisticType)
        {
            case StatisticType.DEFENSE_MELEE:
                return armor + armorInstance.CloseDefence + armorInstance.GameItem.CloseDefence;
            case StatisticType.DEFENSE_RANGED:
                return armor + armorInstance.DistanceDefence + armorInstance.GameItem.DistanceDefence;
            case StatisticType.DEFENSE_MAGIC:
                return armor + armorInstance.MagicDefence + armorInstance.GameItem.MagicDefence;
        }

        return armor;
    }

    public static int GetDamage(this IPlayerEntity character, int basic, bool isMin, StatisticType type, bool isMainWeapon = true)
    {
        int damage = basic;
        GameItemInstance mainWeapon = character.MainWeapon;
        GameItemInstance secondaryWeapon = character.SecondaryWeapon;

        if (character.UseSp)
        {
            int point = character.SpecialistComponent.GetSlHit();

            int p = point switch
            {
                <= 10 => point * 5,
                <= 20 => 50 + (point - 10) * 6,
                <= 30 => 110 + (point - 20) * 7,
                <= 40 => 180 + (point - 30) * 8,
                <= 50 => 260 + (point - 40) * 9,
                <= 60 => 350 + (point - 50) * 10,
                <= 70 => 450 + (point - 60) * 11,
                <= 80 => 560 + (point - 70) * 13,
                <= 90 => 690 + (point - 80) * 14,
                <= 94 => 830 + (point - 90) * 15,
                <= 95 => 890 + 16,
                <= 97 => 906 + (point - 95) * 17,
                <= 100 => 940 + (point - 97) * 20,
                _ => 0
            };

            damage += p;
            damage += character.SpecialistComponent.SpDamage * 10;
            if (isMin)
            {
                damage += character.SpecialistComponent.DamageMinimum;
            }
            else
            {
                damage += character.SpecialistComponent.DamageMaximum;
            }
        }

        damage += character.FindMoreStats(type);

        if (isMainWeapon)
        {
            if (mainWeapon == null)
            {
                return damage;
            }

            if (isMin)
            {
                return damage + mainWeapon.DamageMinimum + mainWeapon.GameItem.DamageMinimum;
            }

            return damage + mainWeapon.DamageMaximum + mainWeapon.GameItem.DamageMaximum;
        }

        if (secondaryWeapon == null)
        {
            return damage;
        }

        if (isMin)
        {
            return damage + secondaryWeapon.DamageMinimum + secondaryWeapon.GameItem.DamageMinimum;
        }

        return damage + secondaryWeapon.DamageMaximum + secondaryWeapon.GameItem.DamageMaximum;
    }

    public static int SlPoint(this GameItemInstance specialistInstance, short spPoint, SpecialistPointsType type)
    {
        try
        {
            int point = 0;
            switch (type)
            {
                case SpecialistPointsType.ATTACK:
                    point = spPoint switch
                    {
                        <= 10 => spPoint,
                        <= 28 => 10 + (spPoint - 10) / 2,
                        <= 88 => 19 + (spPoint - 28) / 3,
                        <= 168 => 39 + (spPoint - 88) / 4,
                        <= 268 => 59 + (spPoint - 168) / 5,
                        <= 334 => 79 + (spPoint - 268) / 6,
                        <= 383 => 90 + (spPoint - 334) / 7,
                        <= 391 => 97 + (spPoint - 383) / 8,
                        <= 400 => 98 + (spPoint - 391) / 9,
                        <= 410 => 99 + (spPoint - 400) / 10,
                        _ => point
                    };

                    break;

                case SpecialistPointsType.DEFENCE:
                    point = spPoint switch
                    {
                        <= 10 => spPoint,
                        <= 48 => 10 + (spPoint - 10) / 2,
                        <= 81 => 29 + (spPoint - 48) / 3,
                        <= 161 => 40 + (spPoint - 81) / 4,
                        <= 236 => 60 + (spPoint - 161) / 5,
                        <= 290 => 75 + (spPoint - 236) / 6,
                        <= 360 => 84 + (spPoint - 290) / 7,
                        <= 400 => 97 + (spPoint - 360) / 8,
                        <= 410 => 99 + (spPoint - 400) / 10,
                        _ => point
                    };

                    break;

                case SpecialistPointsType.ELEMENT:
                    point = spPoint switch
                    {
                        <= 20 => spPoint,
                        <= 40 => 20 + (spPoint - 20) / 2,
                        <= 70 => 30 + (spPoint - 40) / 3,
                        <= 110 => 40 + (spPoint - 70) / 4,
                        <= 210 => 50 + (spPoint - 110) / 5,
                        <= 270 => 70 + (spPoint - 210) / 6,
                        <= 410 => 80 + (spPoint - 270) / 7,
                        _ => point
                    };

                    break;

                case SpecialistPointsType.HPMP:
                    point = spPoint switch
                    {
                        <= 10 => spPoint,
                        <= 50 => 10 + (spPoint - 10) / 2,
                        <= 110 => 30 + (spPoint - 50) / 3,
                        <= 150 => 50 + (spPoint - 110) / 4,
                        <= 200 => 60 + (spPoint - 150) / 5,
                        <= 260 => 70 + (spPoint - 200) / 6,
                        <= 330 => 80 + (spPoint - 260) / 7,
                        <= 410 => 90 + (spPoint - 330) / 8,
                        _ => point
                    };

                    break;
            }

            return point;
        }
        catch
        {
            return 0;
        }
    }

    public static int SpPointsBasic(this GameItemInstance specialistInstance)
    {
        short spLevel = specialistInstance.SpLevel;
        int point = (spLevel - 20) * 3;
        if (spLevel <= 20)
        {
            point = 0;
        }

        switch (specialistInstance.Upgrade)
        {
            case 1:
                point += 5;
                break;

            case 2:
                point += 10;
                break;

            case 3:
                point += 15;
                break;

            case 4:
                point += 20;
                break;

            case 5:
                point += 28;
                break;

            case 6:
                point += 36;
                break;

            case 7:
                point += 46;
                break;

            case 8:
                point += 56;
                break;

            case 9:
                point += 68;
                break;

            case 10:
                point += 80;
                break;

            case 11:
                point += 95;
                break;

            case 12:
                point += 110;
                break;

            case 13:
                point += 128;
                break;

            case 14:
                point += 148;
                break;

            case 15:
                point += 173;
                break;
        }

        return point;
    }
}