// WingsEmu
// 
// Developed by NosWings Team

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using PhoenixLib.Events;
using WingsAPI.Data.Character;
using WingsAPI.Data.Miniland;
using WingsEmu.Core.Generics;
using WingsEmu.DTOs.Account;
using WingsEmu.DTOs.Bonus;
using WingsEmu.DTOs.Buffs;
using WingsEmu.DTOs.Inventory;
using WingsEmu.DTOs.Mates;
using WingsEmu.DTOs.Quests;
using WingsEmu.DTOs.Quicklist;
using WingsEmu.DTOs.Skills;
using WingsEmu.DTOs.Titles;
using WingsEmu.Game._enum;
using WingsEmu.Game.Algorithm;
using WingsEmu.Game.Battle;
using WingsEmu.Game.Battle.Managers;
using WingsEmu.Game.Buffs;
using WingsEmu.Game.Cheats;
using WingsEmu.Game.Entities;
using WingsEmu.Game.EntityStatistics;
using WingsEmu.Game.Exchange;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Families;
using WingsEmu.Game.Groups;
using WingsEmu.Game.Helpers.Damages;
using WingsEmu.Game.Inventory;
using WingsEmu.Game.Mails;
using WingsEmu.Game.Managers;
using WingsEmu.Game.Managers.StaticData;
using WingsEmu.Game.Maps;
using WingsEmu.Game.Mates;
using WingsEmu.Game.Networking;
using WingsEmu.Game.Portals;
using WingsEmu.Game.Quests;
using WingsEmu.Game.Quicklist;
using WingsEmu.Game.Raids;
using WingsEmu.Game.RainbowBattle;
using WingsEmu.Game.Relations;
using WingsEmu.Game.RespawnReturn;
using WingsEmu.Game.Revival;
using WingsEmu.Game.Shops;
using WingsEmu.Game.Skills;
using WingsEmu.Game.SnackFood;
using WingsEmu.Game.Specialists;
using WingsEmu.Game.TimeSpaces;
using WingsEmu.Game.Triggers;
using WingsEmu.Packets.Enums;
using WingsEmu.Packets.Enums.Character;

namespace WingsEmu.Game.Characters;

public partial class PlayerEntity : IPlayerEntity
{
    private readonly IBattleEntityAlgorithmService _algorithm;
    private readonly ICharacterAlgorithm _characterAlgorithm;
    private readonly IAsyncEventPipeline _eventPipeline;
    private readonly IFamilyManager _familyManager;
    private readonly IFoodSnackComponentFactory _foodSnackComponentFactory;
    private readonly IMapManager _mapManager;
    private readonly IPortalFactory _portalFactory;
    private readonly IRandomGenerator _randomGenerator;

    public PlayerEntity(CharacterDTO characterDto, IFamilyManager familyManager, IRandomGenerator randomGenerator, IMapManager mapManager, ICharacterAlgorithm characterAlgorithm,
        IFoodSnackComponentFactory foodSnackComponentFactory, IAsyncEventPipeline eventPipeline, IBattleEntityAlgorithmService algorithm, IPortalFactory portalFactory, IItemsManager itemsManager)
    {
        Id = (int)characterDto.Id;
        Hp = characterDto.Hp;
        Mp = characterDto.Mp;
        Level = characterDto.Level;
        Faction = characterDto.Faction;
        AccountId = characterDto.AccountId;
        Act4Dead = characterDto.Act4Dead;
        Act4Kill = characterDto.Act4Kill;
        Act4Points = characterDto.Act4Points;
        ArenaWinner = characterDto.ArenaWinner;
        Biography = characterDto.Biography;
        BuffBlocked = characterDto.BuffBlocked;
        Class = characterDto.Class;
        Compliment = characterDto.Compliment;
        Dignity = characterDto.Dignity;
        EmoticonsBlocked = characterDto.EmoticonsBlocked;
        ExchangeBlocked = characterDto.ExchangeBlocked;
        FamilyRequestBlocked = characterDto.FamilyRequestBlocked;
        FriendRequestBlocked = characterDto.FriendRequestBlocked;
        Gender = characterDto.Gender;
        Gold = characterDto.Gold;
        GroupRequestBlocked = characterDto.GroupRequestBlocked;
        HairColor = characterDto.HairColor;
        HairStyle = characterDto.HairStyle;
        HeroChatBlocked = characterDto.HeroChatBlocked;
        HeroLevel = characterDto.HeroLevel;
        HeroXp = characterDto.HeroXp;
        HpBlocked = characterDto.HpBlocked;
        IsPetAutoRelive = characterDto.IsPetAutoRelive;
        IsPartnerAutoRelive = characterDto.IsPartnerAutoRelive;
        JobLevel = characterDto.JobLevel;
        JobLevelXp = characterDto.JobLevelXp;
        LevelXp = characterDto.LevelXp;
        MapId = characterDto.MapId;
        MapX = characterDto.MapX;
        MapY = characterDto.MapY;
        MasterPoints = characterDto.MasterPoints;
        MasterTicket = characterDto.MasterTicket;
        MaxPetCount = characterDto.MaxPetCount;
        MaxPartnerCount = characterDto.MaxPartnerCount;
        MinilandInviteBlocked = characterDto.MinilandInviteBlocked;
        MinilandMessage = characterDto.MinilandMessage;
        MinilandPoint = characterDto.MinilandPoint;
        MinilandState = characterDto.MinilandState;
        MouseAimLock = characterDto.MouseAimLock;
        Name = characterDto.Name;
        QuickGetUp = characterDto.QuickGetUp;
        HideHat = characterDto.HideHat;
        UiBlocked = characterDto.UiBlocked;
        Reput = characterDto.Reput;
        Slot = characterDto.Slot;
        SpPointsBonus = characterDto.SpPointsBonus;
        SpPointsBasic = characterDto.SpPointsBasic;
        TalentLose = characterDto.TalentLose;
        TalentSurrender = characterDto.TalentSurrender;
        TalentWin = characterDto.TalentWin;
        WhisperBlocked = characterDto.WhisperBlocked;
        PartnerInventory = characterDto.PartnerInventory;
        NosMates = characterDto.NosMates;
        PartnerWarehouse = characterDto.PartnerWarehouse;
        Bonus = characterDto.Bonus;
        StaticBuffs = characterDto.StaticBuffs;
        Quicklist = characterDto.Quicklist;
        LearnedSkills = characterDto.LearnedSkills;
        Titles = characterDto.Titles;
        CompletedScripts = characterDto.CompletedScripts;
        CompletedQuests = characterDto.CompletedQuests;
        CompletedPeriodicQuests = characterDto.CompletedPeriodicQuests;
        ActiveQuests = characterDto.ActiveQuests;
        CompletedTimeSpaces = characterDto.CompletedTimeSpaces;
        MinilandObjects = characterDto.MinilandObjects;
        Inventory = characterDto.Inventory;
        EquippedStuffs = characterDto.EquippedStuffs;
        LifetimeStats = characterDto.LifetimeStats;
        RaidRestrictionDto = characterDto.RaidRestrictionDto;
        RainbowBattleLeaverBusterDto = characterDto.RainbowBattleLeaverBusterDto;
        _familyManager = familyManager;
        _randomGenerator = randomGenerator;
        _mapManager = mapManager;
        _characterAlgorithm = characterAlgorithm;
        _foodSnackComponentFactory = foodSnackComponentFactory;
        _eventPipeline = eventPipeline;
        _algorithm = algorithm;
        _portalFactory = portalFactory;

        _raidComponent = new RaidComponent();
        TimeSpaceComponent = new TimeSpaceComponent();
        _familyComponent = new FamilyComponent(_familyManager);
        _inventory = new InventoryComponent(this, itemsManager);
        _exchange = new ExchangeComponent();
        _characterRevivalComponent = new CharacterRevivalComponent();
        _groupComponent = new GroupComponent();
        _relationComponent = new RelationComponent();
        _eventTriggerContainer = new EventTriggerContainer(_eventPipeline);
        BCardComponent = new BCardComponent(_randomGenerator);
        _questContainer = new BasicQuestContainer();
        BuffComponent = new BuffComponent();
        _eqOptions = new EquipmentOptionContainer();
        _comboSkillComponent = new ComboSkillComponent();
        _bubbleComponent = new BubbleComponent();
        _partnerInventory = new PartnerInventoryComponent(this);
        _castingComponent = new CastingComponent();
        _skillCooldownComponent = new SkillCooldownComponent();
        _chargeComponent = new ChargeComponent();
        _angelElementBuffComponent = new AngelElementBuffComponent();
        _endBuffDamageComponent = new EndBuffDamageComponent();
        _scoutComponent = new ScoutComponent();
        QuicklistComponent = new QuicklistComponent();
        _foodSnackComponent = _foodSnackComponentFactory.CreateFoodSnackComponent();
        HomeComponent = new HomeComponent(characterDto.ReturnPoint);
        MateComponent = new MateComponent();
        WildKeeperComponent = new WildKeeperComponent();
        SkillComponent = new SkillComponent();
        CheatComponent = new CheatComponent();
        SpecialistComponent = new SpecialistStatsComponent(this);
        StatisticsComponent = new PlayerStatisticsComponent(this);
        ChargeComponent = new ChargeComponent();
        ShopComponent = new ShopComponent();
        MailNoteComponent = new MailNoteComponent();
        RainbowBattleComponent = new RainbowBattleComponent();

        RefreshCharacterStats();

        Killer = null;
        SpCooldownEnd = null;
        LastWalk = new LastWalk
        {
            MapId = null
        };

        LastDefence = DateTime.MinValue;
        LastItemUpgrade = DateTime.MinValue;
        LastHealth = DateTime.MinValue;
        LastSkillUse = DateTime.MinValue;
        LastEffect = DateTime.MinValue;
        LastDayNight = DateTime.MinValue;
        Bubble = DateTime.MinValue;
        ItemsToRemove = DateTime.UtcNow;
        LastPulseTick = DateTime.UtcNow;
        LastAdministrationBazaarRefresh = DateTime.UtcNow;
        LastBuySearchBazaarRefresh = DateTime.UtcNow;
        LastListItemBazaar = DateTime.UtcNow;
        Session = null;

        Skills = new List<IBattleEntitySkill>();
    }

    public DateTime LastDivinePotion { get; set; }

    public DateTime? GrowthShield { get; set; }

    public int AdditionalHp { get; set; }
    public int AdditionalMp { get; set; }

    public int Hp { get; set; }
    public int MaxHp { get; set; }

    public int Mp { get; set; }
    public int MaxMp { get; set; }

    public bool TriggerAmbush { get; set; }

    public DateTime LastMapChange { get; set; }

    public DateTime? LastSkillCombo { get; set; }

    public AuthorityType Authority { get; set; }

    public int CurrentMinigame { get; set; }

    public byte Level { get; set; }
    public byte Direction { get; set; } = 2;

    public int DamagesMinimum
    {
        get => GetDamagesMinimum(true);
        set { }
    }

    public int DamagesMaximum
    {
        get => GetDamagesMaximum(true);
        set { }
    }

    public int SecondDamageMinimum => GetDamagesMinimum(false);

    public int SecondDamageMaximum => GetDamagesMaximum(false);

    public int HitRate => GetHitRate(true);

    public int HitCriticalChance => GetCriticalChance(true);

    public int HitCriticalDamage => GetCriticalDamage(true);

    public int SecondHitRate => GetHitRate(false);

    public int SecondHitCriticalChance => GetCriticalChance(false);

    public int SecondHitCriticalDamage => GetCriticalDamage(false);

    public int MeleeDefence => this.GetDefence(_meleeDefense, StatisticType.DEFENSE_MELEE);

    public int RangedDefence => this.GetDefence(_rangedDefense, StatisticType.DEFENSE_RANGED);

    public int MagicDefence => this.GetDefence(_magicDefense, StatisticType.DEFENSE_MAGIC);

    public int MeleeDodge => this.GetDodge(_meleeDodge, StatisticType.DODGE_MELEE);

    public int RangedDodge => this.GetDodge(_rangedDodge, StatisticType.DODGE_RANGED);

    public byte Element
    {
        get
        {
            if (Fairy == null || Fairy.GameItem.Element == (byte)ElementType.All)
            {
                return 0;
            }

            return (byte)Fairy?.GameItem.Element;
        }
        set { }
    }

    public int ElementRate
    {
        get => this.GetElement(false);
        set { }
    }

    public int SpecialistElementRate => this.GetElement(true);

    public int FireResistance
    {
        get => this.GetResistance(StatisticType.FIRE);
        set { }
    }

    public int WaterResistance
    {
        get => this.GetResistance(StatisticType.WATER);
        set { }
    }

    public int LightResistance
    {
        get => this.GetResistance(StatisticType.LIGHT);
        set { }
    }

    public int DarkResistance
    {
        get => this.GetResistance(StatisticType.DARK);
        set { }
    }

    public DateTime GameStartDate { get; set; }

    public bool HasShopOpened { get; set; }

    public bool Invisible => this.IsInvisible();

    public bool IsCustomSpeed { get; set; }

    public bool IsShopping { get; set; }

    public bool IsSitting { get; set; }

    public bool IsOnVehicle { get; set; }

    public bool IsMorphed { get; set; }

    public (VisualType, long) LastEntity { get; set; }

    public DateTime? RandomMapTeleport { get; set; }

    public DateTime LastMove { get; set; }

    public DateTime LastPutItem { get; set; }

    public DateTime LastSentNote { get; set; }

    public DateTime? CheckWeedingBuff { get; set; }
    public DateTime LastPvPAttack { get; set; }

    public DateTime LastRainbowArrowEffect { get; set; }
    public DateTime LastRainbowEffects { get; set; }

    public LastWalk LastWalk { get; set; }

    public int LastNRunId { get; set; }

    public int LastPulse { get; set; }

    public DateTime LastPulseTick { get; set; }

    public DateTime LastDefence { get; set; }

    public DateTime LastItemUpgrade { get; set; }

    public DateTime LastDeath { get; set; }

    public DateTime LastEffect { get; set; }

    public DateTime LastEffectMinigame { get; set; }

    public DateTime LastHealth { get; set; }

    public DateTime LastPortal { get; set; }

    public DateTime LastPotion { get; set; }

    public DateTime LastSnack { get; set; }

    public DateTime LastFood { get; set; }

    public DateTime LastSkillUse { get; set; }

    public DateTime LastSpeedChange { get; set; }

    public DateTime LastTransform { get; set; }

    public DateTime LastDayNight { get; set; }

    public DateTime? SpCooldownEnd { get; set; }

    public DateTime Bubble { get; set; }

    public DateTime SpyOutStart { get; set; }

    public DateTime ItemsToRemove { get; set; }

    public DateTime BonusesToRemove { get; set; }

    public IMapInstance MapInstance => _mapManager.GetMapInstance(MapInstanceId);

    public Guid MapInstanceId { get; set; }

    public IMapInstance Miniland { get; set; }

    public int Morph { get; set; }

    public int MorphUpgrade { get; set; }

    public int MorphUpgrade2 { get; set; }

    public int? LastMinilandProducedItem { get; set; }

    public bool IsGettingLosingReputation { get; set; }

    public byte DeathsOnAct4 { get; set; }
    public long ArenaKills { get; set; }
    public long ArenaDeaths { get; set; }

    public TimeSpan? MuteRemainingTime { get; set; }

    public DateTime LastMuteTick { get; set; }
    public DateTime LastSitting { get; set; }

    public DateTime? LastChatMuteMessage { get; set; }
    public DateTime LastInventorySort { get; set; }

    public DateTime? ArenaImmunity { get; set; }

    public Position Position
    {
        get => new(PositionX, PositionY);
        set
        {
            Position pos = value;
            PositionX = pos.X;
            PositionY = pos.Y;
        }
    }

    public short PositionX { get; set; }

    public short PositionY { get; set; }

    public FactionType Faction { get; private set; }

    public IClientSession Session { get; private set; }

    public byte Size { get; set; } = 10;

    public List<IBattleEntitySkill> Skills { get; set; }
    public IChargeComponent ChargeComponent { get; }
    public ThreadSafeHashSet<Guid> AggroedEntities { get; } = new();

    public ConcurrentDictionary<int, CharacterSkill> CharacterSkills { get; } = new();

    public ConcurrentDictionary<int, CharacterSkill> SkillsSp { get; set; }

    public byte Speed
    {
        get => _speed;

        set
        {
            LastSpeedChange = DateTime.UtcNow;
            _speed = value > 59 ? (byte)59 : value;
        }
    }

    public IBattleEntity Killer { get; set; }

    public void AddStaticBonus(CharacterStaticBonusDto bonus) => Bonus.Add(bonus);

    public void AddStaticBonuses(IEnumerable<CharacterStaticBonusDto> bonuses) => Bonus.AddRange(bonuses);

    public IReadOnlyList<CharacterStaticBonusDto> GetStaticBonuses() => Bonus;

    public CharacterStaticBonusDto GetStaticBonus(Predicate<CharacterStaticBonusDto> predicate)
    {
        return Bonus.FirstOrDefault(x => predicate(x));
    }

    public ConcurrentDictionary<long, int> HitsByMonsters { get; } = new();

    public bool UseSp { get; set; }

    public byte VehicleSpeed { get; set; }

    public byte VehicleMapSpeed { get; set; }

    public int WareHouseSize { get; set; }

    public DateTime LastBuySearchBazaarRefresh { get; set; }

    public DateTime LastBuyBazaarRefresh { get; set; }

    public DateTime LastListItemBazaar { get; set; }

    public DateTime LastAdministrationBazaarRefresh { get; set; }

    public DateTime LastMonsterCaught { get; set; }

    public bool IsSeal { get; set; }

    public bool IsRemovingSpecialistPoints { get; set; }

    public VisualType Type => VisualType.Player;
    public int Id { get; }
    public long AccountId { get; set; }
    public int Act4Dead { get; set; }
    public int Act4Kill { get; set; }
    public int Act4Points { get; set; }
    public int ArenaWinner { get; set; }
    public string Biography { get; set; }
    public bool BuffBlocked { get; set; }
    public bool ShowRaidDeathInfo { get; set; }
    public ClassType Class { get; set; }
    public short Compliment { get; set; }
    public float Dignity { get; set; }
    public bool EmoticonsBlocked { get; set; }
    public bool ExchangeBlocked { get; set; }
    public bool FamilyRequestBlocked { get; set; }
    public bool FriendRequestBlocked { get; set; }
    public GenderType Gender { get; set; }
    public long Gold { get; set; }
    public bool GroupRequestBlocked { get; set; }
    public HairColorType HairColor { get; set; }
    public HairStyleType HairStyle { get; set; }
    public bool HeroChatBlocked { get; set; }
    public byte HeroLevel { get; set; }
    public long HeroXp { get; set; }
    public bool HpBlocked { get; set; }
    public bool IsPetAutoRelive { get; set; }
    public bool IsPartnerAutoRelive { get; set; }
    public byte JobLevel { get; set; }
    public long JobLevelXp { get; set; }
    public long LevelXp { get; set; }
    public int MapId { get; set; }
    public short MapX { get; set; }
    public short MapY { get; set; }
    public int MasterPoints { get; set; }
    public int MasterTicket { get; set; }
    public byte MaxPetCount { get; set; }
    public byte MaxPartnerCount { get; set; }
    public bool MinilandInviteBlocked { get; set; }
    public string MinilandMessage { get; set; }
    public short MinilandPoint { get; set; }
    public MinilandState MinilandState { get; set; }
    public bool MouseAimLock { get; set; }
    public string Name { get; set; }
    public bool QuickGetUp { get; set; }
    public bool HideHat { get; set; }
    public bool UiBlocked { get; set; }
    public long Reput { get; set; }
    public byte Slot { get; set; }
    public int SpPointsBonus { get; set; }
    public int SpPointsBasic { get; set; }
    public int TalentLose { get; set; }
    public int TalentSurrender { get; set; }
    public int TalentWin { get; set; }
    public bool WhisperBlocked { get; set; }
    public List<CharacterPartnerInventoryItemDto> PartnerInventory { get; set; }
    public List<MateDTO> NosMates { get; set; }
    public HashSet<long> CompletedTimeSpaces { get; set; }
    public List<PartnerWarehouseItemDto> PartnerWarehouse { get; set; }
    public List<CharacterStaticBonusDto> Bonus { get; set; }
    public List<CharacterStaticBuffDto> StaticBuffs { get; set; }
    public List<CharacterQuicklistEntryDto> Quicklist { get; set; }
    public List<CharacterSkillDTO> LearnedSkills { get; set; }
    public List<CharacterTitleDto> Titles { get; set; }
    public List<CompletedScriptsDto> CompletedScripts { get; set; }
    public List<CharacterQuestDto> CompletedQuests { get; set; }
    public List<CharacterQuestDto> CompletedPeriodicQuests { get; set; }
    public List<CharacterQuestDto> ActiveQuests { get; set; }
    public List<CharacterMinilandObjectDto> MinilandObjects { get; set; }
    public List<CharacterInventoryItemDto> Inventory { get; set; }
    public List<CharacterInventoryItemDto> EquippedStuffs { get; set; }
    public CharacterLifetimeStatsDto LifetimeStats { get; set; }
    public CharacterRaidRestrictionDto RaidRestrictionDto { get; set; }
    public RainbowBattleLeaverBusterDto RainbowBattleLeaverBusterDto { get; set; }

    public bool HasBuff(BuffVnums buffVnum) => BuffComponent.HasBuff((short)buffVnum);

    public void SetFaction(FactionType faction)
    {
        Faction = faction;
    }

    public void AddEvent(string trigger, IAsyncEvent notification, bool removeOnTrigger = false)
    {
        _eventTriggerContainer.AddEvent(trigger, notification, removeOnTrigger);
    }

    public async Task TriggerEvents(string key) => await _eventTriggerContainer.TriggerEvents(key);

    public async Task EmitEventAsync<T>(T eventArgs) where T : IBattleEntityEvent
    {
        if (eventArgs.Entity != this)
        {
            throw new ArgumentException("An event should be emitted only from the event sender");
        }

        await _eventPipeline.ProcessEventAsync(eventArgs);
    }

    public void EmitEvent<T>(T eventArgs) where T : IBattleEntityEvent
    {
        EmitEventAsync(eventArgs).ConfigureAwait(false).GetAwaiter().GetResult();
    }

    public bool HasCompletedQuest(int questId) => _questContainer.HasCompletedQuest(questId);
    public void RemoveCompletedQuest(int questId) => _questContainer.RemoveCompletedQuest(questId);

    public void RemoveCompletedScript(int scriptId, int scriptIndex) => _questContainer.RemoveCompletedScript(scriptId, scriptIndex);

    public void RemoveAllCompletedScripts() => _questContainer.RemoveAllCompletedScripts();
    public void ClearCompletedPeriodicQuests() => _questContainer.ClearCompletedPeriodicQuests();

    public IHomeComponent HomeComponent { get; }
    public ICheatComponent CheatComponent { get; }

    protected bool Equals(IPlayerEntity other) => Id == other.Id && Type == other.Type;

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }

        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        return obj.GetType() == GetType() && Equals((IPlayerEntity)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return Id.GetHashCode() * 397 ^ Type.GetHashCode();
        }
    }

    #region Methods

    private int GetDamagesMinimum(bool isMainWeapon)
    {
        return Class switch
        {
            ClassType.Swordman => this.GetDamage(isMainWeapon ? _meleeDamageMin : _rangedDamageMin, true, isMainWeapon ? StatisticType.ATTACK_MELEE : StatisticType.ATTACK_RANGED, isMainWeapon),
            ClassType.Wrestler => this.GetDamage(isMainWeapon ? _meleeDamageMax : _rangedDamageMax, true, isMainWeapon ? StatisticType.ATTACK_MELEE : StatisticType.ATTACK_RANGED, isMainWeapon),
            ClassType.Adventurer => this.GetDamage(isMainWeapon ? _meleeDamageMin : _rangedDamageMin, true, isMainWeapon ? StatisticType.ATTACK_MELEE : StatisticType.ATTACK_RANGED, isMainWeapon),
            ClassType.Archer => this.GetDamage(isMainWeapon ? _rangedDamageMin : _meleeDamageMin, true, isMainWeapon ? StatisticType.ATTACK_RANGED : StatisticType.ATTACK_MELEE, isMainWeapon),
            ClassType.Magician => this.GetDamage(isMainWeapon ? _magicDamageMin : _rangedDamageMin, true, isMainWeapon ? StatisticType.ATTACK_MAGIC : StatisticType.ATTACK_RANGED, isMainWeapon)
        };
    }

    private int GetHitRate(bool isMainWeapon)
    {
        int hitRate = 0;
        if (Class == ClassType.Magician && isMainWeapon)
        {
            return this.GetHitRate(0, true, StatisticType.HITRATE_MAGIC);
        }

        if (isMainWeapon)
        {
            switch (Class)
            {
                case ClassType.Swordman:
                case ClassType.Wrestler:
                case ClassType.Adventurer:
                    hitRate = this.GetHitRate(_meleeHitRate, true, StatisticType.HITRATE_MELEE);
                    break;
                case ClassType.Archer:
                    hitRate = this.GetHitRate(_rangedHitRate, true, StatisticType.HITRATE_RANGED);
                    break;
            }
        }
        else
        {
            switch (Class)
            {
                case ClassType.Wrestler:
                case ClassType.Archer:
                case ClassType.Adventurer:
                    hitRate = this.GetHitRate(_meleeHitRate, false, StatisticType.HITRATE_MELEE);
                    break;
                case ClassType.Magician:
                case ClassType.Swordman:
                    hitRate = this.GetHitRate(_rangedHitRate, false, StatisticType.HITRATE_RANGED);
                    break;
            }
        }

        return hitRate;
    }

    private int GetCriticalChance(bool isMainWeapon)
    {
        if (Class == ClassType.Magician && isMainWeapon)
        {
            return 0;
        }

        return this.GetCriticalChance(_criticalChance, isMainWeapon);
    }

    private int GetCriticalDamage(bool isMainWeapon)
    {
        if (Class == ClassType.Magician && isMainWeapon)
        {
            return 0;
        }

        return this.GetCriticalDamage(_criticalDamage, isMainWeapon);
    }

    private int GetDamagesMaximum(bool isMainWeapon)
    {
        return Class switch
        {
            ClassType.Swordman =>
                this.GetDamage(isMainWeapon ? _meleeDamageMax : _rangedDamageMax, false, isMainWeapon ? StatisticType.ATTACK_MELEE : StatisticType.ATTACK_RANGED, isMainWeapon),
            ClassType.Wrestler =>
                this.GetDamage(isMainWeapon ? _meleeDamageMax : _rangedDamageMax, false, isMainWeapon ? StatisticType.ATTACK_MELEE : StatisticType.ATTACK_RANGED, isMainWeapon),
            ClassType.Adventurer =>
                this.GetDamage(isMainWeapon ? _meleeDamageMax : _rangedDamageMax, false, isMainWeapon ? StatisticType.ATTACK_MELEE : StatisticType.ATTACK_RANGED, isMainWeapon),
            ClassType.Archer =>
                this.GetDamage(isMainWeapon ? _rangedDamageMax : _meleeDamageMax, false, isMainWeapon ? StatisticType.ATTACK_RANGED : StatisticType.ATTACK_MELEE, isMainWeapon),
            ClassType.Magician =>
                this.GetDamage(isMainWeapon ? _magicDamageMax : _rangedDamageMax, false, isMainWeapon ? StatisticType.ATTACK_MAGIC : StatisticType.ATTACK_RANGED, isMainWeapon)
        };
    }

    public bool IsCraftingItem { get; set; }
    public bool IsBankOpen { get; set; }
    public DateTime LastUnfreezedPlayer { get; set; }
    public DateTime LastSpPacketSent { get; set; }
    public DateTime LastSpRemovingProcess { get; set; }
    public DateTime LastSpPointProcess { get; set; }
    public DateTime LastAttack { get; set; }
    public bool InitialScpPacketSent { get; set; }

    public IQuicklistComponent QuicklistComponent { get; }
    public ISpecialistStatsComponent SpecialistComponent { get; }
    public IPlayerStatisticsComponent StatisticsComponent { get; }
    public IRainbowBattleComponent RainbowBattleComponent { get; }

    public int GetCp()
    {
        int cpMax = (Class > 0 ? 40 : 0) + JobLevel * 2;
        int cpUsed = 0;

        foreach (CharacterSkill skill in CharacterSkills.Values)
        {
            if (skill == null)
            {
                continue;
            }

            if (skill.Skill.IsPassiveSkill())
            {
                continue;
            }

            cpUsed += skill.Skill.CPCost;
        }

        return cpMax - cpUsed;
    }

    public int GetDignityIco()
    {
        int icoDignity = 1;

        if (Dignity <= -100)
        {
            icoDignity = 2;
        }

        if (Dignity <= -200)
        {
            icoDignity = 3;
        }

        if (Dignity <= -400)
        {
            icoDignity = 4;
        }

        if (Dignity <= -600)
        {
            icoDignity = 5;
        }

        if (Dignity <= -800)
        {
            icoDignity = 6;
        }

        return icoDignity;
    }

    public List<IPortalEntity> GetExtraPortal()
    {
        if (MapInstance == null || Miniland == null)
        {
            return new List<IPortalEntity>();
        }

        return MapInstance.GenerateMinilandEntryPortals(Miniland, _portalFactory);
    }

    [Obsolete("Move to constructor")]
    public void SetSession(IClientSession clientSession)
    {
        Session = clientSession;
    }

    public int HealthHpLoad()
    {
        if (IsSitting)
        {
            return _characterAlgorithm.GetRegenHp(this, Class, true);
        }

        return LastDefence.AddSeconds(4) <= DateTime.UtcNow ? _characterAlgorithm.GetRegenHp(this, Class, false) : 0;
    }

    public int HealthMpLoad()
    {
        if (IsSitting)
        {
            return _characterAlgorithm.GetRegenMp(this, Class, true);
        }

        return LastDefence.AddSeconds(4) <= DateTime.UtcNow ? _characterAlgorithm.GetRegenMp(this, Class, false) : 0;
    }

    #endregion
}